feat: partially implemented net connection and handshake
This commit is contained in:
parent
051c91f9de
commit
69f77a1423
@ -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. <b>NEVER USE ANYWHERE ELSE!</b>
|
||||
* @param userManager*/
|
||||
protected MarvelousServer(UserManager userManager) { super();
|
||||
this.userManager = userManager;
|
||||
}
|
||||
}
|
||||
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -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<WebSocket, UserMetadata> newUsers;
|
||||
|
||||
/** A set of users that can reconnect if they wish to do so, and their matching Participants */
|
||||
private final HashMap<WebSocket, Participant> 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<WebSocket, UserMetadata> readyToConnect;
|
||||
|
||||
|
||||
/** A set of users that are already assigned to lobbies or character selection */
|
||||
private final HashSet<WebSocket> inGame;
|
||||
|
||||
/**
|
||||
* A map mapping {@link SUID SUIDs} to {@link Participant Participants} to assert whether (and where to) reconnect
|
||||
* an user
|
||||
*/
|
||||
private final HashMap<SUID, Participant> 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<WebSocket, UserMetadata> getNewUsers() {
|
||||
return Collections.unmodifiableMap(newUsers);
|
||||
}
|
||||
|
||||
public Map<WebSocket, Participant> getReadyToReconnect() {
|
||||
return Collections.unmodifiableMap(readyToReconnect);
|
||||
}
|
||||
|
||||
public Map<WebSocket, UserMetadata> getReadyToConnect() {
|
||||
return Collections.unmodifiableMap(readyToConnect);
|
||||
}
|
||||
|
||||
public Set<WebSocket> getInGame() {
|
||||
return Collections.unmodifiableSet(inGame);
|
||||
}
|
||||
|
||||
public Map<SUID, Participant> getActiveParticipants() {
|
||||
return Collections.unmodifiableMap(activeParticipants);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user