diff --git a/server.properties b/server.properties deleted file mode 100644 index 90a0856..0000000 --- a/server.properties +++ /dev/null @@ -1,16 +0,0 @@ -#nl.hanze.newgameserver.GlobalSettings -#Mon Sep 15 21:02:16 CEST 2025 -challenges=true -chat=true -debugTournament=false -gameserver.homepagetext=Welcome to the web interface of the game server. -gameserver.port=7789 -gameserver.tournament.timebetweengames=5 -gameserver.turntimelimit=10 -gameserver.welcomemessage=New Game Server [Aplha version]\n(C) Copyright 2023 Hanzehogeschool Groningen -jsonwebtokensecret=SuperGeheimeJWTSecretKeyOmTokensteCreaten -logging.file.name=./gameserver.log -passwordhash=$2a$10$Yze2Ddu931DdzLQ3J08qGexwsp7dK2cJHFuA8YrZWS2RNyM5IkEeS -server.port=8081 -shuffleWhiteBlack=true -subscribe=true diff --git a/src/main/java/org/toop/Main.java b/src/main/java/org/toop/Main.java index b99c039..30caac1 100644 --- a/src/main/java/org/toop/Main.java +++ b/src/main/java/org/toop/Main.java @@ -21,49 +21,21 @@ public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException, IOException { -// TcpServer server = new TcpServer(5001); -// Thread serverThread = new Thread(server); -// serverThread.start(); - initSystems(); - GlobalEventBus.post(new Events.ServerEvents.StartServer("5001")); + CompletableFuture serverIdFuture = new CompletableFuture<>(); + GlobalEventBus.post(new Events.ServerEvents.StartServerRequest("5001", "tictactoe", serverIdFuture)); + String serverId = serverIdFuture.get(); - CompletableFuture future = new CompletableFuture<>(); - GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest("127.0.0.1", "5001", future)); - String serverId = future.get(); + CompletableFuture connectionIdFuture = new CompletableFuture<>(); + GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest("127.0.0.1", "5001", connectionIdFuture)); + String connectionId = connectionIdFuture.get(); -// for (int i = 0; i < 1; i++) { -// Thread thread = new Thread(() -> { -//// logger.info("Server ID: {}", serverId); -// GlobalEventBus.post(new Events.ServerEvents.Command(serverId, "HELP", "TEST")); -// }); -// thread.start(); -// } + CompletableFuture ticTacToeGame = new CompletableFuture<>(); + GlobalEventBus.post(new Events.ServerEvents.CreateTicTacToeGameRequest(serverId, "John", "Pim", ticTacToeGame)); + String ticTacToeGameId = ticTacToeGame.get(); - GlobalEventBus.post(new Events.ServerEvents.Command(serverId, "HELP", "TEST")); - - GlobalEventBus.post(new Events.ServerEvents.ForceCloseAllConnections()); - GlobalEventBus.post(new Events.ServerEvents.ForceCloseAllServers()); - -// -// CompletableFuture future2 = new CompletableFuture<>(); -// GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest("127.0.0.1", "5001", future2)); -// String serverId2 = future.get(); -// logger.info("Server ID: {}", serverId2); -// GlobalEventBus.post(new Events.ServerEvents.Command(serverId2, "HELP", "TEST2")); - -// GlobalEventBus.post(new Events.ServerEvents.StartConnection("127.0.0.1", "5001")); - - -// Server.startNew("127.0.0.1", "5001"); -// Testsss.start(""); // Used for testing server. -// Window.start(""); - -// CompletableFuture future6 = new CompletableFuture<>(); -// GlobalEventBus.post(new Events.ServerEvents.RequestsAllConnections(future6)); -// String serverConnections = future6.get(); -// logger.info("Running connections: {}", serverConnections); + GlobalEventBus.post(new Events.ServerEvents.RunTicTacToeGame(serverId, ticTacToeGameId)); ConsoleGui console = new ConsoleGui(); GameBase.State state = GameBase.State.INVALID; diff --git a/src/main/java/org/toop/eventbus/Events.java b/src/main/java/org/toop/eventbus/Events.java index eb2fc51..63b7528 100644 --- a/src/main/java/org/toop/eventbus/Events.java +++ b/src/main/java/org/toop/eventbus/Events.java @@ -84,12 +84,18 @@ public class Events implements IEvents { public record ForceCloseAllServers() {} - public record StartServer(String port) {} + public record StartServer(String port, String gameType) {} - public record StartServerRequest(String port, CompletableFuture future) {} + public record StartServerRequest(String port, String gameType, CompletableFuture future) {} public record ServerStarted(String uuid, String port) {} + public record CreateTicTacToeGameRequest(String serverUuid, String playerA, String playerB, CompletableFuture future) {} + + public record RunTicTacToeGame(String serverUuid, String gameUuid) {} + + public record EndTicTacToeGame(String serverUuid, String gameUuid) {} + /** * * Triggers starting a server connection. @@ -109,6 +115,8 @@ public class Events implements IEvents { */ public record StartConnectionRequest(String ip, String port, CompletableFuture future) {} +// public record StartGameConnectionRequest(String ip, String port, CompletableFuture future) {} + /** * Triggers when a connection to a server is established. * diff --git a/src/main/java/org/toop/game/GameBase.java b/src/main/java/org/toop/game/GameBase.java index 558b934..4a2dcca 100644 --- a/src/main/java/org/toop/game/GameBase.java +++ b/src/main/java/org/toop/game/GameBase.java @@ -12,10 +12,10 @@ public abstract class GameBase { public static char EMPTY = '-'; protected int size; - protected char[] grid; + public char[] grid; protected Player[] players; - protected int currentPlayer; + public int currentPlayer; public GameBase(int size, Player player1, Player player2) { this.size = size; @@ -43,18 +43,4 @@ public abstract class GameBase { public Player getCurrentPlayer() { return players[currentPlayer]; } public abstract State play(int index); - - /** - * For AI use only. Does not validate. - */ - public void setGridAt(int index, char move) { - grid[index] = move; - } - - /** - * For AI use only. Does not validate. - */ - public void setCurrentPlayer(int player) { - currentPlayer = player; - } } diff --git a/src/main/java/org/toop/game/tictactoe/MinMaxTicTacToe.java b/src/main/java/org/toop/game/tictactoe/MinMaxTicTacToe.java index 6866e37..a78c757 100644 --- a/src/main/java/org/toop/game/tictactoe/MinMaxTicTacToe.java +++ b/src/main/java/org/toop/game/tictactoe/MinMaxTicTacToe.java @@ -18,7 +18,7 @@ public class MinMaxTicTacToe { int bestMove = 10; // set bestmove to something impossible // simulate all possible moves on the field - for (int i = 0; i < game.getGrid().length; i++) { + for (int i = 0; i < game.grid.length; i++) { if (game.validateMove(i)) { // check if the move is legal here TicTacToe copyGame = game.copyBoard(); // make a copy of the game GameBase.State result = copyGame.play(i); // play a move on the copy board @@ -58,7 +58,7 @@ public class MinMaxTicTacToe { else { boolean empty = false; - for (char cell : game.getGrid()) { // else, look at draw conditions. we check per cell if it's empty or not + for (char cell : game.grid) { // else, look at draw conditions. we check per cell if it's empty or not if (cell == ' ') { empty = true; // if a thing is empty, set to true break; // break the loop @@ -71,7 +71,7 @@ public class MinMaxTicTacToe { if (maximizing) { // it's the maximizing players turn, the AI int bestVal = -100; // set the value to lowest as possible - for (int i = 0; i < game.getGrid().length; i++) { // loop through the grid + for (int i = 0; i < game.grid.length; i++) { // loop through the grid if (game.validateMove(i)) { TicTacToe copyGame = game.copyBoard(); copyGame.play(i); // play the move on a copy board @@ -84,7 +84,7 @@ public class MinMaxTicTacToe { else { // it's the minimizing players turn, the player int bestVal = 100; // set the value to the highest possible - for (int i = 0; i < game.getGrid().length; i++) { // loop through the grid + for (int i = 0; i < game.grid.length; i++) { // loop through the grid if (game.validateMove(i)) { TicTacToe copyGame = game.copyBoard(); copyGame.play(i); // play the move on a copy board diff --git a/src/main/java/org/toop/game/tictactoe/TicTacToe.java b/src/main/java/org/toop/game/tictactoe/TicTacToe.java index 35dce99..671e361 100644 --- a/src/main/java/org/toop/game/tictactoe/TicTacToe.java +++ b/src/main/java/org/toop/game/tictactoe/TicTacToe.java @@ -1,15 +1,48 @@ package org.toop.game.tictactoe; import org.toop.game.*; +import org.toop.server.backend.tictactoe.*; -public class TicTacToe extends GameBase { - private int movesLeft; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class TicTacToe extends GameBase implements Runnable { + public Thread gameThread; + public BlockingQueue commandQueue = new LinkedBlockingQueue<>(); + public BlockingQueue sendQueue = new LinkedBlockingQueue<>(); + + public int movesLeft; public TicTacToe(String player1, String player2) { super(3, new Player(player1, 'X'), new Player(player2, 'O')); movesLeft = size * size; } + public void addCommandToQueue(ParsedCommand command) { + commandQueue.add(command); + } + + @Override + public void run() { + this.gameThread = new Thread(this::gameThread); + this.gameThread.start(); + } + + private void gameThread() { + while (true) { +// String command = getNewestCommand(); +// command = this.parseCommand(command).toString(); +// if (command == null) { continue; } + + if (commandQueue.poll() == null) { + continue; + } + + // TODO: Game use the commandQueue to get the commands. + } + + } + @Override public State play(int index) { if (!validateMove(index)) { @@ -74,14 +107,14 @@ public class TicTacToe extends GameBase { movesLeft--; } - public TicTacToe copyBoard() { - /** - * This method copies the board, mainly for AI use. - */ - TicTacToe clone = new TicTacToe(players[0].name(), players[1].name()); - System.arraycopy(this.grid, 0, clone.grid, 0, this.grid.length); - clone.movesLeft = this.movesLeft; - clone.currentPlayer = this.currentPlayer; - return clone; - } + public TicTacToe copyBoard() { + /** + * This method copies the board, mainly for AI use. + */ + TicTacToe clone = new TicTacToe(players[0].name(), players[1].name()); + System.arraycopy(this.grid, 0, clone.grid, 0, this.grid.length); + clone.movesLeft = this.movesLeft; + clone.currentPlayer = this.currentPlayer; + return clone; + } } diff --git a/src/main/java/org/toop/server/backend/ServerManager.java b/src/main/java/org/toop/server/backend/ServerManager.java index b18bab2..f0f487e 100644 --- a/src/main/java/org/toop/server/backend/ServerManager.java +++ b/src/main/java/org/toop/server/backend/ServerManager.java @@ -5,9 +5,11 @@ import org.toop.eventbus.GlobalEventBus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.toop.server.backend.tictactoe.TicTacToeServer; import java.util.ArrayList; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -29,12 +31,23 @@ public class ServerManager { GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartServerRequest.class, this::handleStartServerRequest); GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartServer.class, this::handleStartServer); GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ForceCloseAllServers.class, _ -> shutdownAll()); + GlobalEventBus.subscribeAndRegister(Events.ServerEvents.CreateTicTacToeGameRequest.class, this::handleStartTicTacToeGameOnAServer); + GlobalEventBus.subscribeAndRegister(Events.ServerEvents.RunTicTacToeGame.class, this::handleRunTicTacToeGameOnAServer); + GlobalEventBus.subscribeAndRegister(Events.ServerEvents.EndTicTacToeGame.class, this::handleEndTicTacToeGameOnAServer); } - private String startServer(String port) { + private String startServer(String port, String gameType) { String serverId = UUID.randomUUID().toString(); + gameType = gameType.toLowerCase(); try { - TcpServer server = new TcpServer(Integer.parseInt(port)); + TcpServer server = null; + if (Objects.equals(gameType, "tictactoe")) { + server = new TicTacToeServer(Integer.parseInt(port)); + } + else { + logger.error("Manager could not create a TcpServer for {}", gameType); + return null; + } this.servers.put(serverId, server); new Thread(server, "Server-" + serverId).start(); logger.info("Connected to server {} at {}", serverId, port); @@ -46,16 +59,41 @@ public class ServerManager { } private void handleStartServerRequest(Events.ServerEvents.StartServerRequest request) { - request.future().complete(this.startServer(request.port())); // TODO: Maybe post StartServer event. + request.future().complete(this.startServer(request.port(), request.gameType())); // TODO: Maybe post StartServer event. } private void handleStartServer(Events.ServerEvents.StartServer event) { GlobalEventBus.post(new Events.ServerEvents.ServerStarted( - this.startServer(event.port()), + this.startServer(event.port(), event.gameType()), event.port() )); } + private void handleStartTicTacToeGameOnAServer(Events.ServerEvents.CreateTicTacToeGameRequest event) { + TicTacToeServer serverThing = (TicTacToeServer) this.servers.get(event.serverUuid()); + String gameId = null; + if (serverThing != null) { + try { + gameId = serverThing.newGame(event.playerA(), event.playerB()); + logger.info("Created game on server: {}", event.serverUuid()); + } + catch (Exception e) { // TODO: Error handling + logger.error("Could not create game on server: {}", event.serverUuid()); + } + } else { logger.warn("Could not find server: {}", event.serverUuid()); } + event.future().complete(gameId); + } + + private void handleRunTicTacToeGameOnAServer(Events.ServerEvents.RunTicTacToeGame event) { + TicTacToeServer gameServer = (TicTacToeServer) this.servers.get(event.serverUuid()); + gameServer.runGame(event.gameUuid()); + } + + private void handleEndTicTacToeGameOnAServer(Events.ServerEvents.EndTicTacToeGame event) { + TicTacToeServer gameServer = (TicTacToeServer) this.servers.get(event.serverUuid()); + gameServer.endGame(event.gameUuid()); + } + private void getAllServers(Events.ServerEvents.RequestsAllServers request) { ArrayList a = new ArrayList<>(this.servers.values()); request.future().complete(a.toString()); diff --git a/src/main/java/org/toop/server/backend/TcpServer.java b/src/main/java/org/toop/server/backend/TcpServer.java index 3bbeedb..d5e5525 100644 --- a/src/main/java/org/toop/server/backend/TcpServer.java +++ b/src/main/java/org/toop/server/backend/TcpServer.java @@ -2,23 +2,26 @@ package org.toop.server.backend; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.toop.server.backend.tictactoe.ParsedCommand; import java.io.*; import java.net.*; -import java.sql.Time; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.*; -import static java.lang.Thread.sleep; - public class TcpServer implements Runnable { protected static final Logger logger = LogManager.getLogger(TcpServer.class); private final ExecutorService executor = Executors.newFixedThreadPool(2); - private final BlockingQueue receivedQueue = new LinkedBlockingQueue<>(); - private final BlockingQueue sendQueue = new LinkedBlockingQueue<>(); - private final int WAIT_TIME = 500; // MS - private final int RETRY_ATTEMPTS = 3; + public final BlockingQueue receivedQueue = new LinkedBlockingQueue<>(); + public final BlockingQueue commandQueue = new LinkedBlockingQueue<>(); + public final BlockingQueue sendQueue = new LinkedBlockingQueue<>(); + public final Map knownPlayers = new HashMap<>(); + public final Map playersGames = new HashMap<>(); + public final int WAIT_TIME = 500; // MS + public final int RETRY_ATTEMPTS = 3; protected int port; protected ServerSocket serverSocket = null; @@ -49,17 +52,37 @@ public class TcpServer implements Runnable { } } - protected String getNewestCommand() { - try { return receivedQueue.poll(this.WAIT_TIME, TimeUnit.MILLISECONDS); } + public void runGame() {} + + public void endGame() {} + + public void newGame() {} + + protected String sendServerMessage() { + try { return sendQueue.poll(this.WAIT_TIME, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { logger.error("Interrupted", e); return null; } } - protected void sendMessage(String message) throws InterruptedException { - sendQueue.put(message); + protected ParsedCommand getNewestCommand() { + try { + String rec = receivedQueue.poll(this.WAIT_TIME, TimeUnit.MILLISECONDS); + if (rec != null) { + return new ParsedCommand(rec); + } + } + catch (InterruptedException e) { + logger.error("Interrupted", e); + return null; + } + return null; } +// +// protected void sendMessage(String message) throws InterruptedException { +// sendQueue.put(message); +// } protected void startWorkers(Socket clientSocket) { running = true; @@ -95,11 +118,6 @@ public class TcpServer implements Runnable { new Thread(() -> { for (int i = 0; i < this.RETRY_ATTEMPTS; i++) { if (this.receivedQueue.offer(finalMessage)) break; - try { - sleep(this.WAIT_TIME); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } } }).start(); } diff --git a/src/main/java/org/toop/server/backend/Testsss.java b/src/main/java/org/toop/server/backend/Testsss.java deleted file mode 100644 index af00056..0000000 --- a/src/main/java/org/toop/server/backend/Testsss.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.toop.server.backend; - -import org.toop.eventbus.Events; -import org.toop.eventbus.GlobalEventBus; - -public class Testsss extends Thread { - - public Testsss() {} - -// public void run() { -// while (true) { -// try { -// sleep(100); -// GlobalEventBus.post(new Events.ServerEvents.command("HELP", "TEST")); -// sleep(1000); -// GlobalEventBus.post(new Events.ServerEvents.ChangeConnection("127.0.0.1", "5001")); -// } catch (InterruptedException e) { -// throw new RuntimeException(e); -// } -// } -// } - - public static void start(String keepEmpty) { - new Testsss().start(); - } - -} diff --git a/src/main/java/org/toop/server/backend/tictactoe/ParsedCommand.java b/src/main/java/org/toop/server/backend/tictactoe/ParsedCommand.java new file mode 100644 index 0000000..b22a5f6 --- /dev/null +++ b/src/main/java/org/toop/server/backend/tictactoe/ParsedCommand.java @@ -0,0 +1,144 @@ +package org.toop.server.backend.tictactoe; + +import java.util.ArrayList; + +public class ParsedCommand { + public TicTacToeServerCommand command; + public ArrayList arguments; + public boolean isValidCommand; + public boolean isServerCommand; + public TicTacToeServerMessage returnMessage; + public String errorMessage; + public String originalCommand; + + public ParsedCommand(String receivedCommand) { + + if (receivedCommand.isEmpty()) { + this.command = null; + this.arguments = null; + this.isValidCommand = false; + this.isServerCommand = true; + this.returnMessage = TicTacToeServerMessage.ERR; + this.errorMessage = "The received command is empty"; + this.originalCommand = receivedCommand; + return; + } + + String[] segments = receivedCommand.split(" "); + if (segments[0].isEmpty()) { + this.command = null; + this.arguments = null; + this.isValidCommand = false; + this.isServerCommand = true; + this.returnMessage = TicTacToeServerMessage.ERR; + this.errorMessage = "The received command is empty or couldn't be split"; + this.originalCommand = receivedCommand; + return; + } + + TicTacToeServerCommand commandEnum = TicTacToeServerCommand.getCommand(segments[0]); + + switch (commandEnum) { + case MOVE -> { + if (segments.length == 2 && !segments[1].isEmpty()) { + this.command = commandEnum; + this.arguments = new ArrayList<>(1); + this.arguments.add(segments[1]); + this.returnMessage = TicTacToeServerMessage.OK; + this.isValidCommand = true; + this.isServerCommand = false; + this.errorMessage = null; + this.originalCommand = receivedCommand; + return; + } + } + case MESSAGE -> { + if (segments.length == 3 && !segments[2].isEmpty()) { + this.command = commandEnum; + this.arguments = new ArrayList<>(2); + this.arguments.add(segments[2]); + this.returnMessage = TicTacToeServerMessage.OK; + this.isValidCommand = true; + this.isServerCommand = true; + this.errorMessage = null; + this.originalCommand = receivedCommand; + return; + } + } + case CHALLENGE -> { + if (!segments[1].isEmpty() && segments[1].equals("accept") && + !segments[2].isEmpty()) { + this.command = commandEnum; + this.arguments = new ArrayList<>(2); + this.arguments.add(segments[1]); + this.arguments.add(segments[2]); // TODO: Needs to be a number. + this.returnMessage = TicTacToeServerMessage.OK; + this.isValidCommand = true; + this.isServerCommand = true; + this.errorMessage = null; + this.originalCommand = receivedCommand; + return; + } else { + this.command = commandEnum; + this.arguments = null; + this.returnMessage = TicTacToeServerMessage.ERR; + this.isValidCommand = false; + this.isServerCommand = true; + this.errorMessage = "The challenge was not parsable"; + this.originalCommand = receivedCommand; + return; + } + } + case LOGIN -> { // TODO: Challenge needs to accept different game types later. + if (!segments[1].isEmpty()) { + this.command = commandEnum; + this.arguments = new ArrayList<>(1); + this.arguments.add(segments[1]); + this.returnMessage = TicTacToeServerMessage.OK; + this.isValidCommand = true; + this.isServerCommand = true; + this.errorMessage = null; + this.originalCommand = receivedCommand; + return; + } else { + this.command = commandEnum; + this.arguments = null; + this.returnMessage = TicTacToeServerMessage.ERR; + this.isValidCommand = false; + this.isServerCommand = true; + this.errorMessage = "The received name is empty or couldn't be understood"; + this.originalCommand = receivedCommand; + return; + } + } +// case GET -> { // TODO: Get needs to accept different game types later. And get the players +// +// } + case BYE, DISCONNECT, LOGOUT, QUIT, EXIT, FORFEIT, SUBSCRIBE -> { + this.command = commandEnum; + this.arguments = null; + this.returnMessage = TicTacToeServerMessage.OK; + this.isValidCommand = true; + this.isServerCommand = true; + this.errorMessage = null; + this.originalCommand = receivedCommand; + return; + } + case null, default -> { + this.command = null; + this.arguments = null; + this.returnMessage = TicTacToeServerMessage.ERR; + this.isValidCommand = false; + this.isServerCommand = true; + this.errorMessage = segments[0] + " is not a supported command"; + this.originalCommand = receivedCommand; + return; + } + } + } +// +// public ParsedCommand parseCommand(String command) { +// return null; +// } + +} \ No newline at end of file diff --git a/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java b/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java index 5b5f92c..64beae2 100644 --- a/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java +++ b/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java @@ -1,22 +1,26 @@ package org.toop.server.backend.tictactoe; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.toop.game.tictactoe.*; import org.toop.server.backend.TcpServer; import java.io.IOException; -import java.io.ObjectStreamException; import java.net.Socket; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; public class TicTacToeServer extends TcpServer { - private TicTacToe game; + protected static final Logger logger = LogManager.getLogger(TicTacToeServer.class); + /** + * Map of gameId -> Game instances + */ + private final Map games = new ConcurrentHashMap<>(); - public TicTacToeServer(int port, String playerA, String playerB) throws IOException { + public TicTacToeServer(int port) throws IOException { super(port); - this.game = new TicTacToe(playerA, playerB); } @Override @@ -29,109 +33,75 @@ public class TicTacToeServer extends TcpServer { logger.info("Connected to client: {}", clientSocket.getInetAddress()); new Thread(() -> this.startWorkers(clientSocket)).start(); - new Thread(() -> this.gameThread()).start(); + new Thread(this::gameManagerThread).start(); } } catch (IOException e) { e.printStackTrace(); } } - private static class ParsedCommand { - public TicTacToeServerCommand command; - public ArrayList arguments; - public boolean isValidCommand; - public TicTacToeServerMessage returnMessage; - public String errorMessage; - public String originalCommand; - - ParsedCommand(String receivedCommand) { - - if (receivedCommand.isEmpty()) { - this.command = null; - this.arguments = null; - this.isValidCommand = false; - this.returnMessage = TicTacToeServerMessage.ERR; - this.errorMessage = "The received command is empty"; - this.originalCommand = receivedCommand; - return; + @Override + protected ParsedCommand getNewestCommand() { + try { + String rec = receivedQueue.poll(this.WAIT_TIME, TimeUnit.MILLISECONDS); + if (rec != null) { + return new ParsedCommand(rec); } - - String[] segments = receivedCommand.split(" "); - if (segments[0].isEmpty()) { - this.command = null; - this.arguments = null; - this.isValidCommand = false; - this.returnMessage = TicTacToeServerMessage.ERR; - this.errorMessage = "The received command is empty or couldn't be split"; - this.originalCommand = receivedCommand; - return; - }; - - TicTacToeServerCommand commandEnum = TicTacToeServerCommand.getCommand(segments[0]); - switch (commandEnum) { - case MOVE -> { - if (segments.length == 2 && !segments[1].isEmpty()) { - this.command = commandEnum; - this.arguments = new ArrayList(1); - this.arguments.add(segments[1]); - this.returnMessage = TicTacToeServerMessage.OK; - this.isValidCommand = true; - this.errorMessage = null; - this.originalCommand = receivedCommand; - return; - } - } - case FORFEIT -> { - this.command = commandEnum; - this.arguments = null; - this.returnMessage = TicTacToeServerMessage.OK; - this.isValidCommand = true; - this.errorMessage = null; - this.originalCommand = receivedCommand; - return; - } - case MESSAGE -> { - if (segments.length == 3 && !segments[2].isEmpty()) { - this.command = commandEnum; - this.arguments = new ArrayList(2); - this.arguments.add(segments[2]); - this.returnMessage = TicTacToeServerMessage.OK; - this.isValidCommand = true; - this.errorMessage = null; - this.originalCommand = receivedCommand; - return; - } - } - case BYE, DISCONNECT, LOGOUT, QUIT, EXIT -> { - this.command = commandEnum; - this.arguments = null; - this.returnMessage = TicTacToeServerMessage.OK; - this.isValidCommand = true; - this.errorMessage = null; - this.originalCommand = receivedCommand; - return; - } - } - this.command = command; - this.arguments = arguments; } - } - - private ParsedCommand parseCommand(String command) { + catch (InterruptedException e) { + logger.error("Interrupted", e); + return null; + } return null; } - private void gameThread() { - - - while (true) { - String command = getNewestCommand(); - command = this.parseCommand(command).toString(); - if (command == null) { continue; } - - // TODO: Game + public void gameManagerThread() { + while (true) { // TODO: Very heavy on thread + try { + synchronized (this) { + wait(250); + } // Fixes current thread is not owner. + } catch (InterruptedException e) { + logger.error("Interrupted", e); + } + ParsedCommand command = getNewestCommand(); + if (command != null && !command.isServerCommand) { + TicTacToe testGame = games.values().iterator().next(); // TODO: Is to get first for testing, must be done a different way later. + testGame.addCommandToQueue(command); + logger.info("Added command to the game queue: {}", command); + return; + } } + } + public String newGame(String playerA, String playerB) { + logger.info("Creating a new game: {} vs {}", playerA, playerB); + String gameId = UUID.randomUUID().toString(); + TicTacToe game = new TicTacToe(playerA, playerB); + this.games.put(gameId, game); +// this.knownPlayers.put(sockA, playerA); // TODO: For remembering players and validation. +// this.knownPlayers.put(sockB, playerB); +// this.playersGames.put(playerA, gameId); +// this.playersGames.put(playerB, gameId); + logger.info("Created a new game: {}. {} vs {}", gameId, playerA, playerB); + return gameId; + } + + public void runGame(String gameId) { + TicTacToe game = this.games.get(gameId); + game.run(); + logger.info("Running game: {}, players: {}", gameId, game.getPlayers()); + } + + public void endGame(String gameId) { + TicTacToe game = this.games.get(gameId); + this.games.remove(gameId); +// this.knownPlayers.put(sockA, playerA); // TODO: Remove players when game is done. +// this.knownPlayers.put(sockB, playerB); +// this.playersGames.put(playerA, gameId); +// this.playersGames.put(playerB, gameId); + logger.info("Ended game: {}", gameId); + // TODO: Multithreading, close game in a graceful matter, etc. } } diff --git a/src/test/java/MinMaxTicTacToeTest.java b/src/test/java/MinMaxTicTacToeTest.java index a7325e5..e2f3901 100644 --- a/src/test/java/MinMaxTicTacToeTest.java +++ b/src/test/java/MinMaxTicTacToeTest.java @@ -12,11 +12,10 @@ // // Fill the board // for (int i = 0; i < board.length(); i++) { // char c = board.charAt(i); -// game.play(i); -// game.setGridAt(i, c); -// if (c != ' ') game.decrementMovesLeft(); +// game.grid[i] = c; +// if (c != ' ') game.movesLeft--; // } -// game.setCurrentPlayer(currentPlayer); +// game.currentPlayer = currentPlayer; // return game; // } //