From 02b2a58cce4e8dbf75307dd03000e37d61cd9711 Mon Sep 17 00:00:00 2001 From: Yannik Bretschneider Date: Sat, 5 Jun 2021 19:40:10 +0200 Subject: [PATCH] feat: partially implemented GameAssignment --- .../server/netconnector/MarvelousServer.java | 9 +- .../server/netconnector/UserManager.java | 99 +++++++++++++------ 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/MarvelousServer.java b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/MarvelousServer.java index 5cb33c0..dfd72f3 100644 --- a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/MarvelousServer.java +++ b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/MarvelousServer.java @@ -12,32 +12,29 @@ public class MarvelousServer extends WebSocketServer { @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { Logger.info("New client connected. Adding new User."); - userManager.connectUser(conn, handshake); - // TODO: Send messages to UserManager, which then packages that stuff and sends it further + userManager.connectUser(conn); } @Override public void onClose(WebSocket conn, int code, String reason, boolean remote) { Logger.info("Client disconnected"); userManager.disconnectUser(conn, remote); - // TODO: Send messages to UserManager, which then removes the user and such } @Override public void onMessage(WebSocket conn, String message) { Logger.debug("Message received: {}", message); userManager.messageReceived(conn, message); - // TODO: Send messages to UserManager, which then packages them and sends them further } @Override public void onError(WebSocket conn, Exception ex) { - // TODO: Send errors to UserManager, which then answers with even more errors + Logger.warn("WebSocket-Error occurred: {}", ex.getMessage()); } @Override public void onStart() { - // TODO: Get the UserManager, and tell it that this thing is active + Logger.info("MarvelousServer started on Address {}", this.getAddress().toString()); } private final UserManager userManager; 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 b4f63e8..c71acf1 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 @@ -1,9 +1,8 @@ package uulm.teamname.marvelous.server.netconnector; -import org.java_websocket.handshake.ClientHandshake; import org.tinylog.Logger; import uulm.teamname.marvelous.gamelibrary.json.JSON; -import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage; +import uulm.teamname.marvelous.gamelibrary.json.ValidationUtility; import uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage; import uulm.teamname.marvelous.gamelibrary.messages.client.*; import uulm.teamname.marvelous.server.lobbymanager.Participant; @@ -12,13 +11,17 @@ 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. + */ public class UserManager { /** A set of users that aren't assigned to lobbies or character selection yet */ private final HashSet newUsers; /** A set of users that can reconnect if they wish to do so, and their matching Participants */ - private final HashMap readyToReconnect; + private final HashMap readyToReconnect; /** A set of users that only have to send the * {@link uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage} to be assigned @@ -49,24 +52,51 @@ public class UserManager { } /** Called on a new WebSocket connection. Places the WebSocket and its ResourceDescriptor in a HashMap. */ - public void connectUser(WebSocket conn, ClientHandshake handshake) { - synchronized (newUsers) { - newUsers.add(conn); - } + public void connectUser(WebSocket conn) { + newUsers.add(conn); } + /** + * Called on any received messages. The method checks the message for validity, and then relays it + * accordingly. {@link HelloServerMessage HelloServerMessages} and {@link ReconnectMessage ReconnectMessages} + * are handled in this component, whereby a handshake or reconnect is performed, respectively. + * @param conn is the {@link WebSocket} that sent the message + * @param message is the {@link String} sent by the connection + */ public void messageReceived(WebSocket conn, String message) { - // TODO: Implement more than only a basic handshake - + Logger.trace("Parsing message..."); var parsedMessageOptional = json.parse(message); if (parsedMessageOptional.isEmpty()) { - Logger.debug("Invalid message '{}' received, sending error", message); + Logger.debug("Message '{}' was invalid, sending error", message); sendError(conn, "Message could not be parsed"); } else { var parsedMessage = parsedMessageOptional.get(); - if (parsedMessage instanceof HelloServerMessage) - handshake(conn, (HelloServerMessage) parsedMessage); + Logger.trace("Validating message..."); + var violations = ValidationUtility.validate(parsedMessage); + + if (violations.isPresent()) { + Logger.debug("Message '{}' was invalid: {}, sending error", message, violations.get()); + sendError(conn, violations.get()); + } else { + Logger.trace("Message was valid. Checking type of message..."); + + if (parsedMessage instanceof HelloServerMessage) { + Logger.trace("Message is instanceof HelloServerMessage, initiating handshake"); + handshake(conn, (HelloServerMessage) parsedMessage); + } else if (parsedMessage instanceof ReconnectMessage) { + Logger.trace("Message is instanceof ReconnectMessage, initiating reconnect"); + reconnectClient(conn, (ReconnectMessage) parsedMessage); + } else if (parsedMessage instanceof PlayerReadyMessage) { + Logger.trace("Message is instanceof PlayerReadyMessage, assigning lobby"); + assignLobby(conn, (PlayerReadyMessage) 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", + message)); + } + } } } @@ -85,7 +115,7 @@ public class UserManager { var participant = activeParticipants.get(clientID); if (participant != null) { synchronized (readyToReconnect) { - readyToReconnect.put(conn, participant); + readyToReconnect.put(conn, clientID); } } else { Logger.trace("removing handshaking user from newUsers"); @@ -99,33 +129,37 @@ public class UserManager { } } - public void reconnectClient(WebSocket conn, ReconnectMessage message) { - if (!readyToConnect.containsKey(conn)) { + void reconnectClient(WebSocket conn, ReconnectMessage message) { + if (!readyToReconnect.containsKey(conn)) { Logger.debug("Non-reconnect-allowed client has sent reconnect message, sending error"); sendError(conn, "Reconnect is not possible"); - return; - } - Logger.info("Reconnecting client {} to their lobby"); - var participantToRestore = readyToReconnect.get(conn); - synchronized (participantToRestore) { + } else if (message.reconnect) { + Logger.info("Reconnecting client {} to their lobby", conn); + var clientID = readyToReconnect.get(conn); + var participantToRestore = activeParticipants.get(clientID); + participantToRestore.setConnection(conn); - } - synchronized (readyToReconnect) { + readyToReconnect.remove(conn); + inGame.put(conn, participantToRestore); + // activeParticipants remains the same, as no players have been removed from the game + } else { + Logger.debug( + "Client {} refused reconnection, will therefore be put into readyToConnect clients", + conn); + + var clientID = readyToReconnect.get(conn); + readyToConnect.put(conn, clientID); readyToReconnect.remove(conn); } - synchronized (inGame) { - inGame.put(conn, participantToRestore); - } - // activeParticipants remains the same, as no players have been removed from the game } - public void assignLobby(WebSocket conn, PlayerReadyMessage message) { + void assignLobby(WebSocket conn, PlayerReadyMessage message) { } - public void charactersSelected(WebSocket conn, CharacterSelectionMessage message) { + void charactersSelected(WebSocket conn, CharacterSelectionMessage message) { } - public void relayRequestMessage(Participant conn, RequestMessage message) { + void relayRequestMessage(Participant conn, RequestMessage message) { } /** Sends an {@link uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage} to the specified user. */ @@ -158,26 +192,31 @@ public class UserManager { } /** Package-private getter for mutable newUsers HashSet, meant for testing */ + @Deprecated HashSet getNewUsers() { return newUsers; } /** Package-private getter for mutable readyToReconnect HashMap, meant for testing */ - HashMap getReadyToReconnect() { + @Deprecated + HashMap getReadyToReconnect() { return readyToReconnect; } /** Package-private getter for mutable readyToConnect HashMap, meant for testing */ + @Deprecated HashMap getReadyToConnect() { return readyToConnect; } /** Package-private getter for mutable inGame HashMap, meant for testing */ + @Deprecated HashMap getInGame() { return inGame; } /** Package-private getter for mutable activeParticipants HashMap, meant for testing */ + @Deprecated HashMap getActiveParticipants() { return activeParticipants; }