diff --git a/game-app/domain-data/src/main/java/org/triplea/domain/data/ChatParticipant.java b/game-app/domain-data/src/main/java/org/triplea/domain/data/ChatParticipant.java index bb5371f1fe1..50252d6d969 100644 --- a/game-app/domain-data/src/main/java/org/triplea/domain/data/ChatParticipant.java +++ b/game-app/domain-data/src/main/java/org/triplea/domain/data/ChatParticipant.java @@ -26,7 +26,7 @@ public class ChatParticipant implements Serializable { @Nonnull private final String playerChatId; /** True if the player has moderator privileges. */ - private final boolean isModerator; + @Setter private boolean isModerator; /** Status is custom text set by players, eg: "AFK" or "Looking for a game". */ @Setter @Nullable private String status; diff --git a/game-app/game-core/src/main/java/games/strategy/engine/chat/Chat.java b/game-app/game-core/src/main/java/games/strategy/engine/chat/Chat.java index 3c983dac8ea..c79b8d1ab11 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/chat/Chat.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/chat/Chat.java @@ -2,6 +2,8 @@ import com.google.common.collect.EvictingQueue; import com.google.common.collect.Sets; +import games.strategy.net.IMessageListener; +import games.strategy.net.Messengers; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -56,6 +58,14 @@ public Chat(final ChatTransmitter chatTransmitter) { updateConnections(); } + public void addMessengersListener(IMessageListener messageListener) { + if (chatTransmitter instanceof MessengersChatTransmitter) { + ((MessengersChatTransmitter) chatTransmitter) + .getMessengers() + .addMessageListener(messageListener); + } + } + private void updateConnections() { final List playerNames = chatters.stream() @@ -178,4 +188,12 @@ boolean isIgnored(final UserName userName) { Collection getOnlinePlayers() { return chatters.stream().map(ChatParticipant::getUserName).collect(Collectors.toSet()); } + + public Messengers getMessengers() { + if (!(chatTransmitter instanceof MessengersChatTransmitter)) { + throw new UnsupportedOperationException( + "getMessengers is to support legacy 'messengers' communication only"); + } + return ((MessengersChatTransmitter) chatTransmitter).getMessengers(); + } } diff --git a/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatController.java b/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatController.java index 199b572f7ba..fd6f8d971c8 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatController.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatController.java @@ -1,19 +1,18 @@ package games.strategy.engine.chat; import com.google.common.base.Strings; -import games.strategy.engine.framework.GameRunner; import games.strategy.engine.message.MessageContext; import games.strategy.engine.message.RemoteName; import games.strategy.net.IConnectionChangeListener; import games.strategy.net.INode; import games.strategy.net.Messengers; +import games.strategy.net.ServerMessenger; import java.util.Collection; 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.function.Predicate; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.triplea.domain.data.ChatParticipant; @@ -26,21 +25,11 @@ public class ChatController implements IChatController { private static final String CHAT_REMOTE = "_ChatRemote_"; private static final String CHAT_CHANNEL = "_ChatControl_"; private final Messengers messengers; + private final ServerMessenger serverMessenger; private final String chatName; private final Map chatters = new HashMap<>(); - private final Predicate isModerator = - node -> { - if (chatters.isEmpty() && !GameRunner.headless()) { - return true; - } else if (GameRunner.headless() && chatters.size() == 1) { - return true; - } else { - return false; - } - }; - private final Map chatterIds = new HashMap<>(); private final Map chatterStatus = new HashMap<>(); @@ -62,9 +51,11 @@ public void connectionRemoved(final INode to) { } }; - public ChatController(final String name, final Messengers messengers) { + public ChatController( + final String name, final Messengers messengers, ServerMessenger serverMessenger) { chatName = name; this.messengers = messengers; + this.serverMessenger = serverMessenger; chatChannel = getChatChannelName(name); messengers.registerRemote(this, getChatControllerRemoteName(name)); messengers.addConnectionChangeListener(connectionChangeListener); @@ -116,7 +107,7 @@ private IChatChannel getChatBroadcaster() { public Collection joinChat() { final INode node = MessageContext.getSender(); log.info("Chatter:" + node + " is joining chat:" + chatName); - final Tag tag = isModerator.test(node) ? Tag.MODERATOR : Tag.NONE; + final Tag tag = Tag.NONE; synchronized (mutex) { final PlayerChatId id = PlayerChatId.newId(); chatterIds.put(node, id); @@ -126,14 +117,14 @@ public Collection joinChat() { ChatParticipant.builder() .userName(node.getPlayerName().getValue()) .playerChatId(id.getValue()) - .isModerator(tag == Tag.MODERATOR) + .isModerator(serverMessenger.isModerator(node)) .build()); return chatters.entrySet().stream() .map( entry -> ChatParticipant.builder() - .isModerator(entry.getValue() == Tag.MODERATOR) + .isModerator(serverMessenger.isModerator(entry.getKey())) .userName(entry.getKey().getPlayerName().getValue()) .playerChatId(chatterIds.get(entry.getKey()).getValue()) .status(chatterStatus.get(entry.getKey().getPlayerName())) diff --git a/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatMessagePanel.java b/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatMessagePanel.java index c433987907f..4e091337d81 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatMessagePanel.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatMessagePanel.java @@ -1,5 +1,8 @@ package games.strategy.engine.chat; +import games.strategy.engine.framework.startup.mc.messages.ModeratorPromoted; +import games.strategy.net.IMessageListener; +import games.strategy.net.INode; import games.strategy.triplea.settings.ClientSetting; import java.awt.BorderLayout; import java.awt.Container; @@ -7,6 +10,7 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; +import java.io.Serializable; import javax.swing.Action; import javax.swing.BoundedRangeModel; import javax.swing.InputMap; @@ -80,10 +84,21 @@ public enum ChatSoundProfile { public ChatMessagePanel( final Chat chat, final ChatSoundProfile chatSoundProfile, final ClipPlayer clipPlayer) { + this.chatSoundProfile = chatSoundProfile; this.clipPlayer = clipPlayer; init(); setChat(chat); + + chat.addMessengersListener( + new IMessageListener() { + @Override + public void messageReceived(Serializable msg, INode from) { + if (msg instanceof ModeratorPromoted) { + addGenericMessage("Moderator Promoted: " + ((ModeratorPromoted) msg).getPlayerName()); + } + } + }); } private void init() { diff --git a/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatPlayerPanel.java b/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatPlayerPanel.java index 214e4d13857..d6f0e2e184a 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatPlayerPanel.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/chat/ChatPlayerPanel.java @@ -1,11 +1,16 @@ package games.strategy.engine.chat; +import games.strategy.engine.framework.startup.mc.messages.ModeratorMessage; +import games.strategy.engine.framework.startup.mc.messages.ModeratorPromoted; +import games.strategy.net.IMessageListener; +import games.strategy.net.INode; import games.strategy.triplea.EngineImageLoader; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -26,6 +31,7 @@ import org.triplea.domain.data.ChatParticipant; import org.triplea.domain.data.UserName; import org.triplea.java.StringUtils; +import org.triplea.swing.EventThreadJOptionPane; import org.triplea.swing.SwingAction; /** A UI component that displays the players participating in a chat. */ @@ -53,6 +59,21 @@ public ChatPlayerPanel(final Chat chat) { layoutComponents(); setupListeners(); setChat(chat); + chat.addMessengersListener( + new IMessageListener() { + @Override + public void messageReceived(Serializable msg, INode from) { + if (msg instanceof ModeratorPromoted) { + String newModerator = ((ModeratorPromoted) msg).getPlayerName(); + for (int i = 0; i < listModel.getSize(); i++) { + if (listModel.get(i).getUserName().toString().equals(newModerator)) { + listModel.get(i).setModerator(true); + break; + } + } + } + } + }); } /** Sets the chat whose players will be displayed in this panel. */ @@ -138,6 +159,7 @@ public void mouseReleased(final MouseEvent e) { mouseOnPlayersList(e); } }); + final JPanel panelReference = this; actionFactories.add( clickedOn -> { // you can't slap or ignore yourself @@ -155,7 +177,54 @@ public void mouseReleased(final MouseEvent e) { final Action slap = SwingAction.of( "Slap " + clickedOn.getUserName(), e -> chat.sendSlap(clickedOn.getUserName())); - return List.of(slap, ignore); + + // TODO: add check for if we are moderator + final Action disconnect = + SwingAction.of( + "Disconnect " + clickedOn.getUserName(), + e -> { + if (EventThreadJOptionPane.showConfirmDialog( + panelReference, + "Disconnect " + clickedOn.getUserName() + "?", + "Confirm Disconnect", + EventThreadJOptionPane.ConfirmDialogType.YES_NO)) { + chat.getMessengers() + .sendToServer( + ModeratorMessage.newDisconnect(clickedOn.getUserName().getValue())); + } + }); + // TODO: add check for if we are moderator + + final Action ban = + SwingAction.of( + "Ban " + clickedOn.getUserName(), + e -> { + if (EventThreadJOptionPane.showConfirmDialog( + panelReference, + "Ban " + clickedOn.getUserName() + "?", + "Confirm Ban", + EventThreadJOptionPane.ConfirmDialogType.YES_NO)) { + chat.getMessengers() + .sendToServer( + ModeratorMessage.newBan(clickedOn.getUserName().getValue())); + } + }); + + List availableActions = new ArrayList<>(List.of(slap, ignore)); + + boolean currentPlayerIsModerator = false; + for (int i = 0, n = listModel.getSize(); i < n; i++) { + var participant = listModel.get(i); + if (chat.getLocalUserName().equals(participant.getUserName())) { + currentPlayerIsModerator = participant.isModerator(); + break; + } + } + + if (currentPlayerIsModerator && !clickedOn.isModerator()) { + availableActions.addAll(List.of(disconnect, ban)); + } + return availableActions; }); } diff --git a/game-app/game-core/src/main/java/games/strategy/engine/chat/MessengersChatTransmitter.java b/game-app/game-core/src/main/java/games/strategy/engine/chat/MessengersChatTransmitter.java index 8cf20ae3f02..891148b9f50 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/chat/MessengersChatTransmitter.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/chat/MessengersChatTransmitter.java @@ -6,6 +6,7 @@ import games.strategy.net.websocket.ClientNetworkBridge; import games.strategy.triplea.settings.ClientSetting; import java.util.Collection; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.triplea.domain.data.ChatParticipant; import org.triplea.domain.data.UserName; @@ -15,7 +16,7 @@ @Slf4j public class MessengersChatTransmitter implements ChatTransmitter { private final UserName userName; - private final Messengers messengers; + @Getter private final Messengers messengers; private IChatChannel chatChannelSubscriber; diff --git a/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/ServerModel.java b/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/ServerModel.java index eb797136921..f75bb7f67d0 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/ServerModel.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/ServerModel.java @@ -20,6 +20,7 @@ import games.strategy.engine.framework.startup.LobbyWatcherThread; import games.strategy.engine.framework.startup.launcher.LaunchAction; import games.strategy.engine.framework.startup.launcher.ServerLauncher; +import games.strategy.engine.framework.startup.mc.messages.ModeratorMessage; import games.strategy.engine.framework.startup.ui.InGameLobbyWatcherWrapper; import games.strategy.engine.framework.startup.ui.PlayerTypes; import games.strategy.engine.framework.startup.ui.panels.main.game.selector.GameSelectorModel; @@ -215,6 +216,24 @@ private GameHostingResponse createServerMessenger(final ServerConnectionProps pr new ServerMessenger(props.getName(), props.getPort(), objectStreamFactory); serverMessenger.addConnectionChangeListener(this); + // add moderator action handlers (eg: ban/disconnect) + serverMessenger.addMessageListener( + (msg, from) -> { + // check that message is from a moderator + if (msg instanceof ModeratorMessage) { + if (!serverMessenger.isModerator(from)) { + return; + } + + ModeratorMessage moderatorMessage = (ModeratorMessage) msg; + if (moderatorMessage.isBan()) { + serverMessenger.banPlayer(moderatorMessage.getPlayerName()); + } else if (moderatorMessage.isDisconnect()) { + serverMessenger.removeConnection(moderatorMessage.getPlayerName()); + } + } + }); + messengers = new Messengers(serverMessenger); messengers.registerRemote( launchAction.getStartupRemote(new DefaultServerModelView()), SERVER_REMOTE_NAME); @@ -257,7 +276,7 @@ private GameHostingResponse createServerMessenger(final ServerConnectionProps pr gameHostingResponse = null; } - chatController = new ChatController(CHAT_NAME, messengers); + chatController = new ChatController(CHAT_NAME, messengers, serverMessenger); // TODO: Project#4 Change no-op network sender to a real network bridge chatModel = diff --git a/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/messages/ModeratorMessage.java b/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/messages/ModeratorMessage.java new file mode 100644 index 00000000000..40effac6b84 --- /dev/null +++ b/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/messages/ModeratorMessage.java @@ -0,0 +1,30 @@ +package games.strategy.engine.framework.startup.mc.messages; + +import java.io.Serializable; +import javax.annotation.Nonnull; +import lombok.Value; + +/** Represents a message sent to the server, from a moderator */ +@Value +public class ModeratorMessage implements Serializable { + private static final long serialVersionUID = 1L; + + @Nonnull String action; + @Nonnull String playerName; + + public static ModeratorMessage newDisconnect(String playerName) { + return new ModeratorMessage("disconnect", playerName); + } + + public static ModeratorMessage newBan(String playerName) { + return new ModeratorMessage("ban", playerName); + } + + public boolean isBan() { + return "ban".equalsIgnoreCase(action); + } + + public boolean isDisconnect() { + return "disconnect".equalsIgnoreCase(action); + } +} diff --git a/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/messages/ModeratorPromoted.java b/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/messages/ModeratorPromoted.java new file mode 100644 index 00000000000..a48c31b32c0 --- /dev/null +++ b/game-app/game-core/src/main/java/games/strategy/engine/framework/startup/mc/messages/ModeratorPromoted.java @@ -0,0 +1,15 @@ +package games.strategy.engine.framework.startup.mc.messages; + +import java.io.Serializable; +import lombok.Value; + +/** + * This is a message sent to all players to indicate moderator player has changed. For example, in a + * bot game, if there are 3 players - then the bot and oldest joined will be moderators. If the + * oldest joined leaves, then the remaining player become smoderator. + */ +@Value +public class ModeratorPromoted implements Serializable { + /** The name of the player who is now a moderator. */ + String playerName; +} diff --git a/game-app/game-core/src/main/java/games/strategy/net/MessageHeader.java b/game-app/game-core/src/main/java/games/strategy/net/MessageHeader.java index 422bc321cbd..8ce8e90e175 100644 --- a/game-app/game-core/src/main/java/games/strategy/net/MessageHeader.java +++ b/game-app/game-core/src/main/java/games/strategy/net/MessageHeader.java @@ -18,4 +18,13 @@ public class MessageHeader implements Serializable { // from can be null if the sending node doesnt know its own address @Nullable private final INode from; private final Serializable message; + + /** Indicates if the message is intended for everyone (true). */ + public boolean isBroadcast() { + return to == null; + } + + public boolean isAddressedTo(INode target) { + return target.equals(to); + } } diff --git a/game-app/game-core/src/main/java/games/strategy/net/Messengers.java b/game-app/game-core/src/main/java/games/strategy/net/Messengers.java index 69b03820e7a..2fb97e5bd45 100644 --- a/game-app/game-core/src/main/java/games/strategy/net/Messengers.java +++ b/game-app/game-core/src/main/java/games/strategy/net/Messengers.java @@ -106,6 +106,10 @@ public void send(final Serializable msg, final INode to) { messenger.send(msg, to); } + public void sendToServer(final Serializable msg) { + messenger.send(msg, messenger.getServerNode()); + } + @Override public void addMessageListener(final IMessageListener listener) { messenger.addMessageListener(listener); diff --git a/game-app/game-core/src/main/java/games/strategy/net/ServerMessenger.java b/game-app/game-core/src/main/java/games/strategy/net/ServerMessenger.java index 878a0011c68..368b1186c78 100644 --- a/game-app/game-core/src/main/java/games/strategy/net/ServerMessenger.java +++ b/game-app/game-core/src/main/java/games/strategy/net/ServerMessenger.java @@ -2,6 +2,9 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Preconditions; +import games.strategy.engine.framework.GameRunner; +import games.strategy.engine.framework.startup.mc.messages.ModeratorPromoted; import games.strategy.net.nio.NioSocket; import games.strategy.net.nio.NioSocketListener; import games.strategy.net.nio.QuarantineConversation; @@ -17,6 +20,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -53,9 +57,11 @@ public class ServerMessenger implements IServerMessenger, NioSocketListener { // all our nodes private final Map nodeToChannel = new ConcurrentHashMap<>(); private final Map channelToNode = new ConcurrentHashMap<>(); + // list that keeps track of the order in which nodes have joined. Nodes are removed as they leave + private final List nodeJoinOrder = new LinkedList<>(); + private final Map cachedMacAddresses = new ConcurrentHashMap<>(); private final Set miniBannedIpAddresses = new ConcurrentSkipListSet<>(); - private final Set miniBannedMacAddresses = new ConcurrentSkipListSet<>(); @Setter @Nullable private GameToLobbyConnection gameToLobbyConnection; @@ -70,6 +76,7 @@ public ServerMessenger( nioSocket = new NioSocket(objectStreamFactory, this); acceptorSelector = Selector.open(); node = new Node(name, IpFinder.findInetAddress(), boundPort); + nodeJoinOrder.add(node); ThreadRunner.runInNewThread(new ConnectionHandler()); } @@ -144,10 +151,11 @@ public void messageReceived(final MessageHeader msg, final SocketChannel channel if (!expectedReceive.equals(msg.getFrom())) { throw new IllegalStateException("Expected: " + expectedReceive + " not: " + msg.getFrom()); } - if (msg.getTo() == null) { + + if (msg.isBroadcast()) { forwardBroadcast(msg); notifyListeners(msg); - } else if (msg.getTo().equals(node)) { + } else if (msg.isAddressedTo(node)) { notifyListeners(msg); } else { forward(msg); @@ -161,14 +169,24 @@ public boolean isPlayerBanned(final String ip, final String mac) { } return miniBannedIpAddresses.contains(ip) - || miniBannedMacAddresses.contains(mac) || (gameToLobbyConnection != null && gameToLobbyConnection.isPlayerBanned(ip)); } @Override public void banPlayer(final String ip, final String mac) { miniBannedIpAddresses.add(ip); - miniBannedMacAddresses.add(mac); + } + + /** Bans & disconnects a player. */ + public void banPlayer(String playerName) { + getNodes().stream() + .filter(n -> n.getName().equals(playerName)) + .findAny() + .ifPresent( + nodeToBan -> { + miniBannedIpAddresses.add(nodeToBan.getIpAddress()); + removeConnection(nodeToBan); + }); } private void forward(final MessageHeader msg) { @@ -303,6 +321,33 @@ public boolean isServer() { return true; } + public boolean isModerator(INode node) { + return getModerators().stream() + .anyMatch( + moderator -> moderator.equals(node) && moderator.getName().equals(node.getName())); + } + + /** + * Moderator is the first person to join, which is the host player. In 'headless' case, it is the + * host player plus the next 'oldest' player. + */ + private List getModerators() { + Preconditions.checkState(!nodeJoinOrder.isEmpty()); + if (nodeJoinOrder.size() == 1) { + return List.of(nodeJoinOrder.get(0)); + } else { + return List.of(nodeJoinOrder.get(0), nodeJoinOrder.get(1)); + } + } + + /** Disconnects a player by player name. */ + public void removeConnection(String playerName) { + getNodes().stream() + .filter(n -> n.getName().equals(playerName)) + .findAny() + .ifPresent(this::removeConnection); + } + @Override public void removeConnection(final INode nodeToRemove) { if (nodeToRemove.equals(this.node)) { @@ -311,13 +356,24 @@ public void removeConnection(final INode nodeToRemove) { notifyPlayerRemoval(nodeToRemove); final SocketChannel channel = nodeToChannel.remove(nodeToRemove); if (channel == null) { - log.warn("Could not find node to remove: " + nodeToRemove); + log.warn("Could not find node to remove: {}", nodeToRemove); return; } channelToNode.remove(channel); nioSocket.close(channel); + + boolean removingModerator = getModerators().contains(nodeToRemove); + nodeJoinOrder.remove(nodeToRemove); + if (removingModerator) { + INode newModerator = getModerators().get(getModerators().size() - 1); + + nodeToChannel + .keySet() + .forEach(node -> send(new ModeratorPromoted(newModerator.getName()), node)); + } + notifyConnectionsChanged(false, nodeToRemove); - log.info("Connection removed:" + nodeToRemove); + log.info("Connection removed: {}", nodeToRemove); } @Override @@ -347,7 +403,14 @@ public void socketUnquarantined( nodeToChannel.put(remote, channel); } channelToNode.put(channel, remote); + // only keep track of join order if the game is headless. If game is not headless, + // then it will end when the host leaves (and so we won't need to worry about + // promoting a next moderator). + if (GameRunner.headless()) { + nodeJoinOrder.add(remote); + } notifyConnectionsChanged(true, remote); - log.info("Connection added to:" + remote); + + log.info("Connection added to: {}", remote); } } diff --git a/game-app/game-core/src/test/java/games/strategy/engine/chat/ChatIntegrationTest.java b/game-app/game-core/src/test/java/games/strategy/engine/chat/ChatIntegrationTest.java index 6a1d874b8a3..1d1ebcee213 100644 --- a/game-app/game-core/src/test/java/games/strategy/engine/chat/ChatIntegrationTest.java +++ b/game-app/game-core/src/test/java/games/strategy/engine/chat/ChatIntegrationTest.java @@ -9,8 +9,8 @@ import games.strategy.engine.message.unifiedmessenger.UnifiedMessenger; import games.strategy.net.ClientMessenger; import games.strategy.net.IMessenger; -import games.strategy.net.IServerMessenger; import games.strategy.net.Messengers; +import games.strategy.net.ServerMessenger; import games.strategy.net.TestServerMessenger; import games.strategy.net.websocket.ClientNetworkBridge; import games.strategy.triplea.settings.AbstractClientSettingTestCase; @@ -32,7 +32,7 @@ final class ChatIntegrationTest extends AbstractClientSettingTestCase { private static final int MESSAGE_COUNT = 50; private static final int NODE_COUNT = 3; - private IServerMessenger messenger; + private ServerMessenger messenger; private IMessenger client1Messenger; private IMessenger client2Messenger; private RemoteMessenger remoteMessenger; @@ -130,7 +130,7 @@ private void runChatTest(final ChatTest chatTest) { private ChatController newChatController() { return new ChatController( - CHAT_NAME, new Messengers(messenger, remoteMessenger, channelMessenger)); + CHAT_NAME, new Messengers(messenger, remoteMessenger, channelMessenger), messenger); } private static Chat newChat(final Messengers messengers) {