ProjectAres/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/whisper/WhisperDispatcher.java

199 lines
6.6 KiB
Java

package tc.oc.commons.bukkit.whisper;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import javax.inject.Inject;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import tc.oc.api.bukkit.users.OnlinePlayers;
import tc.oc.api.docs.BasicModel;
import tc.oc.api.docs.PlayerId;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.Whisper;
import tc.oc.api.docs.virtual.WhisperDoc;
import tc.oc.api.message.MessageListener;
import tc.oc.api.message.MessageService;
import tc.oc.api.message.types.ModelUpdate;
import tc.oc.api.model.IdFactory;
import tc.oc.api.users.UserService;
import tc.oc.api.whispers.WhisperService;
import tc.oc.commons.bukkit.event.UserLoginEvent;
import tc.oc.commons.bukkit.nick.Identity;
import tc.oc.commons.bukkit.settings.SettingManagerProvider;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.commons.core.scheduler.Scheduler;
import tc.oc.minecraft.scheduler.MainThreadExecutor;
public class WhisperDispatcher implements WhisperSender, Listener, MessageListener, PluginFacet {
// Mark a whisper as read if the player is still online
// this long after first seeing it. This prevents a player
// from missing a message because they disconnected right
// at the same time they received it.
private static final Duration MARK_READ_DELAY = Duration.ofSeconds(3);
private final MessageService queue;
private final OnlinePlayers onlinePlayers;
private final MainThreadExecutor executor;
private final Scheduler scheduler;
private final WhisperService whisperService;
private final WhisperFormatter formatter;
private final Server localServer;
private final IdFactory idFactory;
private final SettingManagerProvider playerSettings;
private final UserService userService;
@Inject WhisperDispatcher(MessageService queue,
OnlinePlayers onlinePlayers,
MainThreadExecutor executor,
Scheduler scheduler,
WhisperService whisperService,
WhisperFormatter formatter,
Server localServer,
IdFactory idFactory,
SettingManagerProvider playerSettings,
UserService userService) {
this.queue = queue;
this.onlinePlayers = onlinePlayers;
this.executor = executor;
this.scheduler = scheduler;
this.whisperService = whisperService;
this.formatter = formatter;
this.localServer = localServer;
this.idFactory = idFactory;
this.playerSettings = playerSettings;
this.userService = userService;
}
@Override
public void enable() {
queue.bind(ModelUpdate.class);
queue.subscribe(this, executor);
}
@Override
public void disable() {
queue.unsubscribe(this);
}
@Override
public void send(CommandSender sender, Identity from, Identity to, String content) {
executor.callback(userService.find(to.getPlayerId()), user -> {
final WhisperSettings.Options setting = (WhisperSettings.Options) playerSettings.getManager(user).getValue(WhisperSettings.receive());
if(setting.canSend(sender, to)) {
final ListenableFuture<Whisper> future = whisperService.update(new Out(from, to, content));
executor.callback(future, whisper -> formatter.send(sender, whisper));
} else {
formatter.blocked(sender, to);
}
});
}
private void markRead(Player player, Runnable block) {
scheduler.createDelayedTask(MARK_READ_DELAY, () -> {
// Once willBeOnline goes false, it stays false forever,
// whereas isOnline can become true again if the player
// reconnects.
if(player.willBeOnline()) {
block.run();
}
});
}
@HandleMessage
private void receive(ModelUpdate<Whisper> update) {
final Whisper whisper = update.document();
if(whisper.delivered()) return;
onlinePlayers.byUserId(whisper.recipient_uid()).ifPresent(player -> {
formatter.receive(player, whisper);
markRead(player, () ->
whisperService.update(new Ack(whisper._id()))
);
});
}
/**
* Make sure this runs after the welcome message in the lobby
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void login(UserLoginEvent event) {
final List<Whisper> whispers = event.response().whispers();
if(!whispers.isEmpty()) {
final Player player = event.getPlayer();
whispers.forEach(whisper -> formatter.receive(player, whisper));
markRead(player, () ->
whisperService.updateMulti(Lists.transform(whispers, whisper -> new Ack(whisper._id())))
);
}
}
private class Out extends BasicModel implements Whisper {
final Identity from, to;
final String content;
private Out(Identity from, Identity to, String content) {
super(idFactory.newId());
this.from = from;
this.to = to;
this.content = content;
}
@Override public String family() {
return localServer.family();
}
@Override public String server_id() {
return localServer._id();
}
@Override public Instant sent() {
return Instant.now();
}
@Override public boolean delivered() {
return false;
}
@Override public PlayerId sender_uid() {
return from.getPlayerId();
}
@Override public String sender_nickname() {
return from.getNickname();
}
@Override public PlayerId recipient_uid() {
return to.getPlayerId();
}
@Override public String recipient_specified() {
return to.getNickname();
}
@Override public String content() {
return content;
}
}
private class Ack extends BasicModel implements WhisperDoc.Delivery {
public Ack(String _id) {
super(_id);
}
@Override
public boolean delivered() {
return true;
}
}
}