From 7af0fd40a1a1bdcbb79b5147777e7a711f27450c Mon Sep 17 00:00:00 2001 From: Yannik Bretschneider Date: Sun, 6 Jun 2021 17:44:53 +0200 Subject: [PATCH] feat: implemented UserManager message forwarding --- .../server/netconnector/UserManager.java | 95 ++++++++++++++++--- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserManager.java b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserManager.java index d4fec75..627bd1a 100644 --- a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserManager.java +++ b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserManager.java @@ -8,15 +8,18 @@ import uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage; import uulm.teamname.marvelous.gamelibrary.messages.client.*; import uulm.teamname.marvelous.gamelibrary.messages.server.HelloClientMessage; import uulm.teamname.marvelous.server.Server; +import uulm.teamname.marvelous.server.lobbymanager.LobbyManager; import uulm.teamname.marvelous.server.lobbymanager.Participant; import org.java_websocket.WebSocket; import java.util.*; -/** Class that manages users. It is meant as an extension to the {@link MarvelousServer} class, and should not be called - * from anywhere else. This is the first place where messages are relayed to after they get received. This class is - * responsible for handshakes, reconnects and WebSocket-to-Participant matching. It is not thread-safe. +/** Class that manages users. It is meant as an extension to the {@link MarvelousServer} class. This is the first + * place where messages are relayed to after they get received. This class is + * responsible for handshakes, reconnects and WebSocket-to-Participant matching. + * It is designed to be thread-safe. The Singleton of this class also contains a + * {@link LobbyManager}, which manages the lobbys. */ public class UserManager { @@ -33,6 +36,10 @@ public class UserManager { return instance; } + /** The {@link LobbyManager} associated with the {@link UserManager}. It is therefore a kind of extension + * to the current singleton, and may of course not be called from anywhere else */ + private final LobbyManager lobbyManager; + /** A set of users that aren't assigned to lobbies or character selection yet */ private final HashSet newUsers; @@ -64,12 +71,19 @@ public class UserManager { this.readyToReconnect = new HashMap<>(); this.inGame = new HashMap<>(); this.activeParticipants = new HashMap<>(); + + Logger.trace("Instantiating LobbyManager with message sending callbacks"); + this.lobbyManager = new LobbyManager(this::sendMessage, this::sendError); + + Logger.trace("Instantiating JSON with the server's CharacterConfig"); this.json = new JSON(Server.getCharacterConfig()); } /** Called on a new WebSocket connection. Places the WebSocket and its ResourceDescriptor in a HashMap. */ void connectUser(WebSocket conn) { - newUsers.add(conn); + synchronized (newUsers) { + newUsers.add(conn); + } } /** @@ -106,7 +120,15 @@ public class UserManager { } else if (parsedMessage instanceof PlayerReadyMessage) { Logger.trace("Message is instanceof PlayerReadyMessage, assigning lobby"); assignLobby(conn, (PlayerReadyMessage) parsedMessage); - } else { + } else if (parsedMessage instanceof CharacterSelectionMessage) { + Logger.trace("Message is instanceof CharacterSelectionMessage, passing to LobbyConnection"); + charactersSelected(conn, (CharacterSelectionMessage) parsedMessage); + } else if (parsedMessage instanceof RequestMessage) { + Logger.trace("Message is instanceof RequestMessage, passing to LobbyConnection"); + relayRequestMessage(conn, (RequestMessage) parsedMessage); + } + + else { Logger.debug("Message '{}' has invalid type, Error will be sent"); sendError(conn, String.format( "Message '%s' has invalid type, and cannot be processed on the server", @@ -186,13 +208,48 @@ public class UserManager { } void assignLobby(WebSocket conn, PlayerReadyMessage message) { - // TODO: Assign a lobby + if (readyToReconnect.containsKey(conn) || readyToConnect.containsKey(conn)) { + if (readyToConnect.containsKey(conn)) { + synchronized (readyToConnect) { + readyToConnect.remove(conn); + } + } else { + synchronized (readyToReconnect) { + readyToReconnect.remove(conn); + } + } + synchronized (inGame) { + var participant = lobbyManager.assignLobbyToConnection(conn, "SomeName", message); + inGame.put(conn, participant); + } + } else { + Logger.debug( + "WebSocket {} sent PlayerReadyMessage to server while not ready to connect, sending error", + conn); + sendError(conn, "Invalid message, as client is not ready to connect"); + } } void charactersSelected(WebSocket conn, CharacterSelectionMessage message) { + if (inGame.containsKey(conn)) { + lobbyManager.relayMessageToLobby(inGame.get(conn), message); + } else { + Logger.debug( + "WebSocket {} sent CharacterSelectionMessage to server while not ingame, sending error", + conn); + sendError(conn, "Invalid message, as client is not ingame"); + } } - void relayRequestMessage(Participant conn, RequestMessage message) { + void relayRequestMessage(WebSocket conn, RequestMessage message) { + if (inGame.containsKey(conn)) { + lobbyManager.relayMessageToLobby(inGame.get(conn), message); + } else { + Logger.debug( + "WebSocket {} sent RequestMessage to server while not ingame, sending error", + conn); + sendError(conn, "Invalid message, as client is not ingame"); + } } void disconnectUser(WebSocket conn, boolean closedByRemote) { @@ -200,22 +257,33 @@ public class UserManager { synchronized (newUsers) { newUsers.remove(conn); } + synchronized (readyToConnect) { + readyToConnect.remove(conn); + } + synchronized (readyToReconnect) { + readyToReconnect.remove(conn); + } synchronized (inGame) { inGame.remove(conn); } } - public void sendMessage(WebSocket conn, BasicMessage message) { - var jsonRepresentingMessage = json.stringify(message); - if (jsonRepresentingMessage.isEmpty()) { - Logger.warn("Message {} could not be serialized!", message); + void sendMessage(WebSocket conn, BasicMessage message) { + Logger.trace("Sending message to WebSocket {}", conn); + if (conn == null) { + Logger.debug("Message sent to non-existent websocket, ignoring"); } else { - conn.send(jsonRepresentingMessage.get()); + var jsonRepresentingMessage = json.stringify(message); + if (jsonRepresentingMessage.isEmpty()) { + Logger.warn("Message {} could not be serialized!", message); + } else { + conn.send(jsonRepresentingMessage.get()); + } } } /** Sends an {@link uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage} to the specified user. */ - public void sendError(WebSocket conn, String error) { + void sendError(WebSocket conn, String error) { Logger.debug("Sending error message '{}' to WebSocket {}", error, conn); var errorMessage = new ErrorMessage(); errorMessage.message = error; @@ -230,6 +298,7 @@ public class UserManager { } public int getUserCount() { + // FIXME: This is bugged at the moment return newUsers.size() + readyToConnect.size() + readyToReconnect.size() + inGame.size(); }