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;
}