diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..a1fe99e
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/Server.java b/Server/src/main/java/uulm/teamname/marvelous/server/ServerApplication.java
similarity index 83%
rename from Server/src/main/java/uulm/teamname/marvelous/server/Server.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/ServerApplication.java
index 75f8930..e0432fd 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/Server.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/ServerApplication.java
@@ -11,7 +11,8 @@ import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig;
import uulm.teamname.marvelous.gamelibrary.json.JSON;
import uulm.teamname.marvelous.gamelibrary.json.ValidationUtility;
import uulm.teamname.marvelous.server.args.ServerArgs;
-import uulm.teamname.marvelous.server.netconnector.MarvelousServer;
+import uulm.teamname.marvelous.server.net.MarvelousServer;
+import uulm.teamname.marvelous.server.net.ServerSession;
import java.io.*;
import java.net.InetSocketAddress;
@@ -25,20 +26,22 @@ import java.util.Map;
* {@code -c .\configs\marvelheros.character.json -m .\configs\matchconfig_1.game.json
* -s .\configs\asgard.scenario.json -v} into the arguments field.
*/
-public class Server {
+public class ServerApplication {
private static PartyConfig partyConfig;
private static ScenarioConfig scenarioConfig;
private static CharacterConfig characterConfig;
- private static Integer maxLobbies;
+ private static ServerSession session;
public static void main(String[] args) {
+ Thread.currentThread().setName("Main");
+
ServerArgs serverArgs = new ServerArgs();
JCommander jc = JCommander.newBuilder().addObject(serverArgs).build();
try {
jc.parse(args);
- } catch (ParameterException e) {
+ }catch(ParameterException e) {
Logger.error("Invalid parameters: {}", e.getMessage());
System.exit(1);
return;
@@ -50,13 +53,11 @@ public class Server {
return;
}
- maxLobbies = serverArgs.getMaxLobbies();
-
- if (serverArgs.isVerbose() || serverArgs.isCheckConfig()) {
+ if(serverArgs.isVerbose() || serverArgs.isCheckConfig()) {
// If checkConfig, the LogLevel is also set to max, because more information
// is exactly what checking the requirements means
setLogLevel(5);
- } else {
+ }else {
setLogLevel(serverArgs.getLogLevel());
}
@@ -72,25 +73,33 @@ public class Server {
}
Logger.info("populating static Server variables with config objects");
- Server.scenarioConfig = scenarioConfig;
- Server.characterConfig = characterConfig;
- Server.partyConfig = partyConfig;
+ ServerApplication.scenarioConfig = scenarioConfig;
+ ServerApplication.characterConfig = characterConfig;
+ ServerApplication.partyConfig = partyConfig;
InetSocketAddress address = new InetSocketAddress(serverArgs.getPort());
Logger.trace("Inet address {} created", address);
+ Logger.trace("Instantiating ServerSession...");
+ session = new ServerSession();
+
Logger.trace("Instantiating MarvelousServer...");
- MarvelousServer netConnector = new MarvelousServer(address);
+ MarvelousServer server = new MarvelousServer(address);
Logger.trace("Starting MarvelousServer...");
- netConnector.start();
+ server.start();
+
+ Logger.trace("Starting ServerSession...");
+ getSession().run();
Logger.trace("End of Main reached. Exiting main thread.");
}
- /** Function that sets the log level for {@link Logger Tinylog}.
+ /**
+ * Function that sets the log level for {@link Logger Tinylog}.
* It has to be executed BEFORE ANY LOGGING OPERATIONS .
*/
+ @SuppressWarnings("DuplicateBranchesInSwitch")
private static void setLogLevel(int logLevel) {
Map map = new HashMap<>();
@@ -144,18 +153,21 @@ public class Server {
}
int grassFields = 0;
-
+ int portals = 0;
for (FieldType[] row: config.get().scenario) {
for (FieldType type: row) {
if (type == FieldType.GRASS) grassFields++;
+ if (type == FieldType.PORTAL) portals++;
}
- if (grassFields > 18) break;
}
- if (grassFields <= 18) {
- Logger.error(
- "Scenario Configuration vas invalid: Only {} grass fields found, which is less than 18"
- , grassFields);
+ if (grassFields < 20) {
+ Logger.error("Scenario Configuration vas invalid: Only {} grass fields found, which is less than 20", grassFields);
+ System.exit(1);
+ }
+
+ if (portals < 2) {
+ Logger.error("Scenario Configuration vas invalid: Only {} portals found, which is less than 2", portals);
System.exit(1);
}
@@ -231,8 +243,7 @@ public class Server {
return characterConfig;
}
- /** Returns the maximum amount of lobbies the server should create */
- public static Integer getMaxLobbies() {
- return maxLobbies;
+ public static ServerSession getSession() {
+ return session;
}
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/args/ServerArgs.java b/Server/src/main/java/uulm/teamname/marvelous/server/args/ServerArgs.java
index d06a6fa..e7f987b 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/args/ServerArgs.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/args/ServerArgs.java
@@ -4,6 +4,7 @@ import com.beust.jcommander.Parameter;
import java.io.File;
+@SuppressWarnings({"FieldMayBeFinal", "unused"})
public class ServerArgs {
/** Whether help is requested */
@Parameter(names = {"-h", "--help", "-?", "--?", "-H"}, help = true)
@@ -41,9 +42,6 @@ public class ServerArgs {
@Parameter(names = {"--replay", "-r"}, description = "Path for replay files to be saved at")
private String folderPath;
- @Parameter(names = {"--max-lobbies", "-L", "--team25-max-lobbies"}, description = "Maximum amount of lobbies that can be created")
- private int maxLobbies = 8;
-
/** Whether help is requested */
public boolean isHelp() {
return help;
@@ -89,11 +87,6 @@ public class ServerArgs {
return folderPath;
}
- /** The maximum mount of lobbies automatically created */
- public int getMaxLobbies() {
- return maxLobbies;
- }
-
@Override
public String toString() {
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/game/GameSession.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/GameSession.java
new file mode 100644
index 0000000..3c541bb
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/GameSession.java
@@ -0,0 +1,240 @@
+package uulm.teamname.marvelous.server.game;
+
+import uulm.teamname.marvelous.gamelibrary.ArrayTools;
+import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
+import uulm.teamname.marvelous.gamelibrary.events.Event;
+import uulm.teamname.marvelous.gamelibrary.events.EventBuilder;
+import uulm.teamname.marvelous.gamelibrary.events.EventType;
+import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance;
+import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
+import uulm.teamname.marvelous.gamelibrary.messages.server.EventMessage;
+import uulm.teamname.marvelous.gamelibrary.messages.server.GameStructureMessage;
+import uulm.teamname.marvelous.gamelibrary.messages.server.GeneralAssignmentMessage;
+import uulm.teamname.marvelous.gamelibrary.requests.Request;
+import uulm.teamname.marvelous.server.ServerApplication;
+import uulm.teamname.marvelous.server.game.pipelining.*;
+import uulm.teamname.marvelous.server.net.Client;
+import uulm.teamname.marvelous.server.net.ClientState;
+
+import java.util.*;
+
+public class GameSession {
+ public final String id = UUID.randomUUID().toString();
+
+ public final HashMap characterChoices = new HashMap<>();
+ private final HashMap characterIndices = new HashMap<>();
+ private final HashMap> characterSelection = new HashMap<>();
+
+ private final HashMap badRequests = new HashMap<>();
+
+ private boolean started = false;
+
+ private GameInstance instance;
+ private boolean paused = false;
+
+ private Pipeline pipeline;
+
+ public GameSession() {
+ List characters = ArrayTools.toArrayList(ServerApplication.getCharacterConfig().characters);
+ characterChoices.put(ParticipantType.PlayerOne, new CharacterProperties[characters.size() / 2]);
+ characterChoices.put(ParticipantType.PlayerTwo, new CharacterProperties[characters.size() / 2]);
+ characterIndices.put(ParticipantType.PlayerOne, new Integer[characters.size() / 2]);
+ characterIndices.put(ParticipantType.PlayerTwo, new Integer[characters.size() / 2]);
+ int n1 = 0;
+ int n2 = 0;
+ int i = 0;
+ Collections.shuffle(characters);
+ for(CharacterProperties character: characters) {
+ if(i < characters.size() / 2) {
+ characterChoices.get(ParticipantType.PlayerOne)[n1] = character;
+ characterIndices.get(ParticipantType.PlayerOne)[n1] = character.characterID;
+ n1++;
+ }else {
+ characterChoices.get(ParticipantType.PlayerTwo)[n2] = character;
+ characterIndices.get(ParticipantType.PlayerTwo)[n2] = character.characterID;
+ n2++;
+ }
+ i++;
+ }
+ }
+
+ public void setPaused(boolean paused) {
+ this.paused = paused;
+ }
+
+ public boolean getPaused() {
+ return paused;
+ }
+
+ public GameInstance getInstance() {
+ return instance;
+ }
+
+ public void addSpectator(Client client) {
+ GeneralAssignmentMessage response = new GeneralAssignmentMessage();
+ response.gameID = id;
+ client.sendMessage(response);
+
+ client.sendMessage(getGameStructure(client.getType()));
+ }
+
+ public void handleReconnect(Client client) {
+ GeneralAssignmentMessage response = new GeneralAssignmentMessage();
+ response.gameID = id;
+ client.sendMessage(response);
+
+ client.sendMessage(getGameStructure(client.getType()));
+ }
+
+ public void handleSelection(Client client, boolean[] selection) {
+ ArrayList indices = new ArrayList<>();
+ for(int i = 0; i < selection.length; i++) {
+ if(selection[i]) {
+ indices.add(characterIndices.get(client.getType())[i]);
+ }
+ }
+ characterSelection.put(client.getType(), indices);
+ }
+
+ public void handleRequests(Client client, Request[] messages) {
+ if(client.getType() == ParticipantType.Spectator || paused) {
+ return;
+ }
+
+ Optional> resultingEvents = pipeline.processRequests(client, messages);
+
+ if(resultingEvents.isEmpty()) {
+ reject(client);
+ }else {
+ accept(client, resultingEvents.get());
+
+ if(instance.state.isWon()) {
+ ServerApplication.getSession().reset();
+ }
+ }
+ }
+
+ public void handleDisconnect(Client client) {
+ if(started && client.getType() != ParticipantType.Spectator) {
+ winner(client.getType() == ParticipantType.PlayerOne ? ParticipantType.PlayerTwo : ParticipantType.PlayerOne);
+ }
+ }
+
+
+ public void start() {
+ initialize();
+
+ List start = instance.startGame(characterSelection.get(ParticipantType.PlayerOne), characterSelection.get(ParticipantType.PlayerTwo));
+
+ EventMessage message = new EventMessage();
+ message.messages = start.toArray(new Event[0]);
+ ServerApplication.getSession().broadcast(message);
+
+ started = true;
+ }
+
+ private void initialize() {
+ pipeline = new Pipeline();
+
+ var reqSegment = new RequestGameStateSegment(this);
+ var pauseSegment = new PauseSegment(this);
+ var filterEndRoundRequestSegment = new FilterEndRoundRequestSegment(this);
+ var disconnectSegment = new DisconnectSegment();
+ var playerFilterSegment = new PlayerFilterSegment();
+ var gameLogicSegment = new GameLogicSegment(this);
+ pipeline.addSegment(reqSegment)
+ .addSegment(pauseSegment)
+ .addSegment(filterEndRoundRequestSegment)
+ .addSegment(disconnectSegment)
+ .addSegment(playerFilterSegment)
+ .addSegment(gameLogicSegment);
+
+ instance = new GameInstance(ServerApplication.getPartyConfig(), ServerApplication.getCharacterConfig(), ServerApplication.getScenarioConfig());
+
+ for(Client player: ServerApplication.getSession().getPlayers()) {
+ player.setState(ClientState.Playing);
+ player.sendMessage(getGameStructure(player.getType()));
+ }
+ GameStructureMessage spectators = getGameStructure(ParticipantType.Spectator);
+ for(Client spectator: ServerApplication.getSession().getSpectators()) {
+ spectator.setState(ClientState.Playing);
+ spectator.sendMessage(spectators);
+ }
+ }
+
+ private void accept(Client source, List accepted) {
+ if(accepted.isEmpty()) {
+ return;
+ }
+
+ accepted.add(instance.getGameStateEvent());
+
+ EventMessage message = new EventMessage();
+ message.messages = accepted.toArray(new Event[0]);
+ ServerApplication.getSession().broadcast(message, source.getID());
+
+ accepted.add(0, new EventBuilder(EventType.Ack).buildGameEvent());
+
+ EventMessage response = new EventMessage();
+ response.messages = accepted.toArray(new Event[0]);
+ source.sendMessage(response);
+
+ badRequests.replace(source.getType(), 0);
+ }
+
+ private void reject(Client source) {
+ EventMessage response = new EventMessage();
+ response.messages = new Event[]{
+ new EventBuilder(EventType.Nack).buildGameEvent(),
+ instance.getGameStateEvent()
+ };
+ source.sendMessage(response);
+
+ int bad = badRequests.getOrDefault(source.getType(), 0) + 1;
+ badRequests.replace(source.getType(), bad);
+
+ if(bad >= 5) {
+ winner(source.getType() == ParticipantType.PlayerOne ? ParticipantType.PlayerTwo : ParticipantType.PlayerOne);
+ }
+ }
+
+ private synchronized void winner(ParticipantType winner) {
+ EventMessage message = new EventMessage();
+ message.messages = new Event[] {
+ new EventBuilder(EventType.WinEvent)
+ .withPlayerWon(winner.equals(ParticipantType.PlayerOne) ? 1 : 2)
+ .buildGameEvent(),
+ new EventBuilder(EventType.DisconnectEvent)
+ .buildGameEvent()
+ };
+
+ ServerApplication.getSession().broadcast(message);
+ ServerApplication.getSession().reset();
+ }
+
+
+ private GameStructureMessage getGameStructure(ParticipantType assignment) {
+ GameStructureMessage message = new GameStructureMessage();
+ message.assignment = assignment;
+ message.matchconfig = ServerApplication.getPartyConfig();
+ message.scenarioconfig = ServerApplication.getScenarioConfig();
+
+ var characters = ServerApplication.getCharacterConfig().getIDMap();
+ for(Client player: ServerApplication.getSession().getPlayers()) {
+ int n = 0;
+ CharacterProperties[] selected = new CharacterProperties[characters.size() / 4];
+ for(Integer i: characterSelection.get(player.getType())) {
+ selected[n++] = characters.get(i);
+ }
+ if(player.getType() == ParticipantType.PlayerOne) {
+ message.playerOneName = player.getID().getName();
+ message.playerOneCharacters = selected;
+ }else {
+ message.playerTwoName = player.getID().getName();
+ message.playerTwoCharacters = selected;
+ }
+ }
+
+ return message;
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/DisconnectSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/DisconnectSegment.java
new file mode 100644
index 0000000..eb7c460
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/DisconnectSegment.java
@@ -0,0 +1,23 @@
+package uulm.teamname.marvelous.server.game.pipelining;
+
+import org.tinylog.Logger;
+import uulm.teamname.marvelous.gamelibrary.events.Event;
+import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
+import uulm.teamname.marvelous.server.game.GameSession;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * The {@link DisconnectSegment} handles requests of {@link RequestType} DisconnectRequest.
+ */
+public class DisconnectSegment implements Segment {
+ @Override
+ public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
+ Logger.trace("DisconnectSegment received {} requests.", packet.size());
+ if(packet.containsRequestOfType(RequestType.DisconnectRequest)) {
+ packet.getOrigin().disconnect();
+ packet.clear();
+ }
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/FilterEndRoundRequestSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/FilterEndRoundRequestSegment.java
new file mode 100644
index 0000000..6956ace
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/FilterEndRoundRequestSegment.java
@@ -0,0 +1,41 @@
+package uulm.teamname.marvelous.server.game.pipelining;
+
+import org.tinylog.Logger;
+import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
+import uulm.teamname.marvelous.gamelibrary.events.Event;
+import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance;
+import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
+import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
+import uulm.teamname.marvelous.server.game.GameSession;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * {@link Segment} that checks for an {@link RequestType#EndRoundRequest}, and
+ * if it exists, checks for the player that has sent it. If the player sending
+ * this request is not the active player, it flags the message as an error.
+ */
+public class FilterEndRoundRequestSegment implements Segment {
+
+ private final GameSession parent;
+
+ public FilterEndRoundRequestSegment(GameSession parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
+ Logger.trace("FilterEndRoundSegment has received {} requests", packet.size());
+ if(packet.containsRequestOfType(RequestType.EndRoundRequest)) {
+ Logger.trace("Packet contains EndRoundRequest");
+
+ var active = parent.getInstance().state.getActiveCharacter().type;
+ var from = packet.getOrigin().getType() == ParticipantType.PlayerOne ? EntityType.P1 : EntityType.P2;
+ if(active != from) {
+ Logger.trace("Invalid endRoundRequest. Expected {} but got {}. Aborting...", active, from);
+ abort.set(true);
+ }
+ }
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/GameLogicSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/GameLogicSegment.java
similarity index 71%
rename from Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/GameLogicSegment.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/GameLogicSegment.java
index 42593cf..9094a6f 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/GameLogicSegment.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/GameLogicSegment.java
@@ -1,35 +1,36 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
+package uulm.teamname.marvelous.server.game.pipelining;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
+import uulm.teamname.marvelous.server.game.GameSession;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * The {@link GameLogicSegment} handles all {@link GameInstance game} relevant {@link Request Requests}. Therefore it
- * updates the game with the matching request.
+ * The {@link GameLogicSegment} handles all {@link GameInstance game} relevant {@link Request Requests}.
*/
public class GameLogicSegment implements Segment {
- private GameInstance game;
+ private final GameSession parent;
- public GameLogicSegment(GameInstance game) {
- this.game = game;
+ public GameLogicSegment(GameSession parent) {
+ this.parent = parent;
}
@Override
public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
Logger.trace("GameStateSegment received {} requests.", packet.size());
- var result = game.checkRequestsAndApply(packet);
+
+ var result = parent.getInstance().checkRequestsAndApply(packet);
Logger.trace("GameLogic generated {} events", result.map(List::size).orElse(0));
- if (result.isPresent()) {
+ if(result.isPresent()) {
Logger.trace("Result from GameLogic is present. Adding requests to carrier.");
carrier.addAll(result.get());
packet.clear();
- } else {
+ }else {
Logger.debug("Result from GameLogic is invalid. Triggering error.");
abort.set(true);
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Packet.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Packet.java
similarity index 86%
rename from Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Packet.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Packet.java
index 649209f..79ab834 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Packet.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Packet.java
@@ -1,20 +1,20 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
+package uulm.teamname.marvelous.server.game.pipelining;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
+import uulm.teamname.marvelous.server.net.Client;
import java.util.*;
/**
- * The {@link Packet} contains all {@link Request Requests} and the belonging {@link Participant}. You can add and
+ * The {@link Packet} contains all {@link Request Requests} and the belonging {@link Client}. You can add and
* remove {@link Request Requests} at will.
*/
public class Packet extends ArrayList {
- private final Participant origin;
+ private final Client origin;
- public Packet(Request[] requests, Participant origin) {
+ public Packet(Request[] requests, Client origin) {
this.origin = origin;
addAll(Arrays.asList(requests));
}
@@ -55,7 +55,7 @@ public class Packet extends ArrayList {
this.removeIf(request -> !listOfTypes.contains(request.type));
}
- public Participant getOrigin() {
+ public Client getOrigin() {
return origin;
}
@@ -69,6 +69,7 @@ public class Packet extends ArrayList {
}
@Override
+ @SuppressWarnings("MethodDoesntCallSuperMethod")
public Object clone() {
return new Packet(this.toArray(new Request[0]), this.origin);
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/PauseSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/PauseSegment.java
similarity index 72%
rename from Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/PauseSegment.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/PauseSegment.java
index 1a9a5f8..89ba818 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/PauseSegment.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/PauseSegment.java
@@ -1,4 +1,4 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
+package uulm.teamname.marvelous.server.game.pipelining;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.events.Event;
@@ -7,6 +7,7 @@ import uulm.teamname.marvelous.gamelibrary.events.EventType;
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
+import uulm.teamname.marvelous.server.game.GameSession;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -16,35 +17,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class PauseSegment implements Segment {
- private boolean paused;
+ private final GameSession parent;
- public PauseSegment() {
- paused = false;
+ public PauseSegment(GameSession parent) {
+ this.parent = parent;
}
- /** This method pauses the game if it is not already paused.*/
- public void pauseGame() {
- if (!paused) {
- paused = true;
- Logger.debug("Game paused.");
- }
- }
-
- /**
- * This method ends a paused game.
- */
- public void pauseEnd() {
- if (paused) {
- paused = false;
- Logger.debug("Game unpaused.");
- }
- }
-
- public boolean isPaused() {
- return paused;
- }
-
-
/**
* Pipelining method to process a set of requests. The list of requests will be processed according to the following
* rules:
@@ -68,47 +46,46 @@ public class PauseSegment implements Segment {
public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
Logger.trace("PauseSegment received {} requests. PausedState is {}",
packet.size(),
- paused);
+ parent.getPaused());
+
// check if there is a pause request (either start or stop)
- if (packet.contains(new RequestBuilder(RequestType.PauseStartRequest).buildGameRequest())) {
+ if(packet.contains(new RequestBuilder(RequestType.PauseStartRequest).buildGameRequest())) {
Logger.trace("PauseStartRequest found");
- if(packet.getOrigin().type == ParticipantType.Spectator || packet.getOrigin().isAI) {
+ if(packet.getOrigin().getType() == ParticipantType.Spectator || packet.getOrigin().isAI()) {
Logger.trace("Invalid pause start request. Aborting");
abort.set(true);
return;
}
- if (!paused) {
- // pause the game
- pauseGame();
+ if(!parent.getPaused()) {
+ parent.setPaused(true);
// create a new PauseStartEvent
carrier.add(new EventBuilder(EventType.PauseStartEvent).buildGameEvent());
Logger.trace("Added PauseStartEvent to pipeline carrier");
- } else { // if the game is already paused
+ }else { // if the game is already paused
Logger.info("PauseStartRequest sent even though the game wasn't paused. Error triggered.");
abort.set(true);
return;
}
- } else if (packet.contains(new RequestBuilder(RequestType.PauseStopRequest).buildGameRequest())) {
+ }else if(packet.contains(new RequestBuilder(RequestType.PauseStopRequest).buildGameRequest())) {
Logger.trace("PauseStopRequest found");
- if(packet.getOrigin().type == ParticipantType.Spectator || packet.getOrigin().isAI) {
+ if(packet.getOrigin().getType() == ParticipantType.Spectator || packet.getOrigin().isAI()) {
Logger.trace("Invalid pause stop request. Aborting");
abort.set(true);
return;
}
- if (paused) {
- pauseEnd();
- Logger.debug("Game unpaused.");
+ if(parent.getPaused()) {
+ parent.setPaused(false);
// create a new PauseStartRequest
carrier.add(new EventBuilder(EventType.PauseStopEvent).buildGameEvent());
Logger.trace("Added PauseStopEvent to pipeline carrier");
- } else { // if the game is not paused
+ }else { // if the game is not paused
Logger.info("PauseStopRequest sent even though the game wasn't paused. Error triggered.");
abort.set(true);
return;
}
}
- if (paused) {
+ if(parent.getPaused()) {
Logger.trace("As the game is paused, Requests are removed.");
packet.removeRequestsOfTypes(
RequestType.MeleeAttackRequest,
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Pipeline.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Pipeline.java
similarity index 94%
rename from Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Pipeline.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Pipeline.java
index 6b7c518..e30690d 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Pipeline.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Pipeline.java
@@ -1,9 +1,9 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
+package uulm.teamname.marvelous.server.game.pipelining;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
+import uulm.teamname.marvelous.server.net.Client;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -37,7 +37,7 @@ public class Pipeline {
* first check whether the {@link Optional} is empty by doing {@link Optional#isEmpty()} or {@link
* Optional#isPresent()}, and act accordingly.
*/
- public Optional> processRequests(Request[] requests, Participant origin) {
+ public Optional> processRequests(Client origin, Request[] requests) {
Logger.trace("Pipeline started RequestProcessing");
// The packet carries the requests, and gets smaller per segment
Packet packet = new Packet(requests, origin);
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/PlayerFilterSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/PlayerFilterSegment.java
similarity index 70%
rename from Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/PlayerFilterSegment.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/PlayerFilterSegment.java
index 801f661..5911438 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/PlayerFilterSegment.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/PlayerFilterSegment.java
@@ -1,11 +1,11 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
+package uulm.teamname.marvelous.server.game.pipelining;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.requests.CharacterRequest;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
+import uulm.teamname.marvelous.server.net.Client;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -14,8 +14,9 @@ public class PlayerFilterSegment implements Segment {
@Override
public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
Logger.trace("PlayerFilterSegment received {} requests", packet.size());
- for (Request request: packet) {
- boolean valid = switch (request.type) {
+
+ for(Request request: packet) {
+ boolean valid = switch(request.type) {
case MeleeAttackRequest,
RangedAttackRequest,
MoveRequest,
@@ -23,7 +24,7 @@ public class PlayerFilterSegment implements Segment {
UseInfinityStoneRequest -> isValid((CharacterRequest) request, packet.getOrigin());
default -> true;
};
- if (!valid) {
+ if(!valid) {
Logger.debug("Invalid request of type {} of enemy player found, setting abort flag", request.type);
abort.set(true);
break;
@@ -31,21 +32,17 @@ public class PlayerFilterSegment implements Segment {
}
}
- private boolean isValid(CharacterRequest request, Participant participant) {
- EntityType type = switch(participant.type) {
+ private boolean isValid(CharacterRequest request, Client client) {
+ EntityType type = switch(client.getType()) {
case PlayerOne -> EntityType.P1;
case PlayerTwo -> EntityType.P2;
case Spectator -> null;
};
- if (type == null) {
- Logger.warn("Some spectator-sent movement requests arrived in the PlayerFilterSegment.\n" +
- "Have you ordered your segments properly?");
+ if(type == null) {
return false;
- } else {
+ }else {
return request.originEntity.type.equals(type);
}
-
-
}
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/RequestGameStateSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/RequestGameStateSegment.java
similarity index 56%
rename from Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/RequestGameStateSegment.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/RequestGameStateSegment.java
index ec09d1b..ce9a87c 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/RequestGameStateSegment.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/RequestGameStateSegment.java
@@ -1,35 +1,36 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
+package uulm.teamname.marvelous.server.game.pipelining;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance;
import uulm.teamname.marvelous.gamelibrary.messages.server.EventMessage;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
+import uulm.teamname.marvelous.server.game.GameSession;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * The {@link RequestGameStateSegment} handles requests of {@link RequestType} Req. Therefore it sends the active
- * gamestate and clears the {@link Packet} afterwards.
+ * The {@link RequestGameStateSegment} handles requests of {@link RequestType} Req.
*/
public class RequestGameStateSegment implements Segment {
- private final GameInstance game;
+ private final GameSession parent;
- public RequestGameStateSegment(GameInstance game) {
- this.game = game;
+ public RequestGameStateSegment(GameSession parent) {
+ this.parent = parent;
}
@Override
public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
Logger.trace("RequestGameStateSegment received {} requests", packet.size());
- if (packet.containsRequestOfType(RequestType.Req)) {
- Logger.trace("Req event found. Returning Gamestate, and clearing entire RequestList");
- var gamestateEventMessage = new EventMessage();
- gamestateEventMessage.messages = new Event[] {game.getGameStateEvent()};
- packet.getOrigin().sendMessage(gamestateEventMessage);
+
+ if(packet.containsRequestOfType(RequestType.Req)) {
+ Logger.trace("Req event found. Returning GameState, and clearing entire RequestList");
+ EventMessage message = new EventMessage();
+ message.messages = new Event[] {parent.getInstance().getGameStateEvent()};
+ packet.getOrigin().sendMessage(message);
carrier.clear();
packet.clear();
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Segment.java b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Segment.java
similarity index 90%
rename from Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Segment.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Segment.java
index fc42dce..4b695e2 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Segment.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/game/pipelining/Segment.java
@@ -1,4 +1,4 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
+package uulm.teamname.marvelous.server.game.pipelining;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
@@ -21,5 +21,5 @@ public interface Segment {
* segment, but instead an error in the events passed to it, like for example moving into a Rock. The
* conventional way of setting this boolean is to write {@code abort.set(true); return;}
*/
- public void processRequests(Packet packet, List carrier, AtomicBoolean abort);
+ void processRequests(Packet packet, List carrier, AtomicBoolean abort);
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/LifetimeTimer.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/LifetimeTimer.java
deleted file mode 100644
index c6d2317..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/LifetimeTimer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package uulm.teamname.marvelous.server.lobby;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A timer meant to time the entire Lifetime of the lobby, and if it is higher than the maximum permitted value,
- * call a callback given by the lobby to compute a winner, and quit the lobby.
- */
-public class LifetimeTimer {
-
- private final ScheduledExecutorService timer;
- private final int maxGameTime;
- private final Runnable callback;
-
- LifetimeTimer(int maxGameTime, Runnable callback) {
- String lobbyThreadName = Thread.currentThread().getName();
- ThreadFactory threadFactory = r -> new Thread(r, lobbyThreadName + "-LifetimeTimerThread");
- this.timer = Executors.newSingleThreadScheduledExecutor(threadFactory);
-
- this.maxGameTime = maxGameTime;
- this.callback = callback;
- }
-
- void startTimer() {
- timer.schedule(callback, maxGameTime, TimeUnit.SECONDS);
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/Lobby.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/Lobby.java
deleted file mode 100644
index 6565c3e..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/Lobby.java
+++ /dev/null
@@ -1,364 +0,0 @@
-package uulm.teamname.marvelous.server.lobby;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
-import uulm.teamname.marvelous.gamelibrary.events.Event;
-import uulm.teamname.marvelous.gamelibrary.events.EventBuilder;
-import uulm.teamname.marvelous.gamelibrary.events.EventType;
-import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.config.CharacterConfig;
-import uulm.teamname.marvelous.gamelibrary.config.PartyConfig;
-import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.server.lobby.pipelining.*;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyConnection;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyManager;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.util.*;
-
-public class Lobby {
- private final String gameID;
- private final LobbyConnection connection;
- private final GameInstance game;
- private final Pipeline pipeline;
- private Participant activePlayer;
- private int badRequests;
- private PauseSegment pauseSegment;
- private final TurnTimeoutTimer turnTimeoutTimer;
- private final TimeoutTimer timeoutTimer;
- private final LifetimeTimer lifetimeTimer;
-
- /**
- * The {@link Lobby} is where the magic happens. In this class is a whole {@link GameInstance game} is processed. To
- * initialize the game it gets the following parameters
- *
- * @param gameID a String to identify the game
- * @param connection the Connection to the {@link LobbyManager}
- * @param partyConfig declared in Editor
- * @param characterConfig declared in Editor
- * @param scenarioConfig declared in Editor
- */
- public Lobby(
- String gameID,
- LobbyConnection connection,
- PartyConfig partyConfig,
- CharacterConfig characterConfig,
- ScenarioConfig scenarioConfig,
- List player1Characters,
- List player2Characters
- ) {
- this.gameID = gameID;
- this.connection = connection;
-
- this.game = new GameInstance(
- partyConfig,
- characterConfig,
- scenarioConfig
- );
-
- this.pipeline = new Pipeline();
-
- var reqSegment = new RequestGameStateSegment(this.game);
- this.pauseSegment = new PauseSegment();
- var filterEndRoundRequestSegment = new FilterEndRoundRequestSegment(game.state::getActiveCharacter);
- var disconnectSegment = new DisconnectSegment(this);
- var playerFilterSegment = new PlayerFilterSegment();
- var gameLogicSegment = new GameLogicSegment(this.game);
-
- pipeline.addSegment(reqSegment)
- .addSegment(pauseSegment)
- .addSegment(filterEndRoundRequestSegment)
- .addSegment(disconnectSegment)
- .addSegment(playerFilterSegment)
- .addSegment(gameLogicSegment);
-
- Logger.trace("Instantiating timers...");
-
- this.turnTimeoutTimer = new TurnTimeoutTimer(partyConfig.maxRoundTime, this::turnTimeout);
- updateTurnTimer();
-
- this.timeoutTimer = new TimeoutTimer(partyConfig.maxResponseTime, this::soonTimeout, this::timeout);
- refreshTimeoutTimer(connection.getPlayer1());
- refreshTimeoutTimer(connection.getPlayer2());
-
- this.lifetimeTimer = new LifetimeTimer(
- partyConfig.maxGameTime,
- () -> triggerWin(getParticipantForEntityType(game.state.getCurrentOvertimeWinner()).get()));
-
- this.connection.broadcastEvents(this.game.startGame(player1Characters, player2Characters));
-
- }
-
- /**
- * Called by {@link LobbyConnection} to handle requests
- *
- * @param requests to be processed
- * @param source the player executing the requests
- */
- public synchronized void receiveRequests(Request[] requests, Participant source) {
- Logger.trace("Received {} requests from participant '{}' of type {}",
- requests.length,
- source.id.getName(),
- source.type);
-
- refreshTimeoutTimer(source);
-
- if (activePlayer != source && source.type != ParticipantType.Spectator) {
- Logger.trace("Resetting bad requests as new participant sent data");
- activePlayer = source;
- badRequests = 0;
- }
-
- Logger.info("got {} requests from participant {}",
- requests.length,
- source.id.getName());
-
- Logger.trace("Processing requests through pipeline");
- Optional> resultingEvents = pipeline.processRequests(requests, source);
- Logger.debug("generated {} events from the pipeline", resultingEvents.map(List::size).orElse(0));
-
- //resultingEvents isEmpty when a wrong request appeared
- Logger.trace("Checking whether resultingEvents (an optional) is empty");
- if (resultingEvents.isEmpty()) {
- Logger.debug("Rejecting requests from participant '{}'", source.id.getName());
- reject(source);
- } else {
- if (game.state.isWon()) { // If game was won in the current turn
- Logger.info("Game is won, terminating lobby");
- var events = resultingEvents.get();
- events.add(new EventBuilder(EventType.DisconnectEvent).buildGameEvent());
- accept(source, events);
- connection.terminate();
- return;
- } else { // If not (normally)
- accept(source, resultingEvents.get());
- }
- }
- updateTurnTimer();
- }
-
- /**
- * Called by {@link LobbyConnection} when a client disconnects
- *
- * @param source the player disconnecting
- */
- public synchronized void handleDisconnect(Participant source) {
-
- }
-
- /**
- * Called by {@link LobbyConnection} when a client reconnects
- *
- * @param source the player reconnecting
- */
- public synchronized void handleReconnect(Participant source) {
-
- }
-
- /**
- * This method is called at the end of receiveRequests, to start a timer. The active player has now a specific
- * amount of time to do his moves.
- */
- void updateTurnTimer() {
- var currentlyActiveParticipant =
- getParticipantForEntityType(game.state.getActiveCharacter());
- Logger.trace("Updating turnTimer...");
- if (pauseSegment.isPaused()) {
- Logger.trace("Game is paused, clearing turnTimer");
- turnTimeoutTimer.clear();
-
- } else if (currentlyActiveParticipant.isPresent()) {
- var participant = currentlyActiveParticipant.get();
- Logger.trace("Scheduling turnTimer for Player1");
- turnTimeoutTimer.startTurnTimer(participant);
-
- } else {
- Logger.trace("Currently active participant was NPC, clearing TurnTimer");
- turnTimeoutTimer.clear();
- }
- }
-
- /**
- * Returns an {@link Optional} of the {@link Participant} for the given {@link EntityType}, or an empty {@link
- * Optional} if it was an NPC
- */
- Optional getParticipantForEntityType(EntityID entityID) {
- if (entityID == null) {
- Logger.trace("cannot get participant for empty EntityType, returning empty Optional");
- return Optional.empty();
- } else {
- return getParticipantForEntityType(entityID.type);
- }
- }
-
- Optional getParticipantForEntityType(EntityType type) {
- if (type == EntityType.P1) {
- return Optional.ofNullable(connection.getPlayer1());
- } else if (type == EntityType.P2) {
- return Optional.ofNullable(connection.getPlayer2());
- } else {
- return Optional.empty();
- }
- }
-
- /** Method meant for updating a TurnTimer. Said TurnTimer will be refreshed with the given participant. */
- void refreshTimeoutTimer(Participant participant) {
- if (participant.type == ParticipantType.Spectator) {
- Logger.trace("Tried to refresh timeoutTimer for Spectator, ignoring...");
- } else {
- Logger.debug("Refreshing timeoutTimer for Participant '{}'", participant.id.getName());
- timeoutTimer.refreshTimer(participant);
- }
-
- }
-
- private void accept(Participant source, List accepted) {
- if (!accepted.isEmpty()) {
- Logger.debug("Accepting requests from participant '{}', broadcasting events to all except source",
- source.id.getName());
- accepted.add(game.getGameStateEvent());
- connection.broadcastToAllExcept(source, accepted.toArray(new Event[0]));
-
- Logger.trace("Adding ack and sending back to originParticipant");
- accepted.add(0, new EventBuilder(EventType.Ack).buildGameEvent());
- connection.sendEvents(source, accepted.toArray(new Event[0]));
- }
- badRequests = 0;
- }
-
- /**
- * If the player executed a false request the request gets rejected.
- *
- * @param source the executing player
- */
- private void reject(Participant source) {
- connection.sendEvents(source, new EventBuilder(EventType.Nack).buildGameEvent(), game.getGameStateEvent());
- badRequests++;
- //if the player sends 2 bad messages after one another, the player gets kicked out of the lobby.
- if (badRequests >= 5) {
- Logger.info("Participant '{}' has sent too many bad requests, disconnecting...", source.id.getName());
- connection.removeParticipant(source);
- if (connection.hasPlayer1()) {
- Logger.debug("Triggering win for Player 1");
- triggerWin(connection.getPlayer1());
- } else if (connection.hasPlayer2()) {
- Logger.debug("Triggering win for Player 2");
- triggerWin(connection.getPlayer2());
- }
- }
- }
-
- /**
- * Warns the player he get timeouted soon.
- *
- * @param source soon to be timeouted player
- */
- public synchronized void soonTimeout(Participant source, int timeLeft) {
- connection.sendEvents(
- source,
- new EventBuilder(EventType.TimeoutWarningEvent)
- .withTimeLeft(timeLeft)
- .withMessage("If you do not send a message soon, you will be time-outed")
- .buildGameEvent());
- }
-
- /**
- * If a player times out the other player automatically wins.
- *
- * @param source is the timeouted player
- */
- public synchronized void timeout(Participant source) {
- connection.sendEvents(
- source,
- new EventBuilder(EventType.TimeoutEvent).buildGameEvent());
- connection.removeParticipant(source);
-
- if (connection.hasPlayer1() && !connection.hasPlayer2()) {
- triggerWin(connection.getPlayer1());
- } else if (!connection.hasPlayer1() && connection.hasPlayer2()) {
- triggerWin(connection.getPlayer2());
- } else {
- throw new IllegalStateException("Spectator was time-outed which is impossible");
- }
- }
-
- /** Skips the current turn, and starts a new one. Exclusively called in the {@link TurnTimeoutTimer}. */
- private synchronized void turnTimeout(Participant source) {
- var nextTurnEvents = game.endTurn();
- nextTurnEvents.add(game.getGameStateEvent());
- connection.broadcastEvents(nextTurnEvents.toArray(new Event[0]));
- updateTurnTimer();
- }
-
- /**
- * The method triggers a winEvent for a {@link Participant}, and broadcasts said event. Afterwards it sends a
- * DisconnectRequest to everyone, and terminates the connection.
- *
- * @param winner is the {@link Participant} that won
- */
- public synchronized void triggerWin(Participant winner) {
- Logger.debug("Triggering win. Building events and broadcasting for winner of type '{}'", winner);
- connection.broadcastEvents(
- new EventBuilder(EventType.WinEvent)
- .withPlayerWon(winner.type.equals(ParticipantType.PlayerOne) ? 1 : 2)
- .buildGameEvent(),
- new EventBuilder(EventType.DisconnectEvent)
- .buildGameEvent());
- connection.terminate();
- }
-
- public PauseSegment getPauseSegment() {
- return pauseSegment;
- }
-
- public String getGameID() {
- return gameID;
- }
-
- public LobbyConnection getConnection() {
- return connection;
- }
-
- public GameInstance getGame() {
- return game;
- }
-
- public Pipeline getPipeline() {
- return pipeline;
- }
-
- public Participant getActivePlayer() {
- return activePlayer;
- }
-
- // Note: DO NOT ADD the connection to the equals and hashcode here, otherwise they recursively call each other
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Lobby lobby = (Lobby) o;
- return badRequests == lobby.badRequests && Objects.equals(gameID, lobby.gameID) && Objects.equals(game, lobby.game) && Objects.equals(pipeline, lobby.pipeline) && Objects.equals(activePlayer, lobby.activePlayer) && Objects.equals(pauseSegment, lobby.pauseSegment) && Objects.equals(turnTimeoutTimer, lobby.turnTimeoutTimer);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(gameID, game, pipeline, activePlayer, badRequests, pauseSegment, turnTimeoutTimer);
- }
-
- @Override
- public String toString() {
- return "Lobby{" +
- "gameID='" + gameID + '\'' +
- ", game=" + game +
- ", pipeline=" + pipeline +
- ", activePlayer=" + activePlayer +
- ", badRequests=" + badRequests +
- ", pauseSegment=" + pauseSegment +
- ", turnTimer=" + turnTimeoutTimer +
- '}';
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TimeoutTimer.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TimeoutTimer.java
deleted file mode 100644
index 96e62b9..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TimeoutTimer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package uulm.teamname.marvelous.server.lobby;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.util.concurrent.*;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-public class TimeoutTimer {
-
- private final ScheduledExecutorService timer;
-
- private ScheduledFuture player1AlmostTimeout;
- private ScheduledFuture player1Timeout;
- private ScheduledFuture player2AlmostTimeout;
- private ScheduledFuture player2Timeout;
-
- private final BiConsumer almostTimeoutCallback;
- private final Consumer timeoutCallback;
-
- private final int almostTimeoutTime;
- private final int timeoutTime;
-
- /**
- * Class that manages timeouts of players after not sending a message for a long time.
- *
- * @param timeoutTime is the time that a player has to send any message
- * @param almostTimeoutCallback is the callback that is called if the timeoutTime is almost over
- * @param timeoutCallback is the callback that is called when the timeoutTime is over
- */
- public TimeoutTimer(int timeoutTime,
- BiConsumer almostTimeoutCallback,
- Consumer timeoutCallback) {
- this.almostTimeoutCallback = almostTimeoutCallback;
- this.timeoutCallback = timeoutCallback;
-
- this.timeoutTime = timeoutTime;
- this.almostTimeoutTime = Math.max(timeoutTime - 15, Math.min(timeoutTime, 5));
-
- String lobbyThreadName = Thread.currentThread().getName();
- ThreadFactory threadFactory = r -> new Thread(r, lobbyThreadName + "-timeoutTimer");
- timer = Executors.newSingleThreadScheduledExecutor(threadFactory);
- }
-
- /** Refreshes the timeout timer for the given Participant. This is meant to be used for Players, not Spectators. */
- public void refreshTimer(Participant participant) {
- Logger.debug("Refreshing turnTimer for participant of type '{}'", participant.type);
- switch (participant.type) {
- case PlayerOne -> {
- Logger.trace("Was playerOne, refreshing...");
- if (player1AlmostTimeout != null) player1AlmostTimeout.cancel(false);
- player1AlmostTimeout =
- timer.schedule(() -> {
- almostTimeoutCallback.accept(participant, timeoutTime - almostTimeoutTime);
- return participant;
- }, this.almostTimeoutTime, TimeUnit.SECONDS);
-
- if (player1Timeout != null) player1Timeout.cancel(false);
- player1Timeout =
- timer.schedule(() -> {
- timeoutCallback.accept(participant);
- return participant;
- }, this.timeoutTime, TimeUnit.SECONDS);
- }
- case PlayerTwo -> {
- Logger.trace("Was playerOne, refreshing...");
- if (player2AlmostTimeout != null) player2AlmostTimeout.cancel(false);
- player2AlmostTimeout =
- timer.schedule(() -> {
- almostTimeoutCallback.accept(participant, timeoutTime - almostTimeoutTime);
- return participant;
- }, this.almostTimeoutTime, TimeUnit.SECONDS);
-
- if (player2Timeout != null) player2Timeout.cancel(false);
- player2Timeout =
- timer.schedule(() -> {
- timeoutCallback.accept(participant);
- return participant;
- }, this.timeoutTime, TimeUnit.SECONDS);
- }
- case Spectator -> Logger.warn("Timeout called on spectator '{}'. This is probably a bug.",
- participant.id.getName());
- }
- }
-}
-
-
-
-
-
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TurnTimeoutTimer.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TurnTimeoutTimer.java
deleted file mode 100644
index 39f6f3e..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TurnTimeoutTimer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package uulm.teamname.marvelous.server.lobby;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.util.concurrent.*;
-import java.util.function.Consumer;
-
-/**
- * The {@link TurnTimeoutTimer} class is called by the {@link Lobby} to limit the amount of time a player has per round.
- */
-public class TurnTimeoutTimer {
- private final ScheduledExecutorService timer;
- private final Consumer callback;
- private final int maxRoundTime;
-
- private ScheduledFuture current;
-
- public TurnTimeoutTimer(int maxRoundTime, Consumer callback) {
- String lobbyThreadName = Thread.currentThread().getName();
- ThreadFactory threadFactory = r -> new Thread(r, lobbyThreadName + "-TurnTimerThread");
- this.timer = Executors.newSingleThreadScheduledExecutor(threadFactory);
- this.maxRoundTime = maxRoundTime;
- this.callback = callback;
- }
-
- /**
- * This method checks if the participant is not a spectator. Otherwise it won't start a timer.
- *
- * @param participant the timer is for
- */
- public void startTurnTimer(Participant participant) {
- if (participant.type == ParticipantType.Spectator) {
- throw new IllegalStateException("Spectators don't have TurnTime");
- }
-
- clear();
- Logger.debug("Starting turn timer for participant '{}' with role {}",
- participant.id.getName(), participant.type);
- current = timer.schedule(() -> {
- callback.accept(participant);
- return participant;
- },
- maxRoundTime,
- TimeUnit.SECONDS);
- }
-
- /** cancels all currently running timers */
- public void clear() {
- Logger.trace("Clearing timer");
- if (this.current != null) {
- current.cancel(true);
- }
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/DisconnectSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/DisconnectSegment.java
deleted file mode 100644
index 483f63f..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/DisconnectSegment.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.gamelibrary.events.Event;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobby.Lobby;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * The {@link DisconnectSegment} handles requests of {@link RequestType} DisconnectRequest. Therefore it removes the
- * disconnecting player-connection from the {@link Lobby}.
- */
-public class DisconnectSegment implements Segment {
-
- private final Lobby parent;
-
- public DisconnectSegment(Lobby parent) {
- this.parent = parent;
- }
-
- @Override
- public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
- Logger.trace("DisconnectSegment received {} requests.", packet.size());
- if (packet.containsRequestOfType(RequestType.DisconnectRequest)) {
- Logger.debug("Player of Type {} sent DisconnectRequest", packet.getOrigin().type);
- parent.getConnection().removeParticipant(packet.getOrigin());
-
- if (packet.getOrigin().type != ParticipantType.Spectator) {
- if (parent.getConnection().hasPlayer1()) {
- parent.triggerWin(parent.getConnection().getPlayer1());
- } else if (parent.getConnection().hasPlayer2()) {
- parent.triggerWin(parent.getConnection().getPlayer2());
- }
- }
- packet.clear();
- }
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegment.java
deleted file mode 100644
index 4d0d444..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegment.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
-import uulm.teamname.marvelous.gamelibrary.events.Event;
-import uulm.teamname.marvelous.gamelibrary.gamelogic.GameStateView;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-import uulm.teamname.marvelous.server.lobby.Lobby;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Supplier;
-
-/**
- * {@link Segment} that checks for an {@link RequestType#EndRoundRequest}, and
- * if it exists, checks for the player that has sent it. If the player sending
- * this request is not the active player, it flags the message as an error.
- */
-public class FilterEndRoundRequestSegment implements Segment {
-
- private final Supplier getActiveCharacterCallback;
-
- /**
- * Creates a new {@link FilterEndRoundRequestSegment}
- * @param getActiveCharacterCallback is a {@link Supplier}<{@link Participant}>
- * that supplies the currently active participant
- * in a {@link Lobby}. It should normally be bound
- * to {@link GameStateView#getActiveCharacter()}, by executing
- * {@code new Filter...Segment(game.state::getActiveCharacter)}.
- */
- public FilterEndRoundRequestSegment(Supplier getActiveCharacterCallback) {
- this.getActiveCharacterCallback = getActiveCharacterCallback;
-
- }
-
- @Override
- public void processRequests(Packet packet, List carrier, AtomicBoolean abort) {
- Logger.trace("FilterEndRoundSegment has received {} requests", packet.size());
- if (packet.containsRequestOfType(RequestType.EndRoundRequest)) {
- Logger.trace("Packet contains EndRoundRequest");
-
- var active = getActiveCharacterCallback.get().type;
- var from = packet.getOrigin().type == ParticipantType.PlayerOne ? EntityType.P1 : EntityType.P2;
-
- if (packet.containsRequestOfType(RequestType.EndRoundRequest) && active != from) {
- Logger.trace("Invalid endRoundRequest. Expected {} but got {}. Aborting...", active, from);
- abort.set(true);
- }
- }
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyConnection.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyConnection.java
deleted file mode 100644
index 3ef64a8..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyConnection.java
+++ /dev/null
@@ -1,326 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.gamelibrary.ArrayTools;
-import uulm.teamname.marvelous.gamelibrary.Tuple;
-import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
-import uulm.teamname.marvelous.gamelibrary.events.Event;
-import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.messages.server.*;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.server.Server;
-import uulm.teamname.marvelous.server.lobby.Lobby;
-import uulm.teamname.marvelous.server.netconnector.ClientState;
-import uulm.teamname.marvelous.server.netconnector.SUID;
-import uulm.teamname.marvelous.server.netconnector.UserManager;
-
-import java.util.*;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class LobbyConnection implements Runnable {
- private static boolean synchronous = false;
-
- public final String gameID;
- public LobbyConnectionState state = LobbyConnectionState.Waiting;
-
- private Participant player1;
- private Participant player2;
-
- private final Set spectators = new HashSet<>(10);
- private final Map> selection = new HashMap<>(2);
- public final Map options = new HashMap<>(2);
-
- private final BlockingQueue> requestQueue = new LinkedBlockingQueue<>();
-
- private Lobby lobby;
-
- /** Creates a new LobbyConnection */
- public LobbyConnection(String gameID) {
- this.gameID = gameID;
-
- Tuple picked = Server.getCharacterConfig().getDisjointSetsOfPropertiesOfSize(12);
- this.options.put(ParticipantType.PlayerOne, picked.item1);
- this.options.put(ParticipantType.PlayerTwo, picked.item2);
- }
-
-
- public boolean setSelection(Participant participant, Integer[] selection) {
- this.selection.put(participant.id, ArrayTools.toArrayList(selection));
- return this.selection.size() == 2;
- }
-
- public void addParticipant(Participant participant) {
- if (this.state == LobbyConnectionState.Started) {
- Logger.trace("Set client state to playing");
- participant.getClient().setState(ClientState.Playing);
- participant.state = ParticipantState.Playing;
- participant.sendMessage(generateGameStructure(participant.type));
- }
- if (participant.type == ParticipantType.Spectator) {
- Logger.trace("Adding spectator");
- spectators.add(participant);
- } else if (participant.type == ParticipantType.PlayerOne) {
- player1 = participant;
- } else {
- player2 = participant;
- }
- }
-
- /** Disconnects a participant from the LobbyConnection */
- public void removeParticipant(Participant participant) {
- removeParticipant(participant, "You have been disconnected.");
- }
-
- /** Disconnects a participant from the LobbyConnection */
- public void removeParticipant(Participant participant, String reason) {
- if (participant != null) {
- Logger.debug("Removing participant '{}' with role {} from lobby",
- participant.id.getName(), participant.type);
- LobbyManager.getInstance().removeParticipant(participant);
- UserManager.getInstance().removeClient(participant.getClient(), reason);
-
- if (participant.type == ParticipantType.Spectator) {
- spectators.remove(participant);
- } else if (participant.type == ParticipantType.PlayerOne) {
- player1 = null;
- } else {
- player2 = null;
- }
- }
- }
-
- /** Returns the next free slot in the lobby as a {@link ParticipantType} */
- public ParticipantType freeSlot() {
- if (player1 == null) {
- return ParticipantType.PlayerOne;
- } else if (player2 == null) {
- return ParticipantType.PlayerTwo;
- } else {
- return ParticipantType.Spectator;
- }
- }
-
- /** Returns whether there is a player slot available in the lobby */
- public boolean hasFreePlayerSlot() {
- return player1 == null || player2 == null;
- }
-
-
- public Participant getPlayer1() {
- return player1;
- }
-
- public Participant getPlayer2() {
- return player2;
- }
-
- public Set getSpectators() {
- return spectators;
- }
-
- public boolean hasPlayer1() {
- return player1 != null;
- }
-
- public boolean hasPlayer2() {
- return player2 != null;
- }
-
-
- public void handleMessage(Participant participant, Request[] requests) {
- if(synchronous) {
- lobby.receiveRequests(requests, participant);
- return;
- }
-
- try {
- this.requestQueue.put(Tuple.of(participant, requests));
- } catch (InterruptedException ignored) {
- }
- }
-
- /** Handles disconnect of a Participant. Hereby, the participant is made ready for reconnection */
- public void handleDisconnect(Participant participant) {
- participant.disconnected = true;
- if (state == LobbyConnectionState.Started) {
- lobby.handleDisconnect(participant);
- }
- }
-
- /** Handles reconnect of a Participant. Hereby, the participant is made ready for reconnection */
- public void handleReconnect(Participant participant) {
- participant.disconnected = false;
- if (state == LobbyConnectionState.Started) {
-
- GeneralAssignmentMessage response = new GeneralAssignmentMessage();
- response.gameID = gameID;
- participant.sendMessage(response);
-
- participant.sendMessage(generateGameStructure(participant.type));
-
- lobby.handleReconnect(participant);
- }
- }
-
-
- public void runSynchronous() {
- synchronous = true;
- run();
- }
-
- @Override
- public void run() {
- state = LobbyConnectionState.Started;
-
- player1.state = ParticipantState.Playing;
- player2.state = ParticipantState.Playing;
- for (Participant spectator : spectators) {
- spectator.state = ParticipantState.Playing;
- }
-
- if(!synchronous) {
- Logger.info("Starting Lobby thread for lobby '{}'", gameID);
- }else {
- Logger.info("Starting Lobby in main thread for lobby '{}'", gameID);
- }
-
- broadcastGameStructure();
-
- this.lobby = new Lobby(
- gameID,
- this,
- Server.getPartyConfig(),
- Server.getCharacterConfig(),
- Server.getScenarioConfig(),
- selection.get(player1.id),
- selection.get(player2.id)
- );
-
- if(synchronous) {
- return;
- }
-
- while (state == LobbyConnectionState.Started) {
- Tuple currentRequests = pollQueueAsync();
-
- if (currentRequests != null) {
- lobby.receiveRequests(currentRequests.item2, currentRequests.item1);
- }
- }
- }
-
- public void terminate() {
- LobbyRunner.getInstance().removeLobby(this);
- LobbyManager.getInstance().removeLobby(gameID);
- state = LobbyConnectionState.Aborted;
- removeParticipant(player1);
- removeParticipant(player2);
- spectators.forEach(this::removeParticipant);
- }
-
-
- private void broadcastGameStructure() {
- // Sending GameStructure message with fitting assignment
- player1.sendMessage(generateGameStructure(ParticipantType.PlayerOne));
- player2.sendMessage(generateGameStructure(ParticipantType.PlayerTwo));
- broadcastToSpectators(generateGameStructure(ParticipantType.Spectator));
- }
-
- private GameStructureMessage generateGameStructure(ParticipantType assignment) {
- GameStructureMessage gameStructureMessage = new GameStructureMessage();
- gameStructureMessage.playerOneName = player1.id.getName();
- gameStructureMessage.playerTwoName = player2.id.getName();
-
- gameStructureMessage.playerOneCharacters = new CharacterProperties[6];
- gameStructureMessage.playerTwoCharacters = new CharacterProperties[6];
- int i = 0;
- for (Integer id : selection.get(player1.id)) {
- gameStructureMessage.playerOneCharacters[i++] = Server.getCharacterConfig().getIDMap().get(id);
- }
- i = 0;
- for (Integer id : selection.get(player2.id)) {
- gameStructureMessage.playerTwoCharacters[i++] = Server.getCharacterConfig().getIDMap().get(id);
- }
-
- gameStructureMessage.matchconfig = Server.getPartyConfig();
- gameStructureMessage.scenarioconfig = Server.getScenarioConfig();
-
- gameStructureMessage.assignment = assignment;
-
- return gameStructureMessage;
- }
-
- private Tuple pollQueueAsync() {
- Tuple current = null;
- try {
- current = requestQueue.poll(1000, TimeUnit.MILLISECONDS);
- } catch (InterruptedException ignored) {
- }
- return current;
- }
-
-
- private void broadcast(BasicMessage message) {
- Logger.trace("Broadcasting message of type {} to all members of lobby", message.messageType);
- if (player1 != null) player1.sendMessage(message);
- if (player2 != null) player2.sendMessage(message);
- spectators.forEach(spectator -> spectator.sendMessage(message));
- }
-
- private void broadcastToSpectators(BasicMessage message) {
- Logger.trace("Broadcasting message of type {} to all spectators", message.messageType);
- spectators.forEach(spectator -> spectator.sendMessage(message));
- }
-
- private void broadcastToAllExcept(Participant except, BasicMessage message) {
- Logger.trace("Sending message of type {} to all except participant with role {}",
- message.messageType, except == null ? "NONE" : except.type);
- if (except != null) {
- if (!except.equals(player1)) player1.sendMessage(message);
- if (!except.equals(player2)) player2.sendMessage(message);
- for (Participant spectator : spectators) {
- if (!except.equals(spectator)) {
- spectator.sendMessage(message);
- }
- }
- } else {
- broadcast(message);
- }
- }
-
-
- public void sendEvents(Participant recipient, Event... events) {
- Logger.trace("Sending {} events to participant with role {}",
- events.length, recipient == null ? "NONE" : recipient.type);
- if (recipient != null) {
- EventMessage message = new EventMessage();
- message.messages = events;
-
- recipient.sendMessage(message);
- }
- }
-
- public void broadcastEvents(List events) {
- broadcastEvents(events.toArray(new Event[0]));
- }
-
- public void broadcastEvents(Event... events) {
- Logger.trace("Broadcasting {} events", events.length);
- EventMessage message = new EventMessage();
- message.messages = events;
-
- broadcast(message);
- }
-
- public void broadcastToAllExcept(Participant except, Event... events) {
- Logger.trace("Broadcasting {} events to all except participant with role {}",
- events.length, except == null ? "NONE" : except.type);
- var message = new EventMessage();
- message.messages = events;
-
- broadcastToAllExcept(except, message);
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyConnectionState.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyConnectionState.java
deleted file mode 100644
index e4ce75c..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyConnectionState.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-public enum LobbyConnectionState {
- Waiting,
- Started,
- Aborted
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyManager.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyManager.java
deleted file mode 100644
index 824beea..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyManager.java
+++ /dev/null
@@ -1,273 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.messages.RoleEnum;
-import uulm.teamname.marvelous.gamelibrary.messages.client.CharacterSelectionMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.client.RequestMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.server.ConfirmSelectionMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.server.GameAssignmentMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.server.GeneralAssignmentMessage;
-import uulm.teamname.marvelous.server.netconnector.Client;
-import uulm.teamname.marvelous.server.netconnector.ClientState;
-import uulm.teamname.marvelous.server.netconnector.SUID;
-import uulm.teamname.marvelous.server.netconnector.UserManager;
-
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class LobbyManager {
- private static LobbyManager instance;
-
- /**
- * @return the current instance of the LobbyManager
- */
- public static LobbyManager getInstance() {
- if (instance == null) {
- instance = new LobbyManager();
- }
- return instance;
- }
-
- private final HashMap lobbies = new HashMap<>();
-
- private final HashMap participants = new HashMap<>();
-
-
- /**
- * Handles a newly connected Client, and returns whether the game is already running in the given {@link
- * AtomicBoolean}.
- */
- public boolean handleConnect(Client client, AtomicBoolean running) {
- Logger.debug("Connecting new client");
-
- Participant participant = participants.get(client.getId());
- if (participant != null) {
- LobbyConnection lobby = lobbies.get(participant.lobby);
- if (lobby != null) {
- running.set(lobby.state == LobbyConnectionState.Started);
- }
- }
-
- return true;
- }
-
- public boolean handleReady(Client client, PlayerReadyMessage message) {
- if (participants.containsKey(client.getId())) {
- return false;
- }
-
- addParticipant(client, client.socket.getResourceDescriptor(), message.role);
-
- return true;
- }
-
- public boolean handleReconnect(Client client) {
- if (!participants.containsKey(client.getId())) {
- return false;
- }
-
- Participant participant = participants.get(client.getId());
- participant.setClient(client);
-
- LobbyConnection lobby = lobbies.get(participant.lobby);
-
- if (lobby == null) {
- return false;
- }
-
- lobby.handleReconnect(participant);
-
- return true;
- }
-
- /**
- * Handles a {@link CharacterSelectionMessage}, computes the characters that have been selected and relays that
- * information to the {@link LobbyConnection} concerned by this information.
- *
- * @param client is the client that sent the message
- * @param message is the message sent by the client
- * @return true if handled successfully, and false otherwise
- */
- public boolean handleSelection(Client client, CharacterSelectionMessage message) {
- Logger.debug("Handling characterSelection...");
- if (!participants.containsKey(client.getId())) {
- Logger.trace("Participant didn't exist, returning...");
- return false;
- }
-
- Participant participant = participants.get(client.getId());
-
- if (participant.state != ParticipantState.Assigned) {
- Logger.trace("Participant wasn't assigned, exiting...");
- return false;
- } else if (participant.type == ParticipantType.Spectator) {
- Logger.trace("Spectator sent message, returning...");
- return false;
- }
-
- LobbyConnection lobby = lobbies.get(participant.lobby);
-
- if (lobby == null) {
- return false;
- }
-
- Integer[] selected = new Integer[6];
-
- CharacterProperties[] options = lobby.options.get(participant.type);
-
- int n = 0;
- for (int i = 0; i < 12; i++) {
- if (Boolean.TRUE.equals(message.characters[i])) {
- selected[n++] = options[i].characterID;
- }
- }
-
- if (n != 6) {
- return false;
- }
-
- participant.state = ParticipantState.Selected;
-
- boolean complete = lobby.setSelection(participant, selected);
-
- if (complete) {
- lobby.getPlayer1().getClient().setState(ClientState.Playing);
- lobby.getPlayer2().getClient().setState(ClientState.Playing);
- lobby.getSpectators().forEach(spectator -> spectator.getClient().setState(ClientState.Playing));
- LobbyRunner.getInstance().startLobby(lobby);
- } else {
- ConfirmSelectionMessage response = new ConfirmSelectionMessage();
- response.selectionComplete = false;
- participant.sendMessage(response);
- }
-
- return true;
- }
-
- /**
- * Handles a requestMessage, and relays it to the Lobby if valid
- *
- * @param client is the client that sent the message
- * @param message is the message sent by the client
- * @return true if handled successfully, and false otherwise
- */
- public boolean handleRequests(Client client, RequestMessage message) {
- if (!participants.containsKey(client.getId())) {
- return false;
- }
-
- Participant participant = participants.get(client.getId());
-
- if (participant.state != ParticipantState.Playing) {
- return false;
- }
-
- LobbyConnection lobby = lobbies.get(participant.lobby);
-
- if (lobby == null) {
- return false;
- }
-
- lobby.handleMessage(participant, message.messages);
-
- return true;
- }
-
- /**
- * Handles the disconnect of a WebSocket.
- */
- public void handleDisconnect(Client client, boolean byRemote) {
- Logger.trace("Handling disconnect of Client");
-
- if (!participants.containsKey(client.getId())) {
- return;
- }
-
- Participant participant = participants.get(client.getId());
- LobbyConnection lobby = lobbies.get(participant.lobby);
-
- if (lobby == null) {
- return;
- }
-
- if(lobby.state == LobbyConnectionState.Started) {
- lobby.handleDisconnect(participant);
-
- }else {
- Logger.debug("Deleting participant after leaving a non-started lobby");
- participants.remove(client.getId());
-
- if(lobby.hasFreePlayerSlot()) {
- Logger.debug("Destroying lobby after last player left");
- lobby.terminate();
- lobbies.remove(participant.lobby);
- }
- }
- }
-
-
- public void removeLobby(String lobbyID) {
- lobbies.remove(lobbyID);
- }
-
- /**
- * Adds a participant to a lobby. If the maximum amount of lobbies is already filled, or if the lobby requested
- * isn't free, the participant is disconnected.
- */
- private void addParticipant(Client client, String lobbyID, RoleEnum role) {
- Logger.trace("Adding participant '{}' to the lobby '{}'", client.getId(), lobbyID);
-
- if (!lobbies.containsKey(lobbyID)) {
- if (!LobbyRunner.getInstance().canAddLobby()) {
- Logger.info("Rejecting participant '{}' as server is already full", client.getId());
- UserManager.getInstance().removeClient(client, "The server is currently full. Please connect as a spectator instead.");
- return;
- }
- Logger.info("Lobby '{}' didn't exist yet, initializing", lobbyID);
- lobbies.put(lobbyID, new LobbyConnection(lobbyID));
- }
-
- LobbyConnection lobby = lobbies.get(lobbyID);
-
- if (!lobby.hasFreePlayerSlot() && role != RoleEnum.SPECTATOR) {
- Logger.debug("No free player slots available, disconnecting client '{}'", client.getId());
- UserManager.getInstance().removeClient(client, "The lobby your requested is already full. Please connect as a spectator instead.");
- return;
- }
-
- ParticipantType type = lobby.freeSlot();
-
- Logger.trace("New participant '{}' has the role '{}'", client.getId(), type);
-
- Participant participant = new Participant(client, lobbyID, type, role == RoleEnum.KI);
- participants.put(client.getId(), participant);
-
- lobby.addParticipant(participant);
-
- if (type != ParticipantType.Spectator) {
- Logger.debug("Sending GameAssignment message to user '{}'", client.getId());
- GameAssignmentMessage response = new GameAssignmentMessage();
- response.gameID = lobby.gameID;
- response.characterSelection = lobby.options.get(type);
- participant.sendMessage(response);
- } else {
- Logger.debug("Sending GeneralAssignment message to user '{}'", client.getId());
- GeneralAssignmentMessage response = new GeneralAssignmentMessage();
- response.gameID = lobby.gameID;
- participant.sendMessage(response);
- }
- }
-
- /**
- * Removes a participant from the game entirely. This is done when for example a player gets removed from the
- * Lobby because of a timeout.
- */
- public void removeParticipant(Participant participant) {
- if (participant != null) {
- participants.remove(participant.id);
- }
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyRunner.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyRunner.java
deleted file mode 100644
index 91f15c7..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyRunner.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.server.Server;
-
-import java.util.HashMap;
-
-/**
- * Class meant for running lobbies. It manages said lobbies, creates threads for it, and moves it into an executor
- */
-public class LobbyRunner {
- private static boolean synchronous = false;
-
- private static LobbyRunner instance;
-
- static LobbyRunner getInstance() {
- if (instance == null) {
- instance = new LobbyRunner();
- }
- return instance;
- }
-
- private final HashMap activeLobbies = new HashMap<>();
-
-
- boolean canAddLobby() {
- return activeLobbies.size() < Server.getMaxLobbies();
- }
-
- /** Starts a new thread for the current LobbyConnection, and adds it to the currently active lobbies */
- void startLobby(LobbyConnection connection) {
- Logger.trace("Starting lobby connection thread '{}'", connection.gameID);
- synchronized (activeLobbies) {
- if (activeLobbies.containsKey(connection)) {
- Logger.warn("Already active lobby was started again. This is probably a bug.");
- } else {
- if(!synchronous) {
- Logger.trace("Executing LobbyThread 'Lobby-{}'...", connection.gameID);
- Thread lobbyThread = new Thread(connection, "Lobby-" + connection.gameID);
- activeLobbies.put(connection, lobbyThread);
- lobbyThread.start();
- }else {
- Logger.trace("Executing Lobby 'Lobby-{}'...", connection.gameID);
- connection.runSynchronous();
- }
- }
- }
- }
-
- void removeLobby(LobbyConnection lobby) {
- synchronized (activeLobbies) {
- if (!activeLobbies.containsKey(lobby)) {
- Logger.warn("Tried to remove non-existent lobby thread. This is probably a bug.");
- } else {
- Logger.debug("Stopping and removing lobby '{}'", lobby.gameID);
- activeLobbies.remove(lobby);
- }
- }
- }
-
- boolean isStarted(LobbyConnection connection) {
- return activeLobbies.containsKey(connection);
- }
-
- /** Shutdown all threads, destroy the lobbies, and close everything up */
- void shutdownAll() {
- Logger.info("Stopping and removing all LobbyThreads");
- activeLobbies.keySet().forEach(LobbyConnection::terminate);
- Logger.debug("All lobby shutdown flags set");
- }
-
- // later...
- void checkThreads() {
-
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/Participant.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/Participant.java
deleted file mode 100644
index f8d7f48..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/Participant.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.server.netconnector.Client;
-import uulm.teamname.marvelous.server.netconnector.SUID;
-
-public class Participant {
- private Client client;
- public final SUID id;
- public final String lobby;
- public ParticipantState state = ParticipantState.Assigned;
- public final ParticipantType type;
- public boolean disconnected = false;
- public final boolean isAI;
-
- public Participant(Client client, String lobby, ParticipantType type, boolean ai) {
- this.client = client;
- this.id = client.getId();
- this.lobby = lobby;
- this.type = type;
- this.isAI = ai;
- }
-
- public void setClient(Client client) {
- this.client = client;
- }
-
- public Client getClient() {
- return client;
- }
-
- public boolean sendError(String error) {
- if(disconnected) {
- return false;
- }
- return client.sendError(error);
- }
-
- public boolean sendMessage(BasicMessage message) {
- if(disconnected) {
- return false;
- }
- return client.sendMessage(message);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Participant other = (Participant) o;
- return other.id.equals(id);
- }
-
- @Override
- public int hashCode(){
- final int prime = 31;
- int result = 1;
- result = prime * result + ((client == null) ? 0 : client.hashCode()) + ((id == null) ? 0 : id.hashCode());
- return result;
- }
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/ParticipantState.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/ParticipantState.java
deleted file mode 100644
index 9ff527c..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/ParticipantState.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-public enum ParticipantState {
- Assigned,
- Selected,
- Playing
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/RandomWordGenerator.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/RandomWordGenerator.java
deleted file mode 100644
index 71eee4c..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/RandomWordGenerator.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-import java.util.Random;
-
-public class RandomWordGenerator {
-
-
- private static Random random = new Random();
-
- public static String generateTwoWords() {
- var firstWord = randomWords[random.nextInt(randomWords.length)];
- var secondWord = randomWords[random.nextInt(randomWords.length)];
- while (firstWord.equals(secondWord)) {
- secondWord = randomWords[random.nextInt(randomWords.length)];
- }
-
- firstWord = firstWord.substring(0, 1).toUpperCase() + firstWord.substring(1).toLowerCase();
- secondWord = secondWord.substring(0, 1).toUpperCase() + secondWord.substring(1).toLowerCase();
- return firstWord + secondWord;
- }
-
-
- private static final String[] randomWords = new String[]{
- "wait",
- "release",
- "river",
- "important",
- "mark",
- "electric",
- "defective",
- "poke",
- "blue",
- "beef",
- "spring",
- "hurt",
- "orange",
- "happy",
- "zealous",
- "flowery",
- "accurate",
- "brake",
- "title",
- "x-ray",
- "festive",
- "wrathful",
- "scissors",
- "peaceful",
- "finicky",
- "shape",
- "soothe",
- "head",
- "spotted",
- "needless",
- "time",
- "abundant",
- "humdrum",
- "mouth",
- "trot",
- "bounce",
- "thank",
- "avoid",
- "shocking",
- "minor",
- "secret",
- "rabbit",
- "protect",
- "honey",
- "business",
- "worthless",
- "suggest",
- "splendid",
- "drab",
- "safe",
- "gigantic",
- "arrive",
- "drum",
- "hate",
- "dinosaurs",
- "bore",
- "tired",
- "regret",
- "fit",
- "potato",
- "confuse",
- "childlike",
- "vein",
- "sound",
- "attack",
- "exchange",
- "back",
- "check",
- "damaged",
- "grandmother",
- "division",
- "groovy",
- "throat",
- "office",
- "pin",
- "stare",
- "meddle",
- "shivering",
- "interfere",
- "occur",
- "hole",
- "sugar",
- "test",
- "blind",
- "free",
- "perform",
- "cherries",
- "flavor",
- "stupendous",
- "purpose",
- "extend",
- "risk",
- "fanatical",
- "grubby",
- "beg",
- "romantic",
- "outrageous",
- "swift",
- "bath",
- "room",
- "pocket",
- "front",
- "flower",
- "quicksand",
- "mark",
- "sturdy",
- "resolute",
- "letters",
- "expert",
- "hapless",
- "bloody",
- "blue-eyed",
- "hope",
- "chew",
- };
-}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/net/Client.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/Client.java
new file mode 100644
index 0000000..a1fbbfe
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/Client.java
@@ -0,0 +1,104 @@
+package uulm.teamname.marvelous.server.net;
+
+import org.java_websocket.WebSocket;
+import org.java_websocket.framing.CloseFrame;
+import org.tinylog.Logger;
+import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
+import uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage;
+import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
+
+import java.util.Optional;
+
+@SuppressWarnings("UnusedReturnValue")
+public class Client {
+ private final WebSocket socket;
+ private ClientState state = ClientState.Blank;
+ private SUID id = null;
+ private ParticipantType type = null;
+ private boolean ai = false;
+
+ public Client(WebSocket socket) {
+ this.socket = socket;
+ }
+
+ public boolean sendError(String error) {
+ ErrorMessage errorMessage = new ErrorMessage();
+ errorMessage.message = error;
+ return sendMessage(errorMessage);
+ }
+
+ public boolean sendMessage(BasicMessage message) {
+ if(socket == null) {
+ return false;
+ }
+
+ Optional data = MarvelousServer.json.stringify(message);
+
+ if(data.isEmpty()) {
+ return false;
+ }
+
+ Logger.debug("Sending message to " + this + ": " + data.get()); //i hate java so much
+
+ try {
+ socket.send(data.get());
+ return true;
+ }catch (Exception ignored) {
+ return false;
+ }
+ }
+
+ public boolean disconnect() {
+ if(socket == null) {
+ return false;
+ }
+
+ socket.close(CloseFrame.NORMAL);
+ return true;
+ }
+
+ public ClientState getState() {
+ return state;
+ }
+
+ public void setState(ClientState state) {
+ this.state = state;
+ }
+
+ public SUID getID() {
+ return id;
+ }
+
+ public void setID(SUID id) {
+ this.id = id;
+ }
+
+ public ParticipantType getType() {
+ return type;
+ }
+
+ public void setType(ParticipantType type) {
+ this.type = type;
+ }
+
+ public boolean isAI() {
+ return ai;
+ }
+
+ public void isAI(boolean ai) {
+ this.ai = ai;
+ }
+
+ public WebSocket getSocket() {
+ return socket;
+ }
+
+ @Override
+ public String toString() {
+ if(id != null) {
+ return id.toString();
+ }else {
+ return "Client@"+Integer.toHexString(hashCode());
+ }
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/ClientState.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/ClientState.java
similarity index 61%
rename from Server/src/main/java/uulm/teamname/marvelous/server/netconnector/ClientState.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/net/ClientState.java
index cee5962..1aac3bd 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/ClientState.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/ClientState.java
@@ -1,9 +1,10 @@
-package uulm.teamname.marvelous.server.netconnector;
+package uulm.teamname.marvelous.server.net;
public enum ClientState {
Blank,
Ready,
Assigned,
+ Selected,
Reconnect,
Playing
}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/net/MarvelousServer.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/MarvelousServer.java
new file mode 100644
index 0000000..d8e99ff
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/MarvelousServer.java
@@ -0,0 +1,88 @@
+package uulm.teamname.marvelous.server.net;
+
+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 uulm.teamname.marvelous.server.ServerApplication;
+
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+
+public class MarvelousServer extends WebSocketServer {
+ public static final JSON json;
+
+ //static initializers are executed the first time the class is referenced
+ static {
+ json = new JSON(ServerApplication.getCharacterConfig());
+ }
+
+ public MarvelousServer(InetSocketAddress address) {
+ super(address);
+
+ Thread.currentThread().setName("WebSocketServer");
+ }
+
+ /** A map of all connected clients. */
+ private final HashMap clients = new HashMap<>();
+
+ @Override
+ public void onOpen(WebSocket conn, ClientHandshake handshake) {
+ Logger.debug("Connected new user");
+
+ Client client = new Client(conn);
+
+ synchronized(clients) {
+ clients.put(conn, client);
+ }
+
+ Logger.trace("Queueing event...");
+ ServerApplication.getSession().addEvent(new SocketEvent(SocketEventType.Connect, client));
+ }
+
+ @Override
+ public void onMessage(WebSocket conn, String message) {
+ Logger.debug("Message received from {}", conn);
+
+ Client client = clients.get(conn);
+
+ if(client == null) {
+ return;
+ }
+
+ if(message.length() == 0) {
+ return;
+ }
+
+ Logger.trace("Queueing event...");
+ ServerApplication.getSession().addEvent(new SocketEvent(SocketEventType.Message, client, message));
+ }
+
+ @Override
+ public void onClose(WebSocket conn, int code, String reason, boolean remote) {
+ Logger.info("Disconnecting client '{}'", conn);
+
+ Client client = clients.get(conn);
+ if(client == null) {
+ return;
+ }
+
+ Logger.trace("Queueing event...");
+ ServerApplication.getSession().addEvent(new SocketEvent(SocketEventType.Disconnect, client));
+
+ synchronized(clients) {
+ clients.remove(conn);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ Logger.info("MarvelousServer started on address {}", this.getAddress().toString());
+ }
+
+ @Override
+ public void onError(WebSocket conn, Exception ex) {
+ Logger.warn("WebSocket-Error occurred: {}", ex.getMessage());
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/SUID.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/SUID.java
similarity index 94%
rename from Server/src/main/java/uulm/teamname/marvelous/server/netconnector/SUID.java
rename to Server/src/main/java/uulm/teamname/marvelous/server/net/SUID.java
index d0d20dd..c416c23 100644
--- a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/SUID.java
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/SUID.java
@@ -1,4 +1,4 @@
-package uulm.teamname.marvelous.server.netconnector;
+package uulm.teamname.marvelous.server.net;
import java.util.Objects;
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/net/ServerSession.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/ServerSession.java
new file mode 100644
index 0000000..23caa22
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/ServerSession.java
@@ -0,0 +1,393 @@
+package uulm.teamname.marvelous.server.net;
+
+import org.tinylog.Logger;
+import uulm.teamname.marvelous.gamelibrary.json.ValidationUtility;
+import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
+import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
+import uulm.teamname.marvelous.gamelibrary.messages.RoleEnum;
+import uulm.teamname.marvelous.gamelibrary.messages.client.*;
+import uulm.teamname.marvelous.gamelibrary.messages.server.*;
+import uulm.teamname.marvelous.server.ServerApplication;
+import uulm.teamname.marvelous.server.game.GameSession;
+
+import java.util.*;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class ServerSession implements Runnable {
+ /** Set to true when the thread is asked to stop. */
+ private volatile boolean stopping = false;
+
+ /** Queue of socket events to handle. */
+ private final BlockingQueue events = new LinkedBlockingQueue<>();
+
+ public void addEvent(SocketEvent event) {
+ try {
+ this.events.put(event);
+ }catch(InterruptedException ignored) { }
+ }
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName("Session");
+
+ Logger.trace("Started session thread.");
+
+ while(!stopping) {
+ try {
+ SocketEvent event = events.poll(1000, TimeUnit.MILLISECONDS);
+ if(event != null) {
+ handleSocketEvent(event);
+ }else {
+ Thread.onSpinWait();
+ }
+ }catch (InterruptedException ignored) { }
+ }
+
+ Logger.trace("Stopped session thread.");
+ }
+
+ /**
+ * Handles {@link SocketEvent SocketEvents} from the queue.
+ * @param event The event to handle
+ */
+ private void handleSocketEvent(SocketEvent event) {
+ switch(event.type) {
+ case Connect -> onConnect(event.client);
+ case Message -> onMessage(event.client, event.data);
+ case Disconnect -> onDisconnect(event.client);
+ }
+ }
+
+ /** Requests the session thread to stop. */
+ public void stop() {
+ Logger.trace("Session stop requested.");
+
+ stopping = true;
+ }
+
+
+ public void broadcast(BasicMessage message, SUID... except) {
+ if(state != SessionState.Running) {
+ return;
+ }
+
+ HashSet filter = new HashSet<>(List.of(except));
+
+ for(Client player: players) {
+ if(player == null || filter.contains(player.getID())) {
+ continue;
+ }
+ player.sendMessage(message);
+ }
+ for(Client spectator: spectators) {
+ if(filter.contains(spectator.getID())) {
+ continue;
+ }
+ spectator.sendMessage(message);
+ }
+ }
+
+ public void reset() {
+ if(state != SessionState.Running) {
+ return;
+ }
+
+ state = SessionState.Pending;
+
+ for(int i = 0; i < 2; i++) {
+ if(players[i] != null) {
+ players[i].disconnect();
+ players[i] = null;
+ }
+ }
+ for(Client spectator: spectators) {
+ spectator.disconnect();
+ }
+ spectators.clear();
+
+ game = null;
+ }
+
+
+ private SessionState state = SessionState.Pending;
+
+ private GameSession game = null;
+
+ private final Client[] players = new Client[2];
+ private final ArrayList spectators = new ArrayList<>();
+
+ public Client[] getPlayers() {
+ return players;
+ }
+ public ArrayList getSpectators() {
+ return spectators;
+ }
+
+
+ private void onConnect(Client client) {
+ //do nothing ?
+ }
+
+ private void onMessage(Client client, String message) {
+ Logger.trace("Parsing message...");
+ Optional parsed = MarvelousServer.json.parse(message);
+
+ if(parsed.isEmpty()) {
+ Logger.debug("Message couldn't be parsed, sending error...");
+ client.sendError("Message could not be parsed.");
+ return;
+ }
+
+ BasicMessage data = parsed.get();
+ Optional violations = ValidationUtility.validate(data);
+
+ if(violations.isPresent()) {
+ Logger.debug("The message that client sent had structural violations: '{}'. Sending error...", violations.get());
+ client.sendError(violations.get());
+ }else {
+ HandleMessage(client, data);
+ }
+ }
+
+ private void onDisconnect(Client client) {
+ if(client.getID() == null) {
+ return;
+ }
+
+ Logger.debug("Client " + client + " disconnected");
+
+ if(game != null) {
+ game.handleDisconnect(client);
+ }
+
+ for(int i = 0; i < 2; i++) {
+ if(players[i] != null && players[i].getID().equals(client.getID())) {
+ players[i] = null;
+ return;
+ }
+ }
+ for(int i = 0; i < spectators.size(); i++) {
+ if(spectators.get(i).getID().equals(client.getID())) {
+ spectators.remove(i);
+ return;
+ }
+ }
+ }
+
+
+ private final String errorInvalidMessage = "This message is invalid right now.";
+
+ private void HandleMessage(Client client, BasicMessage data) {
+ Logger.debug("Received message from " + client + ": " + data);
+
+ Logger.trace("Handling message...");
+
+ if(data instanceof HelloServerMessage) {
+ Logger.trace("Message was instanceof HelloServerMessage");
+ HelloServerMessage message = (HelloServerMessage) data;
+ handleHelloServerMessage(client, message);
+
+ } else if(data instanceof ReconnectMessage) {
+ ReconnectMessage message = (ReconnectMessage) data;
+ handleReconnectMessage(client, message);
+
+ } else if(data instanceof PlayerReadyMessage) {
+ Logger.trace("Message was instanceof PlayerReadyMessage");
+ PlayerReadyMessage message = (PlayerReadyMessage) data;
+ handlePlayerReadyMessage(client, message);
+
+ } else if(data instanceof CharacterSelectionMessage) {
+ Logger.trace("Message was instanceof CharacterSelectionMessage");
+ CharacterSelectionMessage message = (CharacterSelectionMessage) data;
+ handleCharacterSelectionMessage(client, message);
+
+ } else if(data instanceof RequestMessage) {
+ Logger.trace("Message was instanceof RequestMessage");
+ RequestMessage message = (RequestMessage) data;
+ handleRequestsMessage(client, message);
+
+ }
+ }
+
+ /** Handles a HelloServerMessage */
+ private void handleHelloServerMessage(Client client, HelloServerMessage message) {
+ if(client.getState() != ClientState.Blank) {
+ client.sendError(errorInvalidMessage);
+ return;
+ }
+
+ SUID id = new SUID(message.name, message.deviceID);
+
+ boolean known = false;
+ for(int i = 0; i < 2; i++) {
+ if(players[i] != null && players[i].getID().equals(id)) {
+ players[i] = client;
+ known = true;
+ break;
+ }
+ }
+ for(int i = 0; i < spectators.size(); i++) {
+ if(known) {
+ break;
+ }
+ if(spectators.get(i).getID().equals(id)) {
+ spectators.set(i, client);
+ known = true;
+ break;
+ }
+ }
+
+ if(!known) {
+ client.setID(id);
+ }
+
+ boolean running = false;
+ if(state == SessionState.Running && known) {
+ client.setState(ClientState.Reconnect);
+ running = true;
+ }else {
+ client.setState(ClientState.Ready);
+ }
+
+ HelloClientMessage response = new HelloClientMessage();
+ response.runningGame = running;
+ client.sendMessage(response);
+ }
+
+ /** Handles a reconnectMessage */
+ private void handleReconnectMessage(Client client, ReconnectMessage message) {
+ if(client.getState() != ClientState.Reconnect) {
+ client.sendError(errorInvalidMessage);
+ return;
+ }
+
+ if(message.reconnect.equals(Boolean.TRUE)) {
+ client.setState(ClientState.Playing);
+
+ game.handleReconnect(client);
+ }else {
+ client.setState(ClientState.Ready);
+ }
+ }
+
+ /** Handles a PlayerReadyMessage */
+ private void handlePlayerReadyMessage(Client client, PlayerReadyMessage message) {
+ if(client.getState() != ClientState.Ready) {
+ client.sendError(errorInvalidMessage);
+ return;
+ }
+
+ if(message.startGame.equals(Boolean.FALSE)) {
+ GoodbyeClientMessage response = new GoodbyeClientMessage();
+ response.message = "No game requested.";
+ client.sendMessage(response);
+
+ client.disconnect();
+ return;
+ }
+
+ ParticipantType participant = null;
+ if(message.role == RoleEnum.SPECTATOR) {
+ spectators.add(client);
+ participant = ParticipantType.Spectator;
+ }else {
+ for(int i = 0; i < 2; i++) {
+ if(players[i] == null) {
+ players[i] = client;
+ participant = i == 0 ? ParticipantType.PlayerOne : ParticipantType.PlayerTwo;
+ break;
+ }
+ }
+ }
+
+ if(participant != null) {
+ if(game == null) {
+ game = new GameSession();
+ }
+
+ client.setType(participant);
+ client.isAI(message.role == RoleEnum.KI);
+ client.setState(ClientState.Assigned);
+
+ if(participant != ParticipantType.Spectator) {
+ GameAssignmentMessage response = new GameAssignmentMessage();
+ response.gameID = game.id;
+ response.characterSelection = game.characterChoices.get(participant);
+ client.sendMessage(response);
+ }else {
+ if(state == SessionState.Running) {
+ game.addSpectator(client);
+ }
+ }
+ }else {
+ client.sendError("The game is already full. Please connect as a spectator instead.");
+
+ client.disconnect();
+ }
+ }
+
+ /** Handles a characterSelectionMessage */
+ private void handleCharacterSelectionMessage(Client client, CharacterSelectionMessage message) {
+ if(client.getState() != ClientState.Assigned) {
+ client.sendError(errorInvalidMessage);
+ return;
+ }
+
+ int total = ServerApplication.getCharacterConfig().characters.length;
+ int received = message.characters.length;
+
+ if(received != total / 2) {
+ client.sendError("Your character selection size is invalid.");
+ return;
+ }
+
+ int selected = 0;
+ boolean[] selection = new boolean[total / 2];
+ for(int i = 0; i < received; i++) {
+ if(message.characters[i].equals(Boolean.TRUE)) {
+ selected++;
+ selection[i] = true;
+ }else {
+ selection[i] = false;
+ }
+ }
+
+ if(selected != total / 4) {
+ client.sendError("Your character selection is incomplete.");
+ return;
+ }
+
+ client.setState(ClientState.Selected);
+
+ int complete = 0;
+ for(int i = 0; i < 2; i++) {
+ if(players[i] != null && players[i].getState() == ClientState.Selected) {
+ complete++;
+ }
+ }
+
+ game.handleSelection(client, selection);
+
+ if(complete <= 1) {
+ ConfirmSelectionMessage response = new ConfirmSelectionMessage();
+ response.selectionComplete = true;
+ client.sendMessage(response);
+ }else {
+ state = SessionState.Running;
+
+ game.start();
+ }
+ }
+
+ /** Handles a RequestMessage */
+ private void handleRequestsMessage(Client client, RequestMessage message) {
+ if(client.getState() != ClientState.Playing) {
+ client.sendError(errorInvalidMessage);
+ return;
+ }
+
+ game.handleRequests(client, message.messages);
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/net/SessionState.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/SessionState.java
new file mode 100644
index 0000000..2a3a11d
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/SessionState.java
@@ -0,0 +1,6 @@
+package uulm.teamname.marvelous.server.net;
+
+public enum SessionState {
+ Pending,
+ Running
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/net/SocketEvent.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/SocketEvent.java
new file mode 100644
index 0000000..5067f47
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/SocketEvent.java
@@ -0,0 +1,19 @@
+package uulm.teamname.marvelous.server.net;
+
+public class SocketEvent {
+ public final SocketEventType type;
+ public final Client client;
+ public final String data;
+
+ public SocketEvent(SocketEventType type, Client client, String data) {
+ this.type = type;
+ this.client = client;
+ this.data = data;
+ }
+
+ public SocketEvent(SocketEventType type, Client client) {
+ this.type = type;
+ this.client = client;
+ this.data = null;
+ }
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/net/SocketEventType.java b/Server/src/main/java/uulm/teamname/marvelous/server/net/SocketEventType.java
new file mode 100644
index 0000000..c0031ee
--- /dev/null
+++ b/Server/src/main/java/uulm/teamname/marvelous/server/net/SocketEventType.java
@@ -0,0 +1,7 @@
+package uulm.teamname.marvelous.server.net;
+
+public enum SocketEventType {
+ Connect,
+ Message,
+ Disconnect
+}
diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/Client.java b/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/Client.java
deleted file mode 100644
index 00ca362..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/Client.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package uulm.teamname.marvelous.server.netconnector;
-
-import org.java_websocket.WebSocket;
-import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage;
-
-import java.util.Optional;
-
-public class Client {
- public final WebSocket socket;
- private SUID id;
- private ClientState state = ClientState.Blank;
-
- public Client(WebSocket socket) {
- this.socket = socket;
- }
-
- public boolean sendError(String error) {
- ErrorMessage errorMessage = new ErrorMessage();
- errorMessage.message = error;
- return sendMessage(errorMessage);
- }
-
- public boolean sendMessage(BasicMessage message) {
- if(socket == null) {
- return false;
- }
-
- Optional data = UserManager.getInstance().json.stringify(message);
-
- if (data.isEmpty()) {
- return false;
- }
-
- socket.send(data.get());
- return true;
- }
-
- public WebSocket getSocket() {
- return socket;
- }
-
- public ClientState getState() {
- return state;
- }
-
- public SUID getId() {
- return id;
- }
-
- public void setId(SUID id) {
- this.id = id;
- }
-
- public void setState(ClientState state) {
- this.state = state;
- }
-}
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
deleted file mode 100644
index 574da86..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/MarvelousServer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-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 java.net.InetSocketAddress;
-
-public class MarvelousServer extends WebSocketServer {
- @Override
- public void onOpen(WebSocket conn, ClientHandshake handshake) {
- Logger.info("New client connected.");
- UserManager.getInstance().connectUser(conn);
- }
-
- @Override
- public void onClose(WebSocket conn, int code, String reason, boolean remote) {
- Logger.info("Client disconnected.");
- UserManager.getInstance().disconnectUser(conn, remote);
- }
-
- @Override
- public void onMessage(WebSocket conn, String message) {
- Logger.debug("Message received: {}", message);
- UserManager.getInstance().messageReceived(conn, message);
- }
-
- @Override
- public void onError(WebSocket conn, Exception ex) {
- Logger.warn("WebSocket-Error occurred: {}", ex.getMessage());
- }
-
- @Override
- public void onStart() {
- Logger.info("MarvelousServer started on Address {}", this.getAddress().toString());
- }
-
- public MarvelousServer(InetSocketAddress address) {
- super(address);
- }
-}
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
deleted file mode 100644
index ada923e..0000000
--- a/Server/src/main/java/uulm/teamname/marvelous/server/netconnector/UserManager.java
+++ /dev/null
@@ -1,278 +0,0 @@
-package uulm.teamname.marvelous.server.netconnector;
-
-import org.java_websocket.framing.CloseFrame;
-import org.tinylog.Logger;
-import uulm.teamname.marvelous.gamelibrary.json.JSON;
-import uulm.teamname.marvelous.gamelibrary.json.ValidationUtility;
-import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.client.*;
-import uulm.teamname.marvelous.gamelibrary.messages.server.GoodbyeClientMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.server.HelloClientMessage;
-import uulm.teamname.marvelous.server.Server;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyManager;
-
-import org.java_websocket.WebSocket;
-
-import java.util.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * 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.
- */
-public class UserManager {
- private static UserManager instance;
- private static final String errorInvalidMessage = "Invalid message.";
-
- /**
- * @return the current instance of the UserManager
- */
- public static UserManager getInstance() {
- if (instance == null) {
- instance = new UserManager();
- }
- return instance;
- }
-
- /** A map of all connected clients. */
- private final HashMap clients = new HashMap<>();
-
- public final JSON json;
-
- /** Constructs a new, empty UserManager */
- private UserManager() {
- this.json = new JSON(Server.getCharacterConfig());
- }
-
- /** Called on a new WebSocket connection. */
- public void connectUser(WebSocket conn) {
- Logger.debug("Connected new user");
- synchronized(clients) {
- clients.put(conn, new Client(conn));
- }
- }
-
- /**
- * Called on any received messages. The method checks the message for validity, and then relays it accordingly.
- * @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) {
- Logger.debug("Message received from {}", conn);
- Client client = clients.get(conn);
-
- if(client == null) {
- Logger.warn("messageReceived called with null-valued client. This is probably a bug.");
- return;
- }
-
- Logger.trace("Parsing message...");
- Optional parsed = json.parse(message);
-
- if(parsed.isEmpty()) {
- Logger.debug("Message couldn't be parsed, sending error...");
- client.sendError("Message could not be parsed.");
- return;
- }
-
- BasicMessage data = parsed.get();
- Optional violations = ValidationUtility.validate(data);
-
- if(violations.isPresent()) {
- Logger.debug("The message that client sent had structural violations: '{}'. Sending error...",
- violations.get());
- client.sendError(violations.get());
- return;
- }
-
- Logger.trace("Handling message...");
- handleMessage(client, data);
- }
-
- /** Called on closed connection. */
- public void disconnectUser(WebSocket conn, boolean closedByRemote) {
- Logger.info("Disconnecting client '{}'", conn);
- Client client = clients.get(conn);
-
- if(client == null) {
- Logger.warn("disconnect called with null-valued client. This is probably a bug.");
- return;
- }
-
- Logger.trace("Calling handleDisconnect on LobbyManager");
- LobbyManager.getInstance().handleDisconnect(client, closedByRemote);
-
- synchronized(clients) {
- Logger.debug("Removing connections from clients HashMap");
- clients.remove(conn);
- }
- }
-
-
- /** Checks type of a given message, and delegates its execution to the respective handler method */
- private void handleMessage(Client client, BasicMessage data) {
- Logger.debug("Received message from client '{}'", client);
- if(data instanceof HelloServerMessage) {
- Logger.trace("Message was instanceof HelloServerMessage");
- HelloServerMessage message = (HelloServerMessage) data;
- handleHelloServerMessage(client, message);
-
- } else if(data instanceof ReconnectMessage) {
- ReconnectMessage message = (ReconnectMessage) data;
- handleReconnectMessage(client, message);
-
- } else if(data instanceof PlayerReadyMessage) {
- Logger.trace("Message was instanceof PlayerReadyMessage");
- PlayerReadyMessage message = (PlayerReadyMessage) data;
- handlePlayerReadyMessage(client, message);
-
- } else if(data instanceof CharacterSelectionMessage) {
- Logger.trace("Message was instanceof CharacterSelectionMessage");
- CharacterSelectionMessage message = (CharacterSelectionMessage) data;
- handleCharacterSelectionMessage(client, message);
-
- } else if(data instanceof RequestMessage) {
- Logger.trace("Message was instanceof RequestMessage");
- RequestMessage message = (RequestMessage) data;
- handleRequestsMessage(client, message);
-
- }
- }
-
- /** Handles a HelloServerMessage */
- private void handleHelloServerMessage(Client client, HelloServerMessage message) {
- if(client.getState() != ClientState.Blank) {
- Logger.debug("Disconnecting client as ClientState isn't Blank but {}", client.getState());
- client.sendError("Invalid message, as handshake already completed.");
- return;
- }
-
- client.setId(new SUID(message.name, message.deviceID));
-
- Logger.trace("forwarding message to the LobbyManager");
- AtomicBoolean running = new AtomicBoolean(false);
- if(LobbyManager.getInstance().handleConnect(client, running)) {
-
- var clientHasRunningGame = running.get();
- if (clientHasRunningGame) {
- client.setState(ClientState.Reconnect);
- } else {
- client.setState(ClientState.Ready);
- }
-
- HelloClientMessage response = new HelloClientMessage();
- response.runningGame = running.get();
- client.sendMessage(response);
- } else {
- client.sendError("Message could not be processed.");
- }
- }
-
- /** Handles a reconnectMessage, and reconnects the client if needed */
- private void handleReconnectMessage(Client client, ReconnectMessage message) {
- if(client.getState() != ClientState.Reconnect) {
- client.sendError("Invalid message, as client is not in reconnect-ready state");
- return;
- }
-
- if(Boolean.TRUE.equals(message.reconnect)) {
- Logger.trace("Reconnecting to lobby. Forwarding reconnect instruction to the LobbyManager");
- if(LobbyManager.getInstance().handleReconnect(client)) {
- Logger.trace("Successfully reconnected client, changing state to Playing...");
- client.setState(ClientState.Playing);
- } else {
- Logger.debug("The client couldn't be reconnected properly");
- client.sendError("You could not be reconnected to the Lobby");
- }
- } else {
- Logger.trace("No reconnect requested, setting client to ready to connect state");
- client.setState(ClientState.Ready);
- }
- }
-
- /** Handles a PlayerReadyMessage, and connects a client to a lobby if valid */
- private void handlePlayerReadyMessage(Client client, PlayerReadyMessage message) {
- Logger.trace("Handing PlayerReadyMessage...");
- if(client.getState() != ClientState.Ready) {
- Logger.debug("Client wasn't in Ready state but instead in {} state, sending error...", client.getState());
- client.sendError("Invalid message, as client is not in Ready state");
- return;
- }
-
- Logger.trace("Relaying message to LobbyManager");
- if(Boolean.TRUE.equals(message.startGame)) {
- if(LobbyManager.getInstance().handleReady(client, message)) {
- client.setState(ClientState.Assigned);
- } else {
- Logger.trace("Sending error to client as message couldn't be processed properly");
- client.sendError(errorInvalidMessage);
- }
- } else {
- Logger.debug("Disconnecting client as game couldn't be started");
- removeClient(client, "You got disconnected.");
- }
- }
-
- /** Handles a characterSelectionMessage, and forwards it to the LobbyManager if valid */
- private void handleCharacterSelectionMessage(Client client, CharacterSelectionMessage message) {
- Logger.trace("Handling CharacterSelectionMessage");
- if(client.getState() != ClientState.Assigned) {
- Logger.debug("Couldn't handle CharacterSelectionMessage as client wasn't in assignedState but in {}",
- client.getState());
- client.sendError("Cannot select character, as client is not in the Character Selection phase");
- return;
- }
-
- Logger.trace("relaying message to be handled by the LobbyManager...");
- if(LobbyManager.getInstance().handleSelection(client, message)) {
- Logger.trace("Handled successfully");
- } else {
- client.sendError(errorInvalidMessage);
- }
- }
-
- /** Handles a RequestMessage, and forwards it to the LobbyManager if valid */
- private void handleRequestsMessage(Client client, RequestMessage message) {
- Logger.trace("Handling RequestMessage");
- if(client.getState() != ClientState.Playing) {
- Logger.debug("Couldn't handle RequestMessage as client wasn't in playingState but in {}",
- client.getState());
- client.sendError("Invalid message, as client is not ingame");
- return;
- }
-
- Logger.trace("relaying message to be handled by the LobbyManager...");
- if(LobbyManager.getInstance().handleRequests(client, message)) {
- Logger.trace("Handled successfully");
- //"i approve" - the server
- } else {
- Logger.debug("Message couldn't be handled, sending error to client");
- client.sendError(errorInvalidMessage);
- }
- }
-
-
- /**
- * Called when a client should be removed from the game.
- * @param client is the client to be removed
- * @param message is the message that is sent to the client in the accompanying {@link GoodbyeClientMessage}
- */
- public void removeClient(Client client, String message) {
- GoodbyeClientMessage response = new GoodbyeClientMessage();
- response.message = message;
-
- client.sendMessage(response);
-
- client.socket.close(CloseFrame.NORMAL);
- }
-
-
- public int getUserCount() {
- return clients.size();
- }
-
- public boolean containsConnection(WebSocket conn) {
- return clients.containsKey(conn);
- }
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/BaseGameLogicTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/BaseGameLogicTest.java
index 1d3084e..483fb91 100644
--- a/Server/src/test/java/uulm/teamname/marvelous/server/BaseGameLogicTest.java
+++ b/Server/src/test/java/uulm/teamname/marvelous/server/BaseGameLogicTest.java
@@ -1,9 +1,7 @@
package uulm.teamname.marvelous.server;
import uulm.teamname.marvelous.gamelibrary.config.*;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyRunner;
-import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ThreadLocalRandom;
@@ -86,16 +84,4 @@ public class BaseGameLogicTest {
return props;
}
-
- static void setPrivateFinalBoolean(Class c, String name, boolean value) throws NoSuchFieldException, IllegalAccessException {
- Field field = LobbyRunner.class.getDeclaredField(name);
- field.setAccessible(true);
-
- //this is broken somehow
- //Field modifiersField = Field.class.getDeclaredField("modifiers");
- //modifiersField.setAccessible(true);
- //modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
-
- field.set(null, value);
- }
}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/MarvelousServerTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/MarvelousServerApplicationTest.java
similarity index 64%
rename from Server/src/test/java/uulm/teamname/marvelous/server/MarvelousServerTest.java
rename to Server/src/test/java/uulm/teamname/marvelous/server/MarvelousServerApplicationTest.java
index 68faede..0edab63 100644
--- a/Server/src/test/java/uulm/teamname/marvelous/server/MarvelousServerTest.java
+++ b/Server/src/test/java/uulm/teamname/marvelous/server/MarvelousServerApplicationTest.java
@@ -1,6 +1,5 @@
package uulm.teamname.marvelous.server;
-import org.java_websocket.WebSocket;
import org.junit.jupiter.api.*;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
@@ -15,87 +14,71 @@ import uulm.teamname.marvelous.gamelibrary.messages.client.HelloServerMessage;
import uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage;
import uulm.teamname.marvelous.gamelibrary.messages.client.ReconnectMessage;
import uulm.teamname.marvelous.gamelibrary.messages.server.*;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyConnection;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyManager;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyRunner;
-import uulm.teamname.marvelous.server.netconnector.UserManager;
+import uulm.teamname.marvelous.server.net.Client;
+import uulm.teamname.marvelous.server.net.ServerSession;
+import uulm.teamname.marvelous.server.net.SocketEvent;
+import uulm.teamname.marvelous.server.net.SocketEventType;
-import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Optional;
import static org.mockito.Mockito.*;
-class MarvelousServerTest extends BaseGameLogicTest {
- private static MockedStatic serverMock;
+@SuppressWarnings({"ResultOfMethodCallIgnored", "SameParameterValue", "unchecked"})
+class MarvelousServerApplicationTest extends BaseGameLogicTest {
+ private static ServerSession session;
+ private static MockedStatic serverMock;
private static JSON json;
@BeforeAll
- static void start() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
- Constructor um = UserManager.class.getDeclaredConstructor();
- um.setAccessible(true);
- um.newInstance();
- Constructor lm = LobbyManager.class.getDeclaredConstructor();
- lm.setAccessible(true);
- lm.newInstance();
+ static void start() {
+ session = new ServerSession();
- serverMock = Mockito.mockStatic(Server.class);
+ serverMock = Mockito.mockStatic(ServerApplication.class);
generate();
- setPrivateFinalBoolean(LobbyConnection.class, "synchronous", true);
- setPrivateFinalBoolean(LobbyRunner.class, "synchronous", true);
-
- serverMock.when(Server::getMaxLobbies).thenReturn(10);
- serverMock.when(Server::getPartyConfig).thenReturn(partyConfig);
- serverMock.when(Server::getScenarioConfig).thenReturn(scenarioConfig);
- serverMock.when(Server::getCharacterConfig).thenReturn(characterConfig);
+ serverMock.when(ServerApplication::getSession).thenReturn(session);
+ serverMock.when(ServerApplication::getPartyConfig).thenReturn(partyConfig);
+ serverMock.when(ServerApplication::getScenarioConfig).thenReturn(scenarioConfig);
+ serverMock.when(ServerApplication::getCharacterConfig).thenReturn(characterConfig);
json = new JSON(characterConfig);
- if (!Configuration.isFrozen()) {
+ if(!Configuration.isFrozen()) {
Configuration.set("writer1", "console");
Configuration.set("writer1.level", "trace");
Configuration.set("writer1.format", "[{thread}] {level}: {message}");
}
}
-
@Test
void main() {
- UserManager m = UserManager.getInstance();
+ session.run();
- WebSocket p1 = mock(WebSocket.class);
- WebSocket p2 = mock(WebSocket.class);
- WebSocket s1 = mock(WebSocket.class);
- WebSocket s2 = mock(WebSocket.class);
+ Client p1 = mock(Client.class);
+ Client p2 = mock(Client.class);
+ Client s1 = mock(Client.class);
+ Client s2 = mock(Client.class);
- when(p1.getResourceDescriptor()).thenReturn("/");
- when(p2.getResourceDescriptor()).thenReturn("/");
- when(s1.getResourceDescriptor()).thenReturn("/");
- when(s2.getResourceDescriptor()).thenReturn("/");
+ session.addEvent(new SocketEvent(SocketEventType.Connect, p1));
+ ensureHandshake(p1, "Player 1", "1234", false);
- m.connectUser(p1);
- ensureHandshake(m, p1, "Player 1", "1234", false);
+ session.addEvent(new SocketEvent(SocketEventType.Connect, p2));
+ ensureHandshake(p2, "Player 2", "4321", false);
- m.connectUser(p2);
- ensureHandshake(m, p2, "Player 2", "4321", false);
+ session.addEvent(new SocketEvent(SocketEventType.Connect, s1));
+ ensureHandshake(s1, "Spectator 1", "3333", false);
- m.connectUser(s1);
- ensureHandshake(m, s1, "Spectator 1", "3333", false);
+ session.addEvent(new SocketEvent(SocketEventType.Connect, s2));
+ ensureHandshake(s2, "Spectator 2", "4444", false);
- m.connectUser(s2);
- ensureHandshake(m, s2, "Spectator 2", "4444", false);
+ ensurePlayerReady(p1, true, RoleEnum.PLAYER);
+ ensurePlayerReady(p2, true, RoleEnum.PLAYER);
+ ensureSpectatorReady(s1, true, RoleEnum.SPECTATOR);
- ensurePlayerReady(m, p1, true, RoleEnum.PLAYER);
- ensurePlayerReady(m, p2, true, RoleEnum.PLAYER);
- ensureSpectatorReady(m, s1, true, RoleEnum.SPECTATOR);
-
- ensureCharacterSelection(m, p1, true);
- ensureCharacterSelection(m, p2, false);
+ ensureCharacterSelection(p1, true);
+ ensureCharacterSelection(p2, false);
GameStructureMessage game = new GameStructureMessage();
game.playerOneName = "Player 1";
@@ -119,14 +102,14 @@ class MarvelousServerTest extends BaseGameLogicTest {
clearInvocations(p1, p2, s1);
- m.disconnectUser(p1, true);
+ session.addEvent(new SocketEvent(SocketEventType.Disconnect, p1));
- m.connectUser(p1);
- ensureHandshake(m, p1, "Player 1", "1234", true);
+ session.addEvent(new SocketEvent(SocketEventType.Connect, p1));
+ ensureHandshake(p1, "Player 1", "1234", true);
ReconnectMessage message = new ReconnectMessage();
message.reconnect = true;
- sendMessage(m, p1, message);
+ sendMessage(p1, message);
ensureReceived(p1, new GeneralAssignmentMessage());
ensureReceived(p1, new GameStructureMessage());
@@ -134,7 +117,7 @@ class MarvelousServerTest extends BaseGameLogicTest {
System.out.println("Test Completed");
}
- private void ensureHandshake(UserManager m, WebSocket c, String name, String deviceID, boolean runningGame) {
+ private void ensureHandshake(Client c, String name, String deviceID, boolean runningGame) {
HelloServerMessage message = new HelloServerMessage();
message.name = name;
message.deviceID = deviceID;
@@ -142,10 +125,10 @@ class MarvelousServerTest extends BaseGameLogicTest {
HelloClientMessage response = new HelloClientMessage();
response.runningGame = runningGame;
- ensureResponse(m, c, message, response);
+ ensureResponse(c, message, response);
}
- private void ensurePlayerReady(UserManager m, WebSocket c, boolean startGame, RoleEnum role) {
+ private void ensurePlayerReady(Client c, boolean startGame, RoleEnum role) {
PlayerReadyMessage message = new PlayerReadyMessage();
message.startGame = startGame;
message.role = role;
@@ -153,10 +136,10 @@ class MarvelousServerTest extends BaseGameLogicTest {
GameAssignmentMessage response = new GameAssignmentMessage();
//properties are left null because we can't test their content (they are random)
- ensureResponse(m, c, message, response);
+ ensureResponse(c, message, response);
}
- private void ensureSpectatorReady(UserManager m, WebSocket c, boolean startGame, RoleEnum role) {
+ private void ensureSpectatorReady(Client c, boolean startGame, RoleEnum role) {
PlayerReadyMessage message = new PlayerReadyMessage();
message.startGame = startGame;
message.role = role;
@@ -164,10 +147,10 @@ class MarvelousServerTest extends BaseGameLogicTest {
GeneralAssignmentMessage response = new GeneralAssignmentMessage();
//properties are left null because we can't test their content (they are random)
- ensureResponse(m, c, message, response);
+ ensureResponse(c, message, response);
}
- private void ensureCharacterSelection(UserManager m, WebSocket c, boolean confirmSelection) {
+ private void ensureCharacterSelection(Client c, boolean confirmSelection) {
CharacterSelectionMessage message = new CharacterSelectionMessage();
message.characters = new Boolean[12];
for(int i = 0; i < 6; i++) {
@@ -180,44 +163,44 @@ class MarvelousServerTest extends BaseGameLogicTest {
if(confirmSelection) {
ConfirmSelectionMessage response = new ConfirmSelectionMessage();
response.selectionComplete = false;
- ensureResponse(m, c, message, response);
+ ensureResponse(c, message, response);
} else {
- sendMessage(m, c, message);
+ sendMessage(c, message);
}
}
/** Sends a message from the socket. */
- private void sendMessage(UserManager m, WebSocket c, BasicMessage message) {
+ private void sendMessage(Client c, BasicMessage message) {
Optional in = json.stringify(message);
if(in.isPresent()) {
- m.messageReceived(c, in.get());
+ session.addEvent(new SocketEvent(SocketEventType.Message, c, in.get()));
}else {
throw new IllegalArgumentException("[TEST] Message in test call could not be serialized!");
}
}
/** Ensures that the given socket received the given response in response to the given message. */
- private void ensureResponse(UserManager m, WebSocket c, BasicMessage message, BasicMessage response) {
+ private void ensureResponse(Client c, BasicMessage message, BasicMessage response) {
Optional in = json.stringify(message);
if(in.isPresent()) {
- m.messageReceived(c, in.get());
- verify(c).send((String)argThat(
+ session.addEvent(new SocketEvent(SocketEventType.Message, c, in.get()));
+ verify(c.getSocket()).send((String)argThat(
(out)->verifyResponse((String)out, response)
));
- clearInvocations(c);
+ clearInvocations(c.getSocket());
}else {
throw new IllegalArgumentException("[TEST] Message in test call could not be serialized!");
}
}
/** Ensures that the given socket received the given response. */
- private void ensureReceived(WebSocket c, T response) {
+ private void ensureReceived(Client c, T response) {
boolean found = false;
- for(Invocation i: mockingDetails(c).getInvocations()) {
+ for(Invocation i: mockingDetails(c.getSocket()).getInvocations()) {
Optional received = json.parse(i.getArgument(0, String.class));
if(received.isPresent()) {
@@ -227,7 +210,7 @@ class MarvelousServerTest extends BaseGameLogicTest {
continue; //message is not of the right type
}
- T message = (T) parsed;
+ T message = (T)parsed;
for(Field field: response.getClass().getDeclaredFields()) {
try {
@@ -265,7 +248,7 @@ class MarvelousServerTest extends BaseGameLogicTest {
Optional in = json.parse(actual);
if(in.isPresent()) {
- T message = (T) in.get();
+ T message = (T)in.get();
for(Field field: expected.getClass().getDeclaredFields()) {
try {
@@ -290,6 +273,7 @@ class MarvelousServerTest extends BaseGameLogicTest {
@AfterAll
static void stop() {
+ session.stop();
serverMock.close();
}
}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/TurnTimeoutTimerTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/TurnTimeoutTimerTest.java
deleted file mode 100644
index d5db765..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/TurnTimeoutTimerTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package uulm.teamname.marvelous.server.lobby;
-
-import org.java_websocket.WebSocket;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-import uulm.teamname.marvelous.server.netconnector.Client;
-
-import java.util.function.Consumer;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-class TurnTimeoutTimerTest {
- TurnTimeoutTimer turnTimeoutTimer;
-
- @BeforeEach
- void beforeEach(){
- var callback = mock(Consumer.class);
- turnTimeoutTimer = new TurnTimeoutTimer(20, callback);
- }
-
- @Test
- void startTurnTimerTest(){
- var connection = mock(WebSocket.class);
- var participant = new Participant(new Client(connection), "lobby", ParticipantType.Spectator, false);
- assertThatIllegalStateException().describedAs("Spectators don't have TurnTime").isThrownBy(() -> turnTimeoutTimer.startTurnTimer(participant));
- }
-
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/DisconnectSegmentTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/DisconnectSegmentTest.java
deleted file mode 100644
index de8f272..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/DisconnectSegmentTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.config.FieldType;
-import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobby.Lobby;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyConnection;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-import static org.assertj.core.api.Assertions.*;
-
-class DisconnectSegmentTest {
- Participant player1;
- Participant player2;
- Participant spectator;
- DisconnectSegment disconnectSegment;
- Lobby lobby;
- LobbyConnection connection;
-
- @BeforeEach
- void beforeEach(){
- connection = mock(LobbyConnection.class);
-
- player1 = mock(Participant.class);
- player2 = mock(Participant.class);
- spectator = mock(Participant.class);
-
- lobby = mock(Lobby.class);
- disconnectSegment = new DisconnectSegment(lobby);
-
- when(lobby.getConnection()).thenReturn(connection);
- when(connection.getPlayer1()).thenReturn(player1);
- when(connection.getPlayer2()).thenReturn(player2);
- }
-
- @Test
- void noDisconnectRequestTest(){
- var requests = new Request[] {
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- Packet packet = new Packet(requests, player1);
- AtomicBoolean abort = new AtomicBoolean(false);
- disconnectSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(packet).containsOnly(requests);
- }
-
- @Test
- void disconnectRequestBySpectatorTest(){
- var requests = new Request[] {
- new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest(),
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- Packet packet = new Packet(requests, spectator);
- AtomicBoolean abort = new AtomicBoolean(false);
- disconnectSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(packet).doesNotContain(requests);
- verify(connection).hasPlayer1();
- verify(connection).hasPlayer2();
- }
-
- @Test
- void disconnectRequestByPlayer1Test(){
- var requests = new Request[] {
- new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest(),
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- Packet packet = new Packet(requests, player1);
- AtomicBoolean abort = new AtomicBoolean(false);
- disconnectSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(packet).doesNotContain(requests);
- verify(connection).removeParticipant(player1);
- assertThat(verify(connection).hasPlayer1()).isFalse();
- verify(connection).hasPlayer2();
- }
-
- @Test
- void disconnectRequestByPlayer2Test(){
- var requests = new Request[] {
- new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest(),
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- Packet packet = new Packet(requests, player2);
- AtomicBoolean abort = new AtomicBoolean(false);
- disconnectSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(packet).doesNotContain(requests);
- verify(connection).removeParticipant(player2);
- verify(connection).hasPlayer1();
- assertThat(verify(connection).hasPlayer2()).isFalse();
- }
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegmentTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegmentTest.java
deleted file mode 100644
index 20972e8..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegmentTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-import uulm.teamname.marvelous.server.netconnector.Client;
-
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.mockito.Mockito.*;
-import static org.assertj.core.api.Assertions.*;
-
-class FilterEndRoundRequestSegmentTest {
-
- FilterEndRoundRequestSegment segment;
-
- EntityID activeCharacter;
-
- Participant activeParticipant, inactiveParticipant;
-
- Request[] requests;
-
- @BeforeEach
- void beforeEach() {
- this.segment = new FilterEndRoundRequestSegment(this::getActiveCharacter);
-
- this.activeCharacter = new EntityID(EntityType.P1, 2);
- this.activeParticipant = new Participant(mock(Client.class), null, ParticipantType.PlayerOne, false);
- this.inactiveParticipant = new Participant(mock(Client.class), null, ParticipantType.PlayerTwo, false);
-
- requests = new Request[] {
- new RequestBuilder(RequestType.EndRoundRequest).buildGameRequest()
- };
- }
-
- private EntityID getActiveCharacter() {
- return activeCharacter;
- }
-
- @Test
- @DisplayName("Request from active participant doesn't get filtered")
- void packetFromActiveParticipantTest() {
- var packet = new Packet(requests, activeParticipant);
- var atomicBoolean = new AtomicBoolean(false);
-
- var processedPacket = (Packet) packet.clone();
- segment.processRequests(processedPacket, new ArrayList<>(), atomicBoolean);
-
- assertThat(processedPacket).isEqualTo(packet);
- assertThat(atomicBoolean.get()).isFalse();
- }
-
- @Test
- @DisplayName("Request from non-active participant gets flagged as an error")
- void packetFromNonActiveParticipantTest() {
- var packet = new Packet(requests, inactiveParticipant);
- var atomicBoolean = new AtomicBoolean(false);
-
- var processedPacket = (Packet) packet.clone();
- segment.processRequests(processedPacket, new ArrayList<>(), atomicBoolean);
-
- // assertThat(processedPacket).isEqualTo(packet); is not necessary as there's no actual filtering going on
- assertThat(atomicBoolean.get()).isTrue();
- }
-
-
-
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/GameLogicSegmentTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/GameLogicSegmentTest.java
deleted file mode 100644
index 0ca8e1b..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/GameLogicSegmentTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.events.Event;
-import uulm.teamname.marvelous.gamelibrary.events.EventBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.events.EventType;
-import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.mockito.Mockito.*;
-import static org.assertj.core.api.Assertions.*;
-
-class GameLogicSegmentTest {
-
- @Test
- void processRequests() {
- var game = mock(GameInstance.class);
- var request = new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest();
- var event = new EventBuilder(EventType.DisconnectEvent).buildGameEvent();
- when(game.checkRequestsAndApply(any(ArrayList.class))).thenReturn(Optional.of(List.of(event)));
- var abort = new AtomicBoolean(false);
-
- var segment = new GameLogicSegment(game);
-
- // note that DisconnectRequests are actually never passed to the GameLogic, ever.
- var packet = new Packet(
- new Request[] {request},
- null);
-
- var carrier = new ArrayList(1);
-
- segment.processRequests(packet, carrier, abort);
-
- assertThat(packet).isEmpty();
- assertThat(carrier).contains(event);
- verify(game).checkRequestsAndApply(any(Packet.class));
- assertThat(abort.get()).isFalse();
- }
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PacketTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PacketTest.java
deleted file mode 100644
index ace112f..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PacketTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-import uulm.teamname.marvelous.server.netconnector.Client;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.assertj.core.api.Assertions.*;
-
-class PacketTest {
- Packet packet;
-
- @BeforeEach
- void beforeEach(){
- var requests = new Request[] {
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- packet = new Packet(requests, null);
- }
-
- @Test
- void containsRequestTest(){
- assertThat(packet.containsRequestOfType(RequestType.Req)).isTrue();
- assertThat(packet.containsRequestOfType(RequestType.DisconnectRequest)).isFalse();
- }
-
- @Test
- void removeRequestsOfTypesTest(){
- packet.removeRequestsOfTypes(RequestType.Req);
- assertThat(packet).containsOnly(new RequestBuilder(RequestType.MoveRequest).buildGameRequest());
- }
-
- @Test
- void removeRequestsNotOfTypesTest(){
- packet.removeRequestsNotOfTypes(RequestType.Req);
- assertThat(packet).containsOnly(new RequestBuilder(RequestType.Req).buildGameRequest());
- }
-
- @Test
- void getOriginTest(){
- var requests = new Request[] {
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- var participant = new Participant(new Client(null), "SomeLobby", ParticipantType.PlayerOne, false);
- packet = new Packet(requests, participant);
-
- assertThat(packet.getOrigin()).isEqualTo(participant);
- }
-
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PauseSegmentTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PauseSegmentTest.java
deleted file mode 100644
index 534b4a7..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PauseSegmentTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobbymanager.LobbyConnection;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.mock;
-
-class PauseSegmentTest {
-
- PauseSegment pauseSegment;
-
- @BeforeEach
- void setUp() {
- pauseSegment = new PauseSegment();
- }
-
- @Test
- void pauseGame() {
- assertThat(pauseSegment.isPaused()).isFalse();
- pauseSegment.pauseGame();
- assertThat(pauseSegment.isPaused()).isTrue();
- pauseSegment.pauseGame();
- assertThat(pauseSegment.isPaused()).isTrue();
- }
-
- @Test
- void pauseEnd() {
- assertThat(pauseSegment.isPaused()).isFalse();
- pauseSegment.pauseEnd();
- assertThat(pauseSegment.isPaused()).isFalse();
- pauseSegment.pauseGame();
- assertThat(pauseSegment.isPaused()).isTrue();
- pauseSegment.pauseEnd();
- assertThat(pauseSegment.isPaused()).isFalse();
- }
-
- @Test
- void doNotProcessEventsIfStopped() {
- var requests = new Request[]{
- new RequestBuilder(RequestType.PauseStartRequest).buildGameRequest(),
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest(),
- new RequestBuilder(RequestType.EndRoundRequest).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- var participant = mock(Participant.class);
- var packet = new Packet(requests, participant);
- AtomicBoolean abort = new AtomicBoolean(false);
- pauseSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(packet).containsOnly(new RequestBuilder(RequestType.Req).buildGameRequest(), new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest());
- }
-
- @Test
- void doProcessEventsIfNotStopped(){
- var requests = new Request[]{
- new RequestBuilder(RequestType.PauseStopRequest).buildGameRequest(),
- new RequestBuilder(RequestType.EndRoundRequest).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- };
- var participant = mock(Participant.class);
- var packet = new Packet(requests, participant);
- AtomicBoolean abort = new AtomicBoolean(false);
- pauseSegment.pauseGame();
- pauseSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(packet).containsOnly(
- new RequestBuilder(RequestType.EndRoundRequest).buildGameRequest(),
- new RequestBuilder(RequestType.MoveRequest).buildGameRequest()
- );
- }
-
- @Test
- void pauseRequestWhilePaused(){
- var requests = new Request[]{
- new RequestBuilder(RequestType.PauseStartRequest).buildGameRequest(),
- };
- var participant = mock(Participant.class);
- var packet = new Packet(requests, participant);
- AtomicBoolean abort = new AtomicBoolean(false);
- pauseSegment.pauseGame();
- pauseSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(abort.get()).isTrue();
- }
-
- @Test
- void unpauseRequestWhenNotPaused(){
- var requests = new Request[]{
- new RequestBuilder(RequestType.PauseStopRequest).buildGameRequest(),
- };
- var participant = mock(Participant.class);
- var packet = new Packet(requests, participant);
- AtomicBoolean abort = new AtomicBoolean(false);
- pauseSegment.pauseEnd();
- pauseSegment.processRequests(packet, new ArrayList<>(), abort);
- assertThat(abort.get()).isTrue();
- }
-}
\ No newline at end of file
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PipelineTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PipelineTest.java
deleted file mode 100644
index c35ccda..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PipelineTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobby.Lobby;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-class PipelineTest {
- Pipeline pipeline;
- @BeforeEach
- void beforeEach(){
- pipeline = new Pipeline();
- }
-
- @Test
- void addSegmentsTest(){
- Lobby lobby = mock(Lobby.class);
- PauseSegment pauseSegment = new PauseSegment();
- DisconnectSegment disconnectSegment = new DisconnectSegment(lobby);
- pipeline.addSegment(pauseSegment);
- assertThat(pipeline.contains(pauseSegment)).isTrue();
- assertThat(pipeline.contains(disconnectSegment)).isFalse();
- pipeline.addSegment(disconnectSegment);
- assertThat(pipeline.contains(pauseSegment)).isTrue();
- assertThat(pipeline.contains(disconnectSegment)).isTrue();
- }
-
- @Test
- void processRequestTest(){
- var segment = mock(Segment.class);
- var segment2 = mock(Segment.class);
- var segment3 = mock(Segment.class);
-
- var requests = new Request[]{
- new RequestBuilder(RequestType.PauseStartRequest).buildGameRequest(),
- new RequestBuilder(RequestType.Req).buildGameRequest()
- };
-
- Participant participant = mock(Participant.class);
-
- var abort = new AtomicBoolean(false);
-
- Packet packet = new Packet(requests, participant);
-
- pipeline.addSegment(segment)
- .addSegment(segment2)
- .addSegment(segment3);
-
- pipeline.processRequests(requests, participant);
- verify(segment).processRequests(eq(packet), eq(new ArrayList<>()), any(AtomicBoolean.class));
- verify(segment2).processRequests(eq(packet), eq(new ArrayList<>()), any(AtomicBoolean.class));
- verify(segment3).processRequests(eq(packet), eq(new ArrayList<>()), any(AtomicBoolean.class));
- }
-}
\ No newline at end of file
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PlayerFilterSegmentTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PlayerFilterSegmentTest.java
deleted file mode 100644
index cdde662..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/PlayerFilterSegmentTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.IntVector2;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
-import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
-import uulm.teamname.marvelous.gamelibrary.events.Event;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-import uulm.teamname.marvelous.server.netconnector.Client;
-
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.mockito.Mockito.*;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.assertj.core.api.Assertions.*;
-
-class PlayerFilterSegmentTest {
-
- PlayerFilterSegment segment;
- AtomicBoolean abort;
-
- @BeforeEach
- void beforeEach() {
- segment = new PlayerFilterSegment();
- abort = new AtomicBoolean(false);
- }
-
- @Test
- void validRequestsRemainUntouched() {
- var participant = new Participant(mock(Client.class), null, ParticipantType.PlayerOne, false);
-
- var requests = new Request[] {
- new RequestBuilder(RequestType.MeleeAttackRequest)
- .withOriginField(new IntVector2(1, 4))
- .withTargetField(new IntVector2(2, 4))
- .withOriginEntity(new EntityID(EntityType.P1, 3))
- .buildCharacterRequest(),
- new RequestBuilder(RequestType.MeleeAttackRequest)
- .withOriginField(new IntVector2(2, 4))
- .withTargetField(new IntVector2(3, 5))
- .withOriginEntity(new EntityID(EntityType.P1, 3))
- .withTargetEntity(new EntityID(EntityType.P2, 3))
- .withValue(14)
- .buildCharacterRequest()
- };
-
- var packet = new Packet(requests, participant);
- var carrier = new ArrayList();
-
- assertThatNoException().isThrownBy(() -> segment.processRequests(packet, carrier, abort));
-
- assertThat(packet.toArray(new Request[0]))
- .isEqualTo(requests);
- assertThat(carrier).isEmpty();
- assertThat(abort).isFalse();
-
- }
-
- @Test
- void invalidRequestsTriggerAbort() {
- var participant = new Participant(mock(Client.class), null, ParticipantType.PlayerOne, false);
-
- var requests = new Request[] {
- new RequestBuilder(RequestType.MeleeAttackRequest)
- .withOriginField(new IntVector2(1, 4))
- .withTargetField(new IntVector2(2, 4))
- .withOriginEntity(new EntityID(EntityType.P2, 3))
- .buildCharacterRequest(),
- new RequestBuilder(RequestType.MeleeAttackRequest)
- .withOriginField(new IntVector2(2, 4))
- .withTargetField(new IntVector2(3, 5))
- .withOriginEntity(new EntityID(EntityType.P2, 3))
- .withTargetEntity(new EntityID(EntityType.P1, 3))
- .withValue(14)
- .buildCharacterRequest()
- };
-
- var packet = new Packet(requests, participant);
- var carrier = new ArrayList();
-
- assertThatNoException().isThrownBy(() -> segment.processRequests(packet, carrier, abort));
-
- assertThat(packet.toArray(new Request[0]))
- .isEqualTo(requests);
- assertThat(carrier).isEmpty();
- assertThat(abort).isTrue();
- }
-
-
- @Test
- void gameRequestsRemainUntouched() {
- var participant = new Participant(mock(Client.class), null, ParticipantType.PlayerOne, false);
-
- var requests = new Request[] {
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.EndRoundRequest).buildGameRequest(),
- new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest()
-
- };
-
- var packet = new Packet(requests, participant);
- var carrier = new ArrayList();
-
- assertThatNoException().isThrownBy(() -> segment.processRequests(packet, carrier, abort));
-
- assertThat(packet.toArray(new Request[0]))
- .isEqualTo(requests);
- assertThat(carrier).isEmpty();
- assertThat(abort).isFalse();
- }
-
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/RequestGameLogicSegmentTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/RequestGameLogicSegmentTest.java
deleted file mode 100644
index 01f3585..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/RequestGameLogicSegmentTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package uulm.teamname.marvelous.server.lobby.pipelining;
-
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.events.Event;
-import uulm.teamname.marvelous.gamelibrary.events.GamestateEvent;
-import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance;
-import uulm.teamname.marvelous.gamelibrary.messages.client.RequestMessage;
-import uulm.teamname.marvelous.gamelibrary.messages.server.EventMessage;
-import uulm.teamname.marvelous.gamelibrary.requests.Request;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
-import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-class RequestGameLogicSegmentTest {
-
- @Test
- void requestGamestateTest(){
- var game = mock(GameInstance.class);
- var gamestateEvent = mock(GamestateEvent.class);
- var segment = new RequestGameStateSegment(game);
- var requests = new Request[]{
- new RequestBuilder(RequestType.Req).buildGameRequest(),
- new RequestBuilder(RequestType.DisconnectRequest).buildGameRequest()
- };
- var participant = mock(Participant.class);
- var packet = new Packet(requests, participant);
-
- var message = new EventMessage();
- message.messages = new Event[] {gamestateEvent};
-
- when(game.getGameStateEvent()).thenReturn(gamestateEvent);
-
- AtomicBoolean abort = new AtomicBoolean(false);
- List carrier = new ArrayList<>();
-
- segment.processRequests(packet, carrier, abort);
- assertThat(packet).isEmpty();
- assertThat(carrier).isEmpty();
- verify(game).getGameStateEvent();
- verify(participant).sendMessage(message);
- }
-
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobbymanager/RandomWordGeneratorTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobbymanager/RandomWordGeneratorTest.java
deleted file mode 100644
index 05893b4..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/lobbymanager/RandomWordGeneratorTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package uulm.teamname.marvelous.server.lobbymanager;
-
-import org.junit.jupiter.api.Test;
-
-import static org.mockito.Mockito.*;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.assertj.core.api.Assertions.*;
-
-class RandomWordGeneratorTest {
-
- @Test
- void generatesAString() {
- // System.out.println(RandomWordGenerator.generateTwoWords());
- assertThat(RandomWordGenerator.generateTwoWords()).isInstanceOf(String.class);
- }
-
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/netconnector/ClientTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/netconnector/ClientTest.java
deleted file mode 100644
index ec556a7..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/netconnector/ClientTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package uulm.teamname.marvelous.server.netconnector;
-
-import org.java_websocket.WebSocket;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.json.JSON;
-import uulm.teamname.marvelous.gamelibrary.messages.ErrorMessage;
-
-import static org.mockito.Mockito.*;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.assertj.core.api.Assertions.*;
-
-class ClientTest {
-
- Client client;
- WebSocket socket;
- SUID suid;
-
- @BeforeEach
- void setUp() {
- socket = mock(WebSocket.class);
- suid = new SUID("ClientName", "DeviceID");
-
- client = new Client(socket);
- }
-
- @Test
- void clientGetsCreatedEmpty() {
- assertThat(client.getState()).isEqualTo(ClientState.Blank);
- assertThat(client.getId()).isNull();
- assertThat(client.getSocket()).isEqualTo(socket);
- }
-
- @Test
- void sendError() {
- client.sendError("SomeMessage");
-
- verify(socket).send("{\"messageType\":\"ERROR\",\"message\":\"SomeMessage\",\"type\":0}");
- }
-
- @Test
- void sendMessage() {
- var stringRepresentingErrorMessage = "{\"messageType\":\"ERROR\",\"message\":\"SomeMessage\",\"type\":0}";
-
- var errorMessage = new ErrorMessage();
- errorMessage.message = "SomeMessage";
- errorMessage.type = 0;
-
- client.sendMessage(errorMessage);
-
- verify(socket).send(stringRepresentingErrorMessage);
- }
-}
diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/netconnector/UserManagerTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/netconnector/UserManagerTest.java
deleted file mode 100644
index 98fd48a..0000000
--- a/Server/src/test/java/uulm/teamname/marvelous/server/netconnector/UserManagerTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package uulm.teamname.marvelous.server.netconnector;
-
-import org.java_websocket.WebSocket;
-import org.java_websocket.handshake.ClientHandshake;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
-import uulm.teamname.marvelous.gamelibrary.messages.client.*;
-import uulm.teamname.marvelous.server.lobbymanager.Participant;
-
-import java.lang.reflect.InvocationTargetException;
-
-import static org.mockito.Mockito.*;
-import static org.assertj.core.api.Assertions.*;
-
-class UserManagerTest {
-
- UserManager manager;
-
- Client client1, client2;
- WebSocket socket1, socket2;
- SUID suid1, suid2;
-
- @BeforeEach
- void beforeEach()
- throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
- socket1 = mock(WebSocket.class);
- client1 = spy(new Client(socket1));
- suid1 = new SUID("name1", "devID1");
- when(client1.getId()).thenReturn(suid1);
- when(client1.getSocket()).thenReturn(socket1);
-
- socket2 = mock(WebSocket.class);
- client2 = spy(new Client(socket2));
- suid2 = new SUID("name2", "devID2");
- when(client2.getId()).thenReturn(suid2);
- when(client2.getSocket()).thenReturn(socket2);
-
- var c = UserManager.class.getDeclaredConstructor();
- c.setAccessible(true);
- manager = spy(c.newInstance());
- }
-
- @Test
- void userIsConnectedTest() {
- assertThat(manager.getUserCount()).isZero();
- assertThat(manager.containsConnection(socket1)).isFalse();
- manager.connectUser(socket1);
- assertThat(manager.getUserCount()).isEqualTo(1);
- assertThat(manager.containsConnection(socket1)).isTrue();
-
- verify(socket1, never()).send(any(String.class));
- }
-
- @Test
- void helloServerMessagesGetAssignedProperly() {
- manager.messageReceived(
- socket1,
- "{\"messageType\":\"HELLO_SERVER\",\"name\":\"SomeAwesomeName\",\"deviceID\":\"YAY\"}");
-
- // TODO: test this
- }
-}