package games.strategy.engine.chat; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import games.strategy.engine.lobby.server.IModeratorController; import games.strategy.engine.lobby.server.ModeratorController; import games.strategy.engine.message.IChannelMessenger; import games.strategy.engine.message.IRemoteMessenger; import games.strategy.engine.message.MessageContext; import games.strategy.engine.message.RemoteName; import games.strategy.net.IConnectionChangeListener; import games.strategy.net.IMessenger; import games.strategy.net.INode; import games.strategy.net.IServerMessenger; import games.strategy.net.Messengers; import games.strategy.util.Tuple; public class ChatController implements IChatController { private static final Logger logger = Logger.getLogger(ChatController.class.getName()); private static final String CHAT_REMOTE = "_ChatRmt"; private static final String CHAT_CHANNEL = "_ChatCtrl"; private final IMessenger messenger; private final IRemoteMessenger remoteMessenger; private final IModeratorController moderatorController; private final IChannelMessenger channelMessenger; private final String chatName; private final Map<INode, Tag> chatters = new HashMap<>(); protected final Object mutex = new Object(); private final String chatChannel; private long version; private final ScheduledExecutorService pingThread = Executors.newScheduledThreadPool(1); private final IConnectionChangeListener connectionChangeListener = new IConnectionChangeListener() { @Override public void connectionAdded(final INode to) {} @Override public void connectionRemoved(final INode to) { synchronized (mutex) { if (chatters.keySet().contains(to)) { leaveChatInternal(to); } } } }; public static RemoteName getChatControlerRemoteName(final String chatName) { return new RemoteName(CHAT_REMOTE + chatName, IChatController.class); } public static String getChatChannelName(final String chatName) { return CHAT_CHANNEL + chatName; } public ChatController(final String name, final IMessenger messenger, final IRemoteMessenger remoteMessenger, final IChannelMessenger channelMessenger, final IModeratorController moderatorController) { chatName = name; this.messenger = messenger; this.remoteMessenger = remoteMessenger; this.moderatorController = moderatorController; this.channelMessenger = channelMessenger; chatChannel = getChatChannelName(name); this.remoteMessenger.registerRemote(this, getChatControlerRemoteName(name)); ((IServerMessenger) this.messenger).addConnectionChangeListener(connectionChangeListener); pingThread.scheduleAtFixedRate(() -> { try { // System.out.println("Pinging"); getChatBroadcaster().ping(); } catch (final Exception e) { logger.log(Level.SEVERE, "Error pinging", e); } }, 180, 60, TimeUnit.SECONDS); } public ChatController(final String name, final Messengers messenger, final ModeratorController moderatorController) { this(name, messenger.getMessenger(), messenger.getRemoteMessenger(), messenger.getChannelMessenger(), moderatorController); } // clean up public void deactivate() { pingThread.shutdown(); synchronized (mutex) { final IChatChannel chatter = getChatBroadcaster(); for (final INode node : chatters.keySet()) { version++; chatter.speakerRemoved(node, version); } remoteMessenger.unregisterRemote(getChatControlerRemoteName(chatName)); } ((IServerMessenger) messenger).removeConnectionChangeListener(connectionChangeListener); } private IChatChannel getChatBroadcaster() { final IChatChannel chatter = (IChatChannel) channelMessenger.getChannelBroadcastor(new RemoteName(chatChannel, IChatChannel.class)); return chatter; } // a player has joined @Override public Tuple<Map<INode, Tag>, Long> joinChat() { final INode node = MessageContext.getSender(); logger.info("Chatter:" + node + " is joining chat:" + chatName); final Tag tag; if (moderatorController.isPlayerAdmin(node)) { tag = Tag.MODERATOR; } else { tag = Tag.NONE; } synchronized (mutex) { chatters.put(node, tag); version++; getChatBroadcaster().speakerAdded(node, tag, version); final Map<INode, Tag> copy = new HashMap<>(chatters); return Tuple.of(copy, version); } } // a player has left @Override public void leaveChat() { leaveChatInternal(MessageContext.getSender()); } protected void leaveChatInternal(final INode node) { long version; synchronized (mutex) { chatters.remove(node); this.version++; version = this.version; } getChatBroadcaster().speakerRemoved(node, version); logger.info("Chatter:" + node + " has left chat:" + chatName); } }