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 b908f73..5cb33c0 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
@@ -3,30 +3,53 @@ package uulm.teamname.marvelous.server.netconnector;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
+import org.tinylog.Logger;
+import uulm.teamname.marvelous.gamelibrary.json.JSON;
+
+import java.net.InetSocketAddress;
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
}
@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
}
@Override
public void onStart() {
+ // TODO: Get the UserManager, and tell it that this thing is active
+ }
+ private final UserManager userManager;
+
+ public MarvelousServer(InetSocketAddress address, JSON json) {
+ super(address);
+ this.userManager = new UserManager(json);
+ }
+
+ /** Practically No-Arg constructor for testing. NEVER USE ANYWHERE ELSE!
+ * @param userManager*/
+ protected MarvelousServer(UserManager userManager) { super();
+ this.userManager = userManager;
}
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/SUID.java b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/SUID.java
new file mode 100644
index 0000000..5e2d29d
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/SUID.java
@@ -0,0 +1,43 @@
+package uulm.teamname.marvelous.server.netconnector;
+
+import java.util.Objects;
+
+/** The SUID (or Somewhat Unique ID) is the ID described by the Name and ID that the client provides on Connect */
+public class SUID {
+ private final String name;
+ private final String deviceID;
+
+ public SUID(String name, String deviceID) {
+ this.name = name;
+ this.deviceID = deviceID;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDeviceID() {
+ return deviceID;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SUID suid = (SUID) o;
+ return Objects.equals(name, suid.name) && Objects.equals(deviceID, suid.deviceID);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, deviceID);
+ }
+
+ @Override
+ public String toString() {
+ return "SUID{" +
+ "name='" + name + '\'' +
+ ", deviceID='" + deviceID + '\'' +
+ '}';
+ }
+}
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
new file mode 100644
index 0000000..11b3eef
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserManager.java
@@ -0,0 +1,155 @@
+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.messages.ErrorMessage;
+import uulm.teamname.marvelous.gamelibrary.messages.client.HelloServerMessage;
+import uulm.teamname.marvelous.server.lobbymanager.Participant;
+import uulm.teamname.marvelous.server.netconnector.UserMetadata;
+
+import org.java_websocket.WebSocket;
+
+import java.util.*;
+
+public class UserManager {
+
+ /** A set of users that aren't assigned to lobbies or character selection yet, and their resource descriptors */
+ private final HashMap newUsers;
+
+ /** A set of users that can reconnect if they wish to do so, and their matching Participants */
+ 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
+ * to a lobby
+ */
+ private final HashMap readyToConnect;
+
+
+ /** A set of users that are already assigned to lobbies or character selection */
+ private final HashSet inGame;
+
+ /**
+ * A map mapping {@link SUID SUIDs} to {@link Participant Participants} to assert whether (and where to) reconnect
+ * an user
+ */
+ private final HashMap activeParticipants;
+
+ private final JSON json;
+
+ /** Constructs a new, empty UserManager */
+ public UserManager(JSON json) {
+ this.newUsers = new HashMap<>();
+ this.readyToConnect = new HashMap<>();
+ this.readyToReconnect = new HashMap<>();
+ this.inGame = new HashSet<>();
+ this.activeParticipants = new HashMap<>();
+ this.json = json;
+ }
+
+ /** Called on a new WebSocket connection. Places the WebSocket and its ResourceDescriptor in a HashMap. */
+ public void connectUser(WebSocket conn, ClientHandshake handshake) {
+ var metadata = new UserMetadata(conn, handshake.getResourceDescriptor());
+ synchronized (newUsers) {
+ newUsers.put(conn, metadata);
+ }
+ }
+
+ public void messageReceived(WebSocket conn, String message) {
+ // TODO: Implement more than only a basic handshake
+
+ var parsedMessageOptional = json.parse(message);
+ if (parsedMessageOptional.isEmpty()) {
+ Logger.debug("Invalid message '{}' received, sending error", message);
+ sendError(conn, "Message could not be parsed");
+ } else {
+ var parsedMessage = parsedMessageOptional.get();
+
+ if (parsedMessage instanceof HelloServerMessage)
+ handshake(conn, (HelloServerMessage) parsedMessage);
+ }
+ }
+
+ void handshake(WebSocket conn, HelloServerMessage message) {
+ if (!newUsers.containsKey(conn)) {
+ Logger.debug("websocket {} sent HelloServerMessage outside of handshake", conn);
+ sendError(conn, "Invalid message, as Handshake is already completed");
+ return;
+ }
+
+ Logger.info("Performing handshake with user '{}'", message.name);
+
+ SUID clientID = new SUID(message.name, message.deviceID);
+
+ // check if client is reconnected
+ var participant = activeParticipants.get(clientID);
+ if (participant != null) {
+ synchronized (readyToReconnect) {
+ readyToReconnect.put(conn, participant);
+ }
+ } else {
+ var metadata = newUsers.get(conn);
+ metadata.username = message.name;
+
+ Logger.trace("removing handshaking user from newUsers");
+ synchronized (newUsers) {
+ newUsers.remove(conn);
+ }
+ Logger.trace("adding handshaking user to readyToConnect");
+ synchronized (readyToConnect) {
+ readyToConnect.put(conn, metadata);
+ }
+ }
+ }
+
+ /** Sends an {@link uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage} to the specified user. */
+ public void sendError(WebSocket conn, String error) {
+ Logger.debug("Sending error message '{}' to WebSocket {}", error, conn);
+ var errorMessage = new ErrorMessage();
+ errorMessage.message = error;
+ conn.send(json.stringify(errorMessage).get());
+ }
+
+ public void disconnectUser(WebSocket conn, boolean closedByRemote) {
+ // TODO: notify clients and such if the connection was in fact closed by the remote. Also remove participant
+ synchronized (newUsers) {
+ newUsers.remove(conn);
+ }
+ synchronized (inGame) {
+ inGame.remove(conn);
+ }
+ }
+
+ public boolean isUserConnected(WebSocket user) {
+ return newUsers.containsKey(user) ||
+ readyToReconnect.containsKey(user) ||
+ readyToConnect.containsKey(user) ||
+ inGame.contains(user);
+ }
+
+ public int getUserCount() {
+ return newUsers.size() + readyToConnect.size() + readyToReconnect.size() + inGame.size();
+ }
+
+ public Map getNewUsers() {
+ return Collections.unmodifiableMap(newUsers);
+ }
+
+ public Map getReadyToReconnect() {
+ return Collections.unmodifiableMap(readyToReconnect);
+ }
+
+ public Map getReadyToConnect() {
+ return Collections.unmodifiableMap(readyToConnect);
+ }
+
+ public Set getInGame() {
+ return Collections.unmodifiableSet(inGame);
+ }
+
+ public Map getActiveParticipants() {
+ return Collections.unmodifiableMap(activeParticipants);
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserMetadata.java b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserMetadata.java
new file mode 100644
index 0000000..535a238
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserMetadata.java
@@ -0,0 +1,14 @@
+package uulm.teamname.marvelous.server.netconnector;
+
+import org.java_websocket.WebSocket;
+
+class UserMetadata {
+ public String username;
+ public final WebSocket conn;
+ public final String resourceDescriptor;
+
+ public UserMetadata(WebSocket conn, String resourceDescriptor) {
+ this.conn = conn;
+ this.resourceDescriptor = resourceDescriptor;
+ }
+}