From 66cb000fad3f480582ba8c24eff1acab228379d6 Mon Sep 17 00:00:00 2001 From: Bas de Jong Date: Fri, 12 Dec 2025 15:15:55 +0100 Subject: [PATCH 01/35] Init server code --- app/src/main/java/org/toop/app/Server.java | 16 +-- .../org/toop/app/canvas/ReversiBitCanvas.java | 5 +- .../toop/app/canvas/TicTacToeBitCanvas.java | 5 +- .../GenericGameController.java | 4 +- .../gameControllers/ReversiBitController.java | 8 +- .../TicTacToeBitController.java | 9 +- .../app/widget/view/LocalMultiplayerView.java | 18 +-- framework/pom.xml | 1 - .../java/org/toop/framework}/game/Move.java | 2 +- .../LocalFixedRateThreadBehaviour.java | 2 +- .../gameThreads/LocalThreadBehaviour.java | 2 +- .../gameThreads/OnlineThreadBehaviour.java | 4 +- .../OnlineWithSleepThreadBehaviour.java | 3 +- .../framework}/game/players/LocalPlayer.java | 3 +- .../framework}/game/players/MiniMaxAI.java | 2 +- .../framework}/game/players/OnlinePlayer.java | 2 +- .../framework}/game/players/RandomAI.java | 2 +- .../controller/GameController.java | 3 +- .../model/game/SupportsOnlinePlay.java | 2 - .../NetworkingClientEventListener.java | 8 +- .../NetworkingClientManager.java | 14 +-- .../clients/TournamentNetworkingClient.java | 8 +- .../events/NetworkEvents.java | 6 +- .../exceptions/ClientNotFoundException.java | 2 +- .../exceptions/CouldNotConnectException.java | 2 +- .../NetworkingInitializationException.java | 2 +- .../handlers/NetworkingGameClientHandler.java | 4 +- .../interfaces/NetworkingClient.java | 4 +- .../interfaces/NetworkingClientManager.java | 8 +- .../types/NetworkingConnector.java | 2 +- .../{ => connection}/types/ServerCommand.java | 2 +- .../connection/types/ServerMessage.java | 3 + .../framework/networking/server/Game.java | 30 +++++ .../networking/server/GameDefinition.java | 26 ++++ .../networking/server/GameServer.java | 8 ++ .../networking/server/MasterServer.java | 68 ++++++++++ .../networking/server/MessageStore.java | 7 ++ .../networking/server/OnlineGame.java | 7 ++ .../networking/server/ParsedMessage.java | 4 + .../framework/networking/server/Parser.java | 4 + .../networking/server/ServableGame.java | 4 + .../framework/networking/server/Server.java | 51 ++++++++ .../networking/server/ServerHandler.java | 117 ++++++++++++++++++ .../networking/server/ServerMessageStore.java | 27 ++++ .../networking/server/ServerUser.java | 9 ++ .../framework/networking/server/User.java | 40 ++++++ .../networking/types/ServerMessage.java | 3 - .../NetworkingClientManagerTest.java | 2 +- .../events/NetworkEventsTest.java | 2 +- 49 files changed, 475 insertions(+), 92 deletions(-) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/Move.java (77%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/gameThreads/LocalFixedRateThreadBehaviour.java (98%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/gameThreads/LocalThreadBehaviour.java (98%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/gameThreads/OnlineThreadBehaviour.java (96%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/gameThreads/OnlineWithSleepThreadBehaviour.java (91%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/players/LocalPlayer.java (96%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/players/MiniMaxAI.java (99%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/players/OnlinePlayer.java (96%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/players/RandomAI.java (95%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/NetworkingClientEventListener.java (96%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/NetworkingClientManager.java (90%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/clients/TournamentNetworkingClient.java (92%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/events/NetworkEvents.java (97%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/exceptions/ClientNotFoundException.java (91%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/exceptions/CouldNotConnectException.java (89%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/exceptions/NetworkingInitializationException.java (76%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/handlers/NetworkingGameClientHandler.java (98%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/interfaces/NetworkingClient.java (66%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/interfaces/NetworkingClientManager.java (57%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/types/NetworkingConnector.java (73%) rename framework/src/main/java/org/toop/framework/networking/{ => connection}/types/ServerCommand.java (52%) create mode 100644 framework/src/main/java/org/toop/framework/networking/connection/types/ServerMessage.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/Game.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/GameServer.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/MasterServer.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/MessageStore.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/ParsedMessage.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/Parser.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/ServableGame.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/Server.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/ServerUser.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/User.java delete mode 100644 framework/src/main/java/org/toop/framework/networking/types/ServerMessage.java rename framework/src/test/java/org/toop/framework/networking/{ => connection}/events/NetworkEventsTest.java (99%) diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index ef691b4..90040ef 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -14,14 +14,14 @@ import org.toop.framework.eventbus.EventFlow; import org.toop.framework.gameFramework.controller.GameController; import org.toop.framework.eventbus.GlobalEventBus; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.framework.networking.clients.TournamentNetworkingClient; -import org.toop.framework.networking.events.NetworkEvents; -import org.toop.framework.networking.types.NetworkingConnector; -import org.toop.game.games.reversi.BitboardReversi; -import org.toop.game.games.tictactoe.BitboardTicTacToe; -import org.toop.game.players.ArtificialPlayer; -import org.toop.game.players.OnlinePlayer; -import org.toop.game.players.RandomAI; +import org.toop.framework.networking.connection.clients.TournamentNetworkingClient; +import org.toop.framework.networking.connection.events.NetworkEvents; +import org.toop.framework.networking.connection.types.NetworkingConnector; +import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; +import org.toop.framework.game.players.ArtificialPlayer; +import org.toop.framework.game.players.OnlinePlayer; +import org.toop.framework.game.players.RandomAI; import org.toop.local.AppContext; import java.util.List; diff --git a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java index 7c2bde0..99b4589 100644 --- a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java @@ -2,10 +2,7 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; -import org.toop.game.games.reversi.BitboardReversi; - -import java.util.Arrays; -import java.util.function.Consumer; +import org.toop.framework.game.games.reversi.BitboardReversi; public class ReversiBitCanvas extends BitGameCanvas { public ReversiBitCanvas() { diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java index 443adbd..6c723cb 100644 --- a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java @@ -2,10 +2,7 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; -import org.toop.game.games.tictactoe.BitboardTicTacToe; - -import java.util.Arrays; -import java.util.function.Consumer; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; public class TicTacToeBitCanvas extends BitGameCanvas{ public TicTacToeBitCanvas() { diff --git a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java index 2c3ad49..80e998f 100644 --- a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java +++ b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java @@ -15,8 +15,8 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; import org.toop.framework.gameFramework.model.player.Player; import org.toop.framework.gameFramework.view.GUIEvents; -import org.toop.framework.networking.events.NetworkEvents; -import org.toop.game.players.LocalPlayer; +import org.toop.framework.networking.connection.events.NetworkEvents; +import org.toop.framework.game.players.LocalPlayer; public class GenericGameController> implements GameController { protected final EventFlow eventFlow = new EventFlow(); diff --git a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java index 40784b0..2f1f3c2 100644 --- a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java @@ -3,10 +3,10 @@ package org.toop.app.gameControllers; import org.toop.app.canvas.ReversiBitCanvas; import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.gameThreads.LocalThreadBehaviour; -import org.toop.game.gameThreads.OnlineThreadBehaviour; -import org.toop.game.games.reversi.BitboardReversi; -import org.toop.game.players.OnlinePlayer; +import org.toop.framework.game.gameThreads.LocalThreadBehaviour; +import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; +import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.game.players.OnlinePlayer; public class ReversiBitController extends GenericGameController { public ReversiBitController(Player[] players) { diff --git a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java index 6307894..b874382 100644 --- a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java @@ -3,11 +3,10 @@ package org.toop.app.gameControllers; import org.toop.app.canvas.TicTacToeBitCanvas; import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.gameThreads.LocalFixedRateThreadBehaviour; -import org.toop.game.gameThreads.LocalThreadBehaviour; -import org.toop.game.gameThreads.OnlineThreadBehaviour; -import org.toop.game.games.tictactoe.BitboardTicTacToe; -import org.toop.game.players.OnlinePlayer; +import org.toop.framework.game.gameThreads.LocalThreadBehaviour; +import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; +import org.toop.framework.game.players.OnlinePlayer; public class TicTacToeBitController extends GenericGameController { public TicTacToeBitController(Player[] players) { diff --git a/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java b/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java index 9b9bed2..d84b074 100644 --- a/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java +++ b/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java @@ -2,24 +2,21 @@ package org.toop.app.widget.view; import javafx.application.Platform; import org.toop.app.GameInformation; -import org.toop.app.canvas.ReversiBitCanvas; -import org.toop.app.canvas.TicTacToeBitCanvas; -import org.toop.app.gameControllers.GenericGameController; import org.toop.app.gameControllers.ReversiBitController; import org.toop.app.gameControllers.TicTacToeBitController; import org.toop.framework.gameFramework.controller.GameController; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.games.reversi.BitboardReversi; -import org.toop.game.games.tictactoe.BitboardTicTacToe; -import org.toop.game.players.ArtificialPlayer; -import org.toop.game.players.LocalPlayer; +import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; +import org.toop.framework.game.players.ArtificialPlayer; +import org.toop.framework.game.players.LocalPlayer; import org.toop.app.widget.Primitive; import org.toop.app.widget.complex.PlayerInfoWidget; import org.toop.app.widget.complex.ViewWidget; import org.toop.app.widget.popup.ErrorPopup; import org.toop.app.widget.tutorial.*; -import org.toop.game.players.MiniMaxAI; -import org.toop.game.players.RandomAI; +import org.toop.framework.game.players.MiniMaxAI; +import org.toop.framework.game.players.RandomAI; import org.toop.local.AppContext; import javafx.geometry.Pos; @@ -27,9 +24,6 @@ import javafx.scene.control.ScrollPane; import javafx.scene.layout.VBox; import org.toop.local.AppSettings; -import java.util.Arrays; -import java.util.Random; - public class LocalMultiplayerView extends ViewWidget { private final GameInformation information; diff --git a/framework/pom.xml b/framework/pom.xml index b5796c5..9d2055f 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -147,7 +147,6 @@ 2.42.0 - diff --git a/game/src/main/java/org/toop/game/Move.java b/framework/src/main/java/org/toop/framework/game/Move.java similarity index 77% rename from game/src/main/java/org/toop/game/Move.java rename to framework/src/main/java/org/toop/framework/game/Move.java index 62a2b1a..cd1401b 100644 --- a/game/src/main/java/org/toop/game/Move.java +++ b/framework/src/main/java/org/toop/framework/game/Move.java @@ -1,3 +1,3 @@ -package org.toop.game; +package org.toop.framework.game; // TODO: Remove this, only used in ReversiCanvas. Needs to not public record Move(int position, char value) {} diff --git a/game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java b/framework/src/main/java/org/toop/framework/game/gameThreads/LocalFixedRateThreadBehaviour.java similarity index 98% rename from game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java rename to framework/src/main/java/org/toop/framework/game/gameThreads/LocalFixedRateThreadBehaviour.java index 0a9da7f..1104511 100644 --- a/game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java +++ b/framework/src/main/java/org/toop/framework/game/gameThreads/LocalFixedRateThreadBehaviour.java @@ -1,4 +1,4 @@ -package org.toop.game.gameThreads; +package org.toop.framework.game.gameThreads; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.gameFramework.GameState; diff --git a/game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java b/framework/src/main/java/org/toop/framework/game/gameThreads/LocalThreadBehaviour.java similarity index 98% rename from game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java rename to framework/src/main/java/org/toop/framework/game/gameThreads/LocalThreadBehaviour.java index 79c57f9..dc5d400 100644 --- a/game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java +++ b/framework/src/main/java/org/toop/framework/game/gameThreads/LocalThreadBehaviour.java @@ -1,4 +1,4 @@ -package org.toop.game.gameThreads; +package org.toop.framework.game.gameThreads; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.gameFramework.model.game.threadBehaviour.AbstractThreadBehaviour; diff --git a/game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java b/framework/src/main/java/org/toop/framework/game/gameThreads/OnlineThreadBehaviour.java similarity index 96% rename from game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java rename to framework/src/main/java/org/toop/framework/game/gameThreads/OnlineThreadBehaviour.java index ae9aa88..5c53bd9 100644 --- a/game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java +++ b/framework/src/main/java/org/toop/framework/game/gameThreads/OnlineThreadBehaviour.java @@ -1,4 +1,4 @@ -package org.toop.game.gameThreads; +package org.toop.framework.game.gameThreads; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.gameFramework.model.game.threadBehaviour.AbstractThreadBehaviour; @@ -6,7 +6,7 @@ import org.toop.framework.gameFramework.view.GUIEvents; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.game.SupportsOnlinePlay; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.players.OnlinePlayer; +import org.toop.framework.game.players.OnlinePlayer; /** * Handles online multiplayer game logic. diff --git a/game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java b/framework/src/main/java/org/toop/framework/game/gameThreads/OnlineWithSleepThreadBehaviour.java similarity index 91% rename from game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java rename to framework/src/main/java/org/toop/framework/game/gameThreads/OnlineWithSleepThreadBehaviour.java index a666f8d..6ba3351 100644 --- a/game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java +++ b/framework/src/main/java/org/toop/framework/game/gameThreads/OnlineWithSleepThreadBehaviour.java @@ -1,7 +1,6 @@ -package org.toop.game.gameThreads; +package org.toop.framework.game.gameThreads; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import org.toop.framework.networking.events.NetworkEvents; /** * Online thread behaviour that adds a fixed delay before processing diff --git a/game/src/main/java/org/toop/game/players/LocalPlayer.java b/framework/src/main/java/org/toop/framework/game/players/LocalPlayer.java similarity index 96% rename from game/src/main/java/org/toop/game/players/LocalPlayer.java rename to framework/src/main/java/org/toop/framework/game/players/LocalPlayer.java index 8f3b94d..3bbfe97 100644 --- a/game/src/main/java/org/toop/game/players/LocalPlayer.java +++ b/framework/src/main/java/org/toop/framework/game/players/LocalPlayer.java @@ -1,8 +1,7 @@ -package org.toop.game.players; +package org.toop.framework.game.players; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.AbstractPlayer; -import org.toop.framework.gameFramework.model.player.Player; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; diff --git a/game/src/main/java/org/toop/game/players/MiniMaxAI.java b/framework/src/main/java/org/toop/framework/game/players/MiniMaxAI.java similarity index 99% rename from game/src/main/java/org/toop/game/players/MiniMaxAI.java rename to framework/src/main/java/org/toop/framework/game/players/MiniMaxAI.java index 440bb50..de67068 100644 --- a/game/src/main/java/org/toop/game/players/MiniMaxAI.java +++ b/framework/src/main/java/org/toop/framework/game/players/MiniMaxAI.java @@ -1,4 +1,4 @@ -package org.toop.game.players; +package org.toop.framework.game.players; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.PlayResult; diff --git a/game/src/main/java/org/toop/game/players/OnlinePlayer.java b/framework/src/main/java/org/toop/framework/game/players/OnlinePlayer.java similarity index 96% rename from game/src/main/java/org/toop/game/players/OnlinePlayer.java rename to framework/src/main/java/org/toop/framework/game/players/OnlinePlayer.java index 9f011c0..60a45da 100644 --- a/game/src/main/java/org/toop/game/players/OnlinePlayer.java +++ b/framework/src/main/java/org/toop/framework/game/players/OnlinePlayer.java @@ -1,4 +1,4 @@ -package org.toop.game.players; +package org.toop.framework.game.players; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.AbstractPlayer; diff --git a/game/src/main/java/org/toop/game/players/RandomAI.java b/framework/src/main/java/org/toop/framework/game/players/RandomAI.java similarity index 95% rename from game/src/main/java/org/toop/game/players/RandomAI.java rename to framework/src/main/java/org/toop/framework/game/players/RandomAI.java index 2d0fe02..f7006fb 100644 --- a/game/src/main/java/org/toop/game/players/RandomAI.java +++ b/framework/src/main/java/org/toop/framework/game/players/RandomAI.java @@ -1,4 +1,4 @@ -package org.toop.game.players; +package org.toop.framework.game.players; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.AbstractAI; diff --git a/framework/src/main/java/org/toop/framework/gameFramework/controller/GameController.java b/framework/src/main/java/org/toop/framework/gameFramework/controller/GameController.java index f355dc5..5591d25 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/controller/GameController.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/controller/GameController.java @@ -1,8 +1,7 @@ package org.toop.framework.gameFramework.controller; -import org.toop.framework.gameFramework.model.game.SupportsOnlinePlay; import org.toop.framework.gameFramework.model.game.threadBehaviour.Controllable; -import org.toop.framework.networking.events.NetworkEvents; +import org.toop.framework.networking.connection.events.NetworkEvents; public interface GameController extends Controllable, UpdatesGameUI { /** Called when it is this player's turn to make a move. */ diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/game/SupportsOnlinePlay.java b/framework/src/main/java/org/toop/framework/gameFramework/model/game/SupportsOnlinePlay.java index 1cc1641..965056e 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/game/SupportsOnlinePlay.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/game/SupportsOnlinePlay.java @@ -1,7 +1,5 @@ package org.toop.framework.gameFramework.model.game; -import org.toop.framework.networking.events.NetworkEvents; - /** * Interface for games that support online multiplayer play. *

diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingClientEventListener.java b/framework/src/main/java/org/toop/framework/networking/connection/NetworkingClientEventListener.java similarity index 96% rename from framework/src/main/java/org/toop/framework/networking/NetworkingClientEventListener.java rename to framework/src/main/java/org/toop/framework/networking/connection/NetworkingClientEventListener.java index 5a9a08d..05e9bc8 100644 --- a/framework/src/main/java/org/toop/framework/networking/NetworkingClientEventListener.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/NetworkingClientEventListener.java @@ -1,13 +1,13 @@ -package org.toop.framework.networking; +package org.toop.framework.networking.connection; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.framework.SnowflakeGenerator; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.bus.EventBus; -import org.toop.framework.networking.events.NetworkEvents; -import org.toop.framework.networking.exceptions.ClientNotFoundException; -import org.toop.framework.networking.interfaces.NetworkingClientManager; +import org.toop.framework.networking.connection.events.NetworkEvents; +import org.toop.framework.networking.connection.exceptions.ClientNotFoundException; +import org.toop.framework.networking.connection.interfaces.NetworkingClientManager; public class NetworkingClientEventListener { private static final Logger logger = LogManager.getLogger(NetworkingClientEventListener.class); diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java b/framework/src/main/java/org/toop/framework/networking/connection/NetworkingClientManager.java similarity index 90% rename from framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java rename to framework/src/main/java/org/toop/framework/networking/connection/NetworkingClientManager.java index 4f1d2ac..1bd0643 100644 --- a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/NetworkingClientManager.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking; +package org.toop.framework.networking.connection; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -10,13 +10,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.bus.EventBus; -import org.toop.framework.networking.events.NetworkEvents; -import org.toop.framework.networking.exceptions.ClientNotFoundException; -import org.toop.framework.networking.exceptions.CouldNotConnectException; -import org.toop.framework.networking.interfaces.NetworkingClient; -import org.toop.framework.networking.types.NetworkingConnector; +import org.toop.framework.networking.connection.events.NetworkEvents; +import org.toop.framework.networking.connection.exceptions.ClientNotFoundException; +import org.toop.framework.networking.connection.exceptions.CouldNotConnectException; +import org.toop.framework.networking.connection.interfaces.NetworkingClient; +import org.toop.framework.networking.connection.types.NetworkingConnector; -public class NetworkingClientManager implements org.toop.framework.networking.interfaces.NetworkingClientManager { +public class NetworkingClientManager implements org.toop.framework.networking.connection.interfaces.NetworkingClientManager { private static final Logger logger = LogManager.getLogger(NetworkingClientManager.class); private final EventBus eventBus; diff --git a/framework/src/main/java/org/toop/framework/networking/clients/TournamentNetworkingClient.java b/framework/src/main/java/org/toop/framework/networking/connection/clients/TournamentNetworkingClient.java similarity index 92% rename from framework/src/main/java/org/toop/framework/networking/clients/TournamentNetworkingClient.java rename to framework/src/main/java/org/toop/framework/networking/connection/clients/TournamentNetworkingClient.java index 47adef9..1976e91 100644 --- a/framework/src/main/java/org/toop/framework/networking/clients/TournamentNetworkingClient.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/clients/TournamentNetworkingClient.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.clients; +package org.toop.framework.networking.connection.clients; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; @@ -12,9 +12,9 @@ import io.netty.util.CharsetUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.framework.eventbus.bus.EventBus; -import org.toop.framework.networking.exceptions.CouldNotConnectException; -import org.toop.framework.networking.handlers.NetworkingGameClientHandler; -import org.toop.framework.networking.interfaces.NetworkingClient; +import org.toop.framework.networking.connection.exceptions.CouldNotConnectException; +import org.toop.framework.networking.connection.handlers.NetworkingGameClientHandler; +import org.toop.framework.networking.connection.interfaces.NetworkingClient; import java.net.InetSocketAddress; diff --git a/framework/src/main/java/org/toop/framework/networking/events/NetworkEvents.java b/framework/src/main/java/org/toop/framework/networking/connection/events/NetworkEvents.java similarity index 97% rename from framework/src/main/java/org/toop/framework/networking/events/NetworkEvents.java rename to framework/src/main/java/org/toop/framework/networking/connection/events/NetworkEvents.java index 891fb39..d6c0140 100644 --- a/framework/src/main/java/org/toop/framework/networking/events/NetworkEvents.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/events/NetworkEvents.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.events; +package org.toop.framework.networking.connection.events; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -6,8 +6,8 @@ import java.util.concurrent.CompletableFuture; import org.toop.annotations.AutoResponseResult; import org.toop.framework.eventbus.GlobalEventBus; import org.toop.framework.eventbus.events.*; -import org.toop.framework.networking.interfaces.NetworkingClient; -import org.toop.framework.networking.types.NetworkingConnector; +import org.toop.framework.networking.connection.interfaces.NetworkingClient; +import org.toop.framework.networking.connection.types.NetworkingConnector; /** * Defines all event types related to the networking subsystem. diff --git a/framework/src/main/java/org/toop/framework/networking/exceptions/ClientNotFoundException.java b/framework/src/main/java/org/toop/framework/networking/connection/exceptions/ClientNotFoundException.java similarity index 91% rename from framework/src/main/java/org/toop/framework/networking/exceptions/ClientNotFoundException.java rename to framework/src/main/java/org/toop/framework/networking/connection/exceptions/ClientNotFoundException.java index 2506b26..7d8594a 100644 --- a/framework/src/main/java/org/toop/framework/networking/exceptions/ClientNotFoundException.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/exceptions/ClientNotFoundException.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.exceptions; +package org.toop.framework.networking.connection.exceptions; /** * Thrown when an operation is attempted on a networking client diff --git a/framework/src/main/java/org/toop/framework/networking/exceptions/CouldNotConnectException.java b/framework/src/main/java/org/toop/framework/networking/connection/exceptions/CouldNotConnectException.java similarity index 89% rename from framework/src/main/java/org/toop/framework/networking/exceptions/CouldNotConnectException.java rename to framework/src/main/java/org/toop/framework/networking/connection/exceptions/CouldNotConnectException.java index 839fb0b..a364c9c 100644 --- a/framework/src/main/java/org/toop/framework/networking/exceptions/CouldNotConnectException.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/exceptions/CouldNotConnectException.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.exceptions; +package org.toop.framework.networking.connection.exceptions; public class CouldNotConnectException extends RuntimeException { diff --git a/framework/src/main/java/org/toop/framework/networking/exceptions/NetworkingInitializationException.java b/framework/src/main/java/org/toop/framework/networking/connection/exceptions/NetworkingInitializationException.java similarity index 76% rename from framework/src/main/java/org/toop/framework/networking/exceptions/NetworkingInitializationException.java rename to framework/src/main/java/org/toop/framework/networking/connection/exceptions/NetworkingInitializationException.java index 0ff430a..3b2582f 100644 --- a/framework/src/main/java/org/toop/framework/networking/exceptions/NetworkingInitializationException.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/exceptions/NetworkingInitializationException.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.exceptions; +package org.toop.framework.networking.connection.exceptions; public class NetworkingInitializationException extends RuntimeException { public NetworkingInitializationException(String message, Throwable cause) { diff --git a/framework/src/main/java/org/toop/framework/networking/handlers/NetworkingGameClientHandler.java b/framework/src/main/java/org/toop/framework/networking/connection/handlers/NetworkingGameClientHandler.java similarity index 98% rename from framework/src/main/java/org/toop/framework/networking/handlers/NetworkingGameClientHandler.java rename to framework/src/main/java/org/toop/framework/networking/connection/handlers/NetworkingGameClientHandler.java index 7b4bf69..a313d97 100644 --- a/framework/src/main/java/org/toop/framework/networking/handlers/NetworkingGameClientHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/handlers/NetworkingGameClientHandler.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.handlers; +package org.toop.framework.networking.connection.handlers; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -9,7 +9,7 @@ import java.util.regex.Pattern; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.framework.eventbus.bus.EventBus; -import org.toop.framework.networking.events.NetworkEvents; +import org.toop.framework.networking.connection.events.NetworkEvents; public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LogManager.getLogger(NetworkingGameClientHandler.class); diff --git a/framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClient.java b/framework/src/main/java/org/toop/framework/networking/connection/interfaces/NetworkingClient.java similarity index 66% rename from framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClient.java rename to framework/src/main/java/org/toop/framework/networking/connection/interfaces/NetworkingClient.java index 09b215c..31731d3 100644 --- a/framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClient.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/interfaces/NetworkingClient.java @@ -1,6 +1,6 @@ -package org.toop.framework.networking.interfaces; +package org.toop.framework.networking.connection.interfaces; -import org.toop.framework.networking.exceptions.CouldNotConnectException; +import org.toop.framework.networking.connection.exceptions.CouldNotConnectException; import java.net.InetSocketAddress; diff --git a/framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClientManager.java b/framework/src/main/java/org/toop/framework/networking/connection/interfaces/NetworkingClientManager.java similarity index 57% rename from framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClientManager.java rename to framework/src/main/java/org/toop/framework/networking/connection/interfaces/NetworkingClientManager.java index c236080..31fcaa2 100644 --- a/framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClientManager.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/interfaces/NetworkingClientManager.java @@ -1,8 +1,8 @@ -package org.toop.framework.networking.interfaces; +package org.toop.framework.networking.connection.interfaces; -import org.toop.framework.networking.exceptions.ClientNotFoundException; -import org.toop.framework.networking.exceptions.CouldNotConnectException; -import org.toop.framework.networking.types.NetworkingConnector; +import org.toop.framework.networking.connection.exceptions.ClientNotFoundException; +import org.toop.framework.networking.connection.exceptions.CouldNotConnectException; +import org.toop.framework.networking.connection.types.NetworkingConnector; public interface NetworkingClientManager { void startClient( diff --git a/framework/src/main/java/org/toop/framework/networking/types/NetworkingConnector.java b/framework/src/main/java/org/toop/framework/networking/connection/types/NetworkingConnector.java similarity index 73% rename from framework/src/main/java/org/toop/framework/networking/types/NetworkingConnector.java rename to framework/src/main/java/org/toop/framework/networking/connection/types/NetworkingConnector.java index ee6ed44..ca8fc40 100644 --- a/framework/src/main/java/org/toop/framework/networking/types/NetworkingConnector.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/types/NetworkingConnector.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.types; +package org.toop.framework.networking.connection.types; import java.util.concurrent.TimeUnit; diff --git a/framework/src/main/java/org/toop/framework/networking/types/ServerCommand.java b/framework/src/main/java/org/toop/framework/networking/connection/types/ServerCommand.java similarity index 52% rename from framework/src/main/java/org/toop/framework/networking/types/ServerCommand.java rename to framework/src/main/java/org/toop/framework/networking/connection/types/ServerCommand.java index e11bb61..9472810 100644 --- a/framework/src/main/java/org/toop/framework/networking/types/ServerCommand.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/types/ServerCommand.java @@ -1,3 +1,3 @@ -package org.toop.framework.networking.types; +package org.toop.framework.networking.connection.types; public record ServerCommand(long clientId, String command) {} diff --git a/framework/src/main/java/org/toop/framework/networking/connection/types/ServerMessage.java b/framework/src/main/java/org/toop/framework/networking/connection/types/ServerMessage.java new file mode 100644 index 0000000..3ca1158 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/connection/types/ServerMessage.java @@ -0,0 +1,3 @@ +package org.toop.framework.networking.connection.types; + +public record ServerMessage(String message) {} diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/Game.java new file mode 100644 index 0000000..057e37c --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/Game.java @@ -0,0 +1,30 @@ +package org.toop.framework.networking.server; + +import org.toop.framework.game.BitboardGame; + +public class Game implements OnlineGame { + + private long id; + private User[] users; + private GameDefinition> game; + + public Game(GameDefinition game, User... users) { + this.game = game; + this.users = users; + } + + @Override + public long id() { + return id; + } + + @Override + public GameDefinition game() { + return game; + } + + @Override + public User[] users() { + return users; + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java b/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java new file mode 100644 index 0000000..629bc24 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java @@ -0,0 +1,26 @@ +package org.toop.framework.networking.server; + +import java.lang.reflect.InvocationTargetException; + +public class GameDefinition { + private final String name; + private final Class game; + + public GameDefinition(String name, Class game) { + this.name = name; + this.game = game; + } + + public String name() { + return name; + } + + public T create(String... users) { + try { + return game.getDeclaredConstructor().newInstance(users); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameServer.java b/framework/src/main/java/org/toop/framework/networking/server/GameServer.java new file mode 100644 index 0000000..39a03f7 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/GameServer.java @@ -0,0 +1,8 @@ +package org.toop.framework.networking.server; + +public interface GameServer { +// List gameTypes(); +// List ongoingGames(); +// void startGame(String gameType, User... users); +// String[] onlineUsers(); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java new file mode 100644 index 0000000..634ae6f --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java @@ -0,0 +1,68 @@ +package org.toop.framework.networking.server; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.LineBasedFrameDecoder; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import org.toop.framework.SnowflakeGenerator; +import org.toop.framework.game.BitboardGame; + +import java.util.Map; + +public class MasterServer { + private final int port; + public final Server gs; + + public MasterServer(int port, Map>> gameTypes) { + this.port = port; + this.gs = new Server(gameTypes); + } + + public void start() throws InterruptedException { + + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); + + try { + + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(workerGroup); + bootstrap.channel(NioServerSocketChannel.class); + bootstrap.option(ChannelOption.SO_BACKLOG, 128); + bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); + bootstrap.handler(new LoggingHandler(LogLevel.INFO)); + bootstrap.childHandler( + new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel ch) { + + ChannelPipeline pipeline = ch.pipeline(); + + pipeline.addLast(new LineBasedFrameDecoder(8192)); + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new StringEncoder()); + + long userid = SnowflakeGenerator.nextId(); + User user = new User(userid, ""+userid); + pipeline.addLast(new ServerHandler(user, gs)); + } + } + ); + + ChannelFuture future = bootstrap.bind(port).sync(); + System.out.println("MasterServer listening on port " + port); + + future.channel().closeFuture().sync(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/MessageStore.java b/framework/src/main/java/org/toop/framework/networking/server/MessageStore.java new file mode 100644 index 0000000..6fbba3b --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/MessageStore.java @@ -0,0 +1,7 @@ +package org.toop.framework.networking.server; + +public interface MessageStore { + void add(String message); + String get(); + void reset(); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java new file mode 100644 index 0000000..59b3399 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java @@ -0,0 +1,7 @@ +package org.toop.framework.networking.server; + +public interface OnlineGame { + long id(); + GameDefinition game(); + User[] users(); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ParsedMessage.java b/framework/src/main/java/org/toop/framework/networking/server/ParsedMessage.java new file mode 100644 index 0000000..5f8e550 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/ParsedMessage.java @@ -0,0 +1,4 @@ +package org.toop.framework.networking.server; + +public record ParsedMessage(String command, String... args) {} + diff --git a/framework/src/main/java/org/toop/framework/networking/server/Parser.java b/framework/src/main/java/org/toop/framework/networking/server/Parser.java new file mode 100644 index 0000000..016f797 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/Parser.java @@ -0,0 +1,4 @@ +package org.toop.framework.networking.server; + +public class Parser { +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServableGame.java b/framework/src/main/java/org/toop/framework/networking/server/ServableGame.java new file mode 100644 index 0000000..17dd3a9 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/ServableGame.java @@ -0,0 +1,4 @@ +package org.toop.framework.networking.server; + +public interface ServableGame { +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java new file mode 100644 index 0000000..757c490 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -0,0 +1,51 @@ +package org.toop.framework.networking.server; + +import org.toop.framework.game.BitboardGame; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class Server implements GameServer { + + final private Map>> gameTypes; + public List games = new ArrayList<>(); + final private Map users = new ConcurrentHashMap<>(); + + public Server(Map>> gameTypes) { + this.gameTypes = gameTypes; + } + + public void addUser(ServerUser user) { + users.putIfAbsent(user.id(), user); + } + + public void removeUser(ServerUser user) { + users.remove(user.id()); + } + + public String[] gameTypes() { + return gameTypes.keySet().toArray(new String[0]); + } + +// public List>> ongoingGames() { +// return List.of(); +// } + + public void startGame(String gameType, User... users) { + if (!gameTypes.containsKey(gameType)) return; + + try { + var game = new Game(gameTypes.get(gameType).create(), users); + games.addLast(new Game(game, users)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + public String[] onlineUsers() { + return users.values().stream().map(ServerUser::name).toArray(String[]::new); + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java new file mode 100644 index 0000000..f5da259 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java @@ -0,0 +1,117 @@ +package org.toop.framework.networking.server; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class ServerHandler extends SimpleChannelInboundHandler { + + private final User user; + private final Server server; + + public ServerHandler(User user, Server server) { + this.user = user; + this.server = server; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + ctx.writeAndFlush("WELCOME " + user.id() + "\n"); + + user.setCtx(ctx); + server.addUser(user); // TODO set correct name on login + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, String msg) { + ParsedMessage p = parse(msg); + if (p == null) return; + + IO.println(p.command() + " " + Arrays.toString(p.args())); + + switch (p.command()) { + case "ping" -> ctx.writeAndFlush("PONG\n"); + case "login" -> handleLogin(p); + case "get" -> handleGet(p); + case "subscribe" -> handleSubscribe(p); + case "move" -> handleMove(p); + case "challenge" -> handleChallenge(p); + case "message" -> handleMessage(p); + case "help" -> handleHelp(p); + default -> ctx.writeAndFlush("ERROR Unknown command\n"); + } + } + + private boolean allowedArgs(String... args) { + if (args.length < 1) return false; + return true; + } + + private void handleLogin(ParsedMessage p) { + + if (!allowedArgs(p.args())) return; + + user.setName(p.args()[0]); + } + + private void handleGet(ParsedMessage p) { + if (!allowedArgs(p.args())) return; + + switch (p.args()[0]) { + case "playerlist" -> user.ctx().writeAndFlush(Arrays.toString(server.onlineUsers())); + case "gamelist" -> user.ctx().writeAndFlush(Arrays.toString(server.gameTypes())); + } + } + + private void handleSubscribe(ParsedMessage p) { + // TODO + } + + private void handleHelp(ParsedMessage p) { + // TODO + } + + private void handleMessage(ParsedMessage p) { + // TODO + } + + private void handleChallenge(ParsedMessage p) { + // TODO + } + + private void handleMove(ParsedMessage p) { + // TODO + } + + private ParsedMessage parse(String msg) { + // TODO, what if empty string. + + if (msg.isEmpty()) return null; + + msg = msg.trim().toLowerCase(); + + List parts = new LinkedList<>(List.of(msg.split(" "))); + + if (parts.size() > 1) { + String command = parts.removeFirst(); + return new ParsedMessage(command, parts.toArray(String[]::new)); + } + else { + return new ParsedMessage(msg); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + server.removeUser(user); + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java b/framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java new file mode 100644 index 0000000..22af1d7 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java @@ -0,0 +1,27 @@ +package org.toop.framework.networking.server; + +import java.util.Queue; + +public class ServerMessageStore implements MessageStore { + + Queue messageQueue; + + public ServerMessageStore(Queue messageQueue) { + this.messageQueue = messageQueue; + } + + @Override + public void add(String message) { + messageQueue.offer(message); + } + + @Override + public String get() { + return messageQueue.poll(); + } + + @Override + public void reset() { + messageQueue.clear(); + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java new file mode 100644 index 0000000..8e6e7a5 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java @@ -0,0 +1,9 @@ +package org.toop.framework.networking.server; + +import java.net.InetSocketAddress; + +public interface ServerUser { + long id(); + String name(); + void setName(String name); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/User.java b/framework/src/main/java/org/toop/framework/networking/server/User.java new file mode 100644 index 0000000..8ae5026 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/User.java @@ -0,0 +1,40 @@ +package org.toop.framework.networking.server; + +import io.netty.channel.ChannelHandlerContext; + +import java.net.InetSocketAddress; + +public class User implements ServerUser { + final private long id; + private String name; + private ChannelHandlerContext connectionContext; + + public User(long userId, String name) { + this.id = userId; + this.name = name; + } + + @Override + public long id() { + return id; + } + + @Override + public String name() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + public ChannelHandlerContext ctx() { + return connectionContext; + } + + public void setCtx(ChannelHandlerContext ctx) { + this.connectionContext = ctx; + } + +} diff --git a/framework/src/main/java/org/toop/framework/networking/types/ServerMessage.java b/framework/src/main/java/org/toop/framework/networking/types/ServerMessage.java deleted file mode 100644 index 606607d..0000000 --- a/framework/src/main/java/org/toop/framework/networking/types/ServerMessage.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.toop.framework.networking.types; - -public record ServerMessage(String message) {} diff --git a/framework/src/test/java/org/toop/framework/networking/NetworkingClientManagerTest.java b/framework/src/test/java/org/toop/framework/networking/NetworkingClientManagerTest.java index e626683..4ce747b 100644 --- a/framework/src/test/java/org/toop/framework/networking/NetworkingClientManagerTest.java +++ b/framework/src/test/java/org/toop/framework/networking/NetworkingClientManagerTest.java @@ -9,7 +9,7 @@ //import org.mockito.*; //import org.toop.framework.SnowflakeGenerator; //import org.toop.framework.eventbus.EventFlow; -//import org.toop.framework.networking.events.NetworkEvents; +//import org.toop.framework.networking.connection.events.NetworkEvents; // //class NetworkingClientManagerTest { // diff --git a/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java b/framework/src/test/java/org/toop/framework/networking/connection/events/NetworkEventsTest.java similarity index 99% rename from framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java rename to framework/src/test/java/org/toop/framework/networking/connection/events/NetworkEventsTest.java index 5170381..635772d 100644 --- a/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java +++ b/framework/src/test/java/org/toop/framework/networking/connection/events/NetworkEventsTest.java @@ -1,4 +1,4 @@ -//package org.toop.framework.networking.events; +//package org.toop.framework.networking.connection.events; // //import static org.junit.jupiter.api.Assertions.*; // From 84e411fa38ec08ac5c192f9e875e3cce1a34a10e Mon Sep 17 00:00:00 2001 From: Bas de Jong Date: Fri, 12 Dec 2025 15:17:12 +0100 Subject: [PATCH 02/35] Moves --- .../main/java/org/toop/framework}/game/BitboardGame.java | 3 +-- .../framework}/game/games/reversi/BitboardReversi.java | 7 ++++--- .../framework}/game/games/tictactoe/BitboardTicTacToe.java | 7 ++++--- .../org/toop/framework}/game/players/ArtificialPlayer.java | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/BitboardGame.java (96%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/games/reversi/BitboardReversi.java (96%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/games/tictactoe/BitboardTicTacToe.java (93%) rename {game/src/main/java/org/toop => framework/src/main/java/org/toop/framework}/game/players/ArtificialPlayer.java (97%) diff --git a/game/src/main/java/org/toop/game/BitboardGame.java b/framework/src/main/java/org/toop/framework/game/BitboardGame.java similarity index 96% rename from game/src/main/java/org/toop/game/BitboardGame.java rename to framework/src/main/java/org/toop/framework/game/BitboardGame.java index 4ebdb95..2090e95 100644 --- a/game/src/main/java/org/toop/game/BitboardGame.java +++ b/framework/src/main/java/org/toop/framework/game/BitboardGame.java @@ -1,10 +1,9 @@ -package org.toop.game; +package org.toop.framework.game; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.Player; import java.util.Arrays; -import java.util.concurrent.atomic.AtomicInteger; // There is AI performance to be gained by getting rid of non-primitives and thus speeding up deepCopy public abstract class BitboardGame> implements TurnBasedGame { diff --git a/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java similarity index 96% rename from game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java rename to framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java index f380bef..0ea4346 100644 --- a/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java +++ b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java @@ -1,11 +1,12 @@ -package org.toop.game.games.reversi; +package org.toop.framework.game.games.reversi; +import org.toop.framework.game.BitboardGame; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.BitboardGame; +import org.toop.framework.networking.server.ServableGame; -public class BitboardReversi extends BitboardGame { +public class BitboardReversi extends BitboardGame implements ServableGame { public record Score(int black, int white) {} diff --git a/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java b/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java similarity index 93% rename from game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java rename to framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java index 0927431..2d77a9f 100644 --- a/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java +++ b/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java @@ -1,11 +1,12 @@ -package org.toop.game.games.tictactoe; +package org.toop.framework.game.games.tictactoe; +import org.toop.framework.game.BitboardGame; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.BitboardGame; +import org.toop.framework.networking.server.ServableGame; -public class BitboardTicTacToe extends BitboardGame { +public class BitboardTicTacToe extends BitboardGame implements ServableGame { private final long[] winningLines = { 0b111000000L, // top row 0b000111000L, // middle row diff --git a/game/src/main/java/org/toop/game/players/ArtificialPlayer.java b/framework/src/main/java/org/toop/framework/game/players/ArtificialPlayer.java similarity index 97% rename from game/src/main/java/org/toop/game/players/ArtificialPlayer.java rename to framework/src/main/java/org/toop/framework/game/players/ArtificialPlayer.java index 418cbed..75e4078 100644 --- a/game/src/main/java/org/toop/game/players/ArtificialPlayer.java +++ b/framework/src/main/java/org/toop/framework/game/players/ArtificialPlayer.java @@ -1,4 +1,4 @@ -package org.toop.game.players; +package org.toop.framework.game.players; import org.toop.framework.gameFramework.model.player.*; import org.toop.framework.gameFramework.model.game.TurnBasedGame; From 2599f9fa409ce0a473b8c5edea034e26e760c7d9 Mon Sep 17 00:00:00 2001 From: Bas de Jong Date: Fri, 12 Dec 2025 15:17:29 +0100 Subject: [PATCH 03/35] Testing code --- app/src/main/java/org/toop/app/App.java | 35 +++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 5d99acd..b9e0d8d 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -1,10 +1,14 @@ package org.toop.app; +import javafx.application.Application; import javafx.application.Platform; +import javafx.geometry.Pos; +import javafx.scene.Scene; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyEvent; - +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; import org.toop.app.widget.Primitive; import org.toop.app.widget.WidgetContainer; import org.toop.app.widget.complex.LoadingWidget; @@ -16,8 +20,12 @@ import org.toop.framework.audio.*; import org.toop.framework.audio.events.AudioEvents; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.GlobalEventBus; -import org.toop.framework.networking.NetworkingClientEventListener; -import org.toop.framework.networking.NetworkingClientManager; +import org.toop.framework.game.BitboardGame; +import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.networking.connection.NetworkingClientEventListener; +import org.toop.framework.networking.connection.NetworkingClientManager; +import org.toop.framework.networking.server.GameDefinition; +import org.toop.framework.networking.server.MasterServer; import org.toop.framework.resource.ResourceLoader; import org.toop.framework.resource.ResourceManager; import org.toop.framework.resource.events.AssetLoaderEvents; @@ -27,13 +35,8 @@ import org.toop.framework.resource.resources.SoundEffectAsset; import org.toop.local.AppContext; import org.toop.local.AppSettings; -import javafx.application.Application; -import javafx.geometry.Pos; -import javafx.scene.Scene; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; - import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -86,7 +89,7 @@ public final class App extends Application { AppSettings.applySettings(); - setKeybinds(root); + setKeybinds(root); LoadingWidget loading = new LoadingWidget(Primitive.text( "Loading...", false), 0, 0, Integer.MAX_VALUE, false, false // Just set a high default @@ -138,7 +141,17 @@ public final class App extends Application { stage.show(); - } + var games = new ConcurrentHashMap>>(); + games.put("tictactoe", new GameDefinition<>("tictactoe", BitboardReversi.class)); + games.put("reversi", new GameDefinition<>("reversi", BitboardReversi.class)); + var s = new MasterServer(6666, games); + try { + s.start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + } private void setKeybinds(StackPane root) { root.addEventHandler(KeyEvent.KEY_PRESSED,event -> { From fa9c2ce32b0c20308875bfab6a6bee4518cd2cbd Mon Sep 17 00:00:00 2001 From: Stef Date: Fri, 12 Dec 2025 15:53:24 +0100 Subject: [PATCH 04/35] Removed Generics, pray nothing breaks. --- app/src/main/java/org/toop/app/Server.java | 12 ++++++------ .../org/toop/app/canvas/BitGameCanvas.java | 2 +- .../java/org/toop/app/canvas/GameCanvas.java | 3 +-- .../java/org/toop/app/canvas/GameDrawer.java | 4 ++-- .../org/toop/app/canvas/ReversiBitCanvas.java | 9 +++++---- .../toop/app/canvas/TicTacToeBitCanvas.java | 9 +++++---- .../gameControllers/GenericGameController.java | 16 +++++++++------- .../gameControllers/ReversiBitController.java | 12 ++++++------ .../TicTacToeBitController.java | 12 ++++++------ .../app/widget/view/LocalMultiplayerView.java | 16 ++++++++-------- .../model/game/PlayerProvider.java | 4 ++-- .../model/game/TurnBasedGame.java | 2 +- .../AbstractThreadBehaviour.java | 6 +++--- .../gameFramework/model/player/AI.java | 2 +- .../gameFramework/model/player/AbstractAI.java | 2 +- .../model/player/AbstractPlayer.java | 6 +++--- .../model/player/MoveProvider.java | 4 ++-- .../gameFramework/model/player/Player.java | 2 +- .../main/java/org/toop/game/BitboardGame.java | 14 +++++++------- .../LocalFixedRateThreadBehaviour.java | 6 +++--- .../game/gameThreads/LocalThreadBehaviour.java | 6 +++--- .../gameThreads/OnlineThreadBehaviour.java | 6 +++--- .../OnlineWithSleepThreadBehaviour.java | 4 ++-- .../game/games/reversi/BitboardReversi.java | 4 ++-- .../games/tictactoe/BitboardTicTacToe.java | 4 ++-- .../toop/game/players/ArtificialPlayer.java | 14 +++++++------- .../org/toop/game/players/LocalPlayer.java | 14 +++++++------- .../java/org/toop/game/players/MiniMaxAI.java | 18 +++++++++--------- .../org/toop/game/players/OnlinePlayer.java | 8 ++++---- .../java/org/toop/game/players/RandomAI.java | 8 ++++---- 30 files changed, 116 insertions(+), 113 deletions(-) diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index ef691b4..a781a70 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -214,15 +214,15 @@ public final class Server { switch (type) { case TICTACTOE ->{ - Player[] players = new Player[2]; - players[(myTurn + 1) % 2] = new OnlinePlayer<>(response.opponent()); - players[myTurn] = new ArtificialPlayer<>(new RandomAI(), user); + Player[] players = new Player[2]; + players[(myTurn + 1) % 2] = new OnlinePlayer(response.opponent()); + players[myTurn] = new ArtificialPlayer(new RandomAI(), user); gameController = new TicTacToeBitController(players); } case REVERSI -> { - Player[] players = new Player[2]; - players[(myTurn + 1) % 2] = new OnlinePlayer<>(response.opponent()); - players[myTurn] = new ArtificialPlayer<>(new RandomAI(), user); + Player[] players = new Player[2]; + players[(myTurn + 1) % 2] = new OnlinePlayer(response.opponent()); + players[myTurn] = new ArtificialPlayer(new RandomAI(), user); gameController = new ReversiBitController(players);} default -> new ErrorPopup("Unsupported game type."); diff --git a/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java index 85c6f7d..5beb7af 100644 --- a/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java @@ -14,7 +14,7 @@ import org.toop.framework.gameFramework.view.GUIEvents; import java.util.function.Consumer; -public abstract class BitGameCanvas> implements GameCanvas { +public abstract class BitGameCanvas implements GameCanvas { protected record Cell(float x, float y, float width, float height) { public boolean isInside(double x, double y) { return x >= this.x && x <= this.x + width && diff --git a/app/src/main/java/org/toop/app/canvas/GameCanvas.java b/app/src/main/java/org/toop/app/canvas/GameCanvas.java index d1361c5..1739d66 100644 --- a/app/src/main/java/org/toop/app/canvas/GameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/GameCanvas.java @@ -1,8 +1,7 @@ package org.toop.app.canvas; import javafx.scene.canvas.Canvas; -import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public interface GameCanvas> extends GameDrawer{ +public interface GameCanvas extends GameDrawer{ Canvas getCanvas(); } diff --git a/app/src/main/java/org/toop/app/canvas/GameDrawer.java b/app/src/main/java/org/toop/app/canvas/GameDrawer.java index 261334c..2adb6b0 100644 --- a/app/src/main/java/org/toop/app/canvas/GameDrawer.java +++ b/app/src/main/java/org/toop/app/canvas/GameDrawer.java @@ -2,6 +2,6 @@ package org.toop.app.canvas; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public interface GameDrawer> { - void redraw(T gameCopy); +public interface GameDrawer { + void redraw(TurnBasedGame gameCopy); } diff --git a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java index 7c2bde0..016d6b3 100644 --- a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java @@ -2,12 +2,13 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.game.games.reversi.BitboardReversi; import java.util.Arrays; import java.util.function.Consumer; -public class ReversiBitCanvas extends BitGameCanvas { +public class ReversiBitCanvas extends BitGameCanvas { public ReversiBitCanvas() { super(Color.GRAY, new Color(0f, 0.4f, 0.2f, 1f), (App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3, 8, 8, 5, true); canvas.setOnMouseMoved(event -> { @@ -33,10 +34,10 @@ public class ReversiBitCanvas extends BitGameCanvas { } @Override - public void redraw(BitboardReversi gameCopy) { + public void redraw(TurnBasedGame gameCopy) { clearAll(); long[] board = gameCopy.getBoard(); - loopOverBoard(board[0], (i) -> drawDot(Color.WHITE, i)); - loopOverBoard(board[1], (i) -> drawDot(Color.BLACK, i)); + loopOverBoard(board[0], (i) -> drawDot(Color.WHITE, (int)i)); + loopOverBoard(board[1], (i) -> drawDot(Color.BLACK, (int)i)); } } diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java index 443adbd..3520417 100644 --- a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java @@ -2,12 +2,13 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.game.games.tictactoe.BitboardTicTacToe; import java.util.Arrays; import java.util.function.Consumer; -public class TicTacToeBitCanvas extends BitGameCanvas{ +public class TicTacToeBitCanvas extends BitGameCanvas{ public TicTacToeBitCanvas() { super( Color.GRAY, @@ -22,14 +23,14 @@ public class TicTacToeBitCanvas extends BitGameCanvas{ } @Override - public void redraw(BitboardTicTacToe gameCopy) { + public void redraw(TurnBasedGame gameCopy) { clearAll(); drawMoves(gameCopy.getBoard()); } private void drawMoves(long[] gameBoard){ - loopOverBoard(gameBoard[0], (i) -> drawX(Color.RED, i)); - loopOverBoard(gameBoard[1], (i) -> drawO(Color.BLUE, i)); + loopOverBoard(gameBoard[0], (i) -> drawX(Color.RED, (int)i)); + loopOverBoard(gameBoard[1], (i) -> drawO(Color.BLUE, (Integer) i)); } diff --git a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java index 2c3ad49..4de41f6 100644 --- a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java +++ b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java @@ -18,7 +18,7 @@ import org.toop.framework.gameFramework.view.GUIEvents; import org.toop.framework.networking.events.NetworkEvents; import org.toop.game.players.LocalPlayer; -public class GenericGameController> implements GameController { +public class GenericGameController implements GameController { protected final EventFlow eventFlow = new EventFlow(); // Logger for logging @@ -28,13 +28,13 @@ public class GenericGameController> implements GameCo protected final GameView gameView; // Reference to game canvas - protected final GameCanvas canvas; + protected final GameCanvas canvas; - protected final TurnBasedGame game; // Reference to game instance + protected final TurnBasedGame game; // Reference to game instance private final ThreadBehaviour gameThreadBehaviour; // TODO: Change gameType to automatically happen with either dependency injection or something else. - public GenericGameController(GameCanvas canvas, T game, ThreadBehaviour gameThreadBehaviour, String gameType) { + public GenericGameController(GameCanvas canvas, TurnBasedGame game, ThreadBehaviour gameThreadBehaviour, String gameType) { logger.info("Creating: " + this.getClass()); this.canvas = canvas; @@ -55,7 +55,9 @@ public class GenericGameController> implements GameCo // Listen to updates eventFlow .listen(GUIEvents.GameEnded.class, this::onGameFinish, false) - .listen(GUIEvents.PlayerAttemptedMove.class, event -> {if (getCurrentPlayer() instanceof LocalPlayer lp){lp.setMove(event.move());}}, false); + .listen(GUIEvents.PlayerAttemptedMove.class, event -> { + if (getCurrentPlayer() instanceof LocalPlayer lp){lp.setMove(event.move());} + }, false); } public void start(){ @@ -70,7 +72,7 @@ public class GenericGameController> implements GameCo gameThreadBehaviour.stop(); } - public Player getCurrentPlayer(){ + public Player getCurrentPlayer(){ return game.getPlayer(getCurrentPlayerIndex()); } @@ -97,7 +99,7 @@ public class GenericGameController> implements GameCo stop(); } - public Player getPlayer(int player){ + public Player getPlayer(int player){ if (player < 0 || player >= 2){ // TODO: Make game turn player count logger.error("Invalid player index"); throw new IllegalArgumentException("player out of range"); diff --git a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java index 40784b0..c698eeb 100644 --- a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java @@ -8,13 +8,13 @@ import org.toop.game.gameThreads.OnlineThreadBehaviour; import org.toop.game.games.reversi.BitboardReversi; import org.toop.game.players.OnlinePlayer; -public class ReversiBitController extends GenericGameController { - public ReversiBitController(Player[] players) { +public class ReversiBitController extends GenericGameController { + public ReversiBitController(Player[] players) { BitboardReversi game = new BitboardReversi(players); - ThreadBehaviour thread = new LocalThreadBehaviour<>(game); - for (Player player : players) { - if (player instanceof OnlinePlayer){ - thread = new OnlineThreadBehaviour<>(game); + ThreadBehaviour thread = new LocalThreadBehaviour(game); + for (Player player : players) { + if (player instanceof OnlinePlayer){ + thread = new OnlineThreadBehaviour(game); } } super(new ReversiBitCanvas(), game, thread, "Reversi"); diff --git a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java index 6307894..495f6f9 100644 --- a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java @@ -9,13 +9,13 @@ import org.toop.game.gameThreads.OnlineThreadBehaviour; import org.toop.game.games.tictactoe.BitboardTicTacToe; import org.toop.game.players.OnlinePlayer; -public class TicTacToeBitController extends GenericGameController { - public TicTacToeBitController(Player[] players) { +public class TicTacToeBitController extends GenericGameController { + public TicTacToeBitController(Player[] players) { BitboardTicTacToe game = new BitboardTicTacToe(players); - ThreadBehaviour thread = new LocalThreadBehaviour<>(game); - for (Player player : players) { - if (player instanceof OnlinePlayer){ - thread = new OnlineThreadBehaviour<>(game); + ThreadBehaviour thread = new LocalThreadBehaviour(game); + for (Player player : players) { + if (player instanceof OnlinePlayer){ + thread = new OnlineThreadBehaviour(game); } } super(new TicTacToeBitCanvas(), game, thread , "TicTacToe"); diff --git a/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java b/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java index 9b9bed2..1176c37 100644 --- a/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java +++ b/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java @@ -58,14 +58,14 @@ public class LocalMultiplayerView extends ViewWidget { switch (information.type) { case TICTACTOE: if (information.players[0].isHuman) { - players[0] = new LocalPlayer<>(information.players[0].name); + players[0] = new LocalPlayer(information.players[0].name); } else { - players[0] = new ArtificialPlayer<>(new RandomAI(), "Random AI"); + players[0] = new ArtificialPlayer(new RandomAI(), "Random AI"); } if (information.players[1].isHuman) { - players[1] = new LocalPlayer<>(information.players[1].name); + players[1] = new LocalPlayer(information.players[1].name); } else { - players[1] = new ArtificialPlayer<>(new MiniMaxAI(9), "MiniMax AI"); + players[1] = new ArtificialPlayer(new MiniMaxAI(9), "MiniMax AI"); } if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstTTT()) { new ShowEnableTutorialWidget( @@ -86,14 +86,14 @@ public class LocalMultiplayerView extends ViewWidget { break; case REVERSI: if (information.players[0].isHuman) { - players[0] = new LocalPlayer<>(information.players[0].name); + players[0] = new LocalPlayer(information.players[0].name); } else { - players[0] = new ArtificialPlayer<>(new RandomAI(), "Random AI"); + players[0] = new ArtificialPlayer(new RandomAI(), "Random AI"); } if (information.players[1].isHuman) { - players[1] = new LocalPlayer<>(information.players[1].name); + players[1] = new LocalPlayer(information.players[1].name); } else { - players[1] = new ArtificialPlayer<>(new MiniMaxAI(6), "MiniMax"); + players[1] = new ArtificialPlayer(new MiniMaxAI(6), "MiniMax"); } if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstReversi()) { new ShowEnableTutorialWidget( diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/game/PlayerProvider.java b/framework/src/main/java/org/toop/framework/gameFramework/model/game/PlayerProvider.java index 8db47a3..181edaa 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/game/PlayerProvider.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/game/PlayerProvider.java @@ -2,6 +2,6 @@ package org.toop.framework.gameFramework.model.game; import org.toop.framework.gameFramework.model.player.Player; -public interface PlayerProvider> { - Player getPlayer(int index); +public interface PlayerProvider { + Player getPlayer(int index); } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java b/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java index d4cb4df..dae192d 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java @@ -1,6 +1,6 @@ package org.toop.framework.gameFramework.model.game; -public interface TurnBasedGame> extends Playable, DeepCopyable, PlayerProvider, BoardProvider { +public interface TurnBasedGame extends Playable, PlayerProvider, BoardProvider, DeepCopyable { int getCurrentTurn(); int getPlayerCount(); int getWinner(); diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/game/threadBehaviour/AbstractThreadBehaviour.java b/framework/src/main/java/org/toop/framework/gameFramework/model/game/threadBehaviour/AbstractThreadBehaviour.java index da6647a..9dd6af7 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/game/threadBehaviour/AbstractThreadBehaviour.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/game/threadBehaviour/AbstractThreadBehaviour.java @@ -16,14 +16,14 @@ import java.util.function.Consumer; * a running flag, a game reference, and a logger. * Subclasses implement the actual game-loop logic. */ -public abstract class AbstractThreadBehaviour> implements ThreadBehaviour { +public abstract class AbstractThreadBehaviour implements ThreadBehaviour { private LongPairConsumer onSendMove; private Runnable onUpdateUI; /** Indicates whether the game loop or event processing is active. */ protected final AtomicBoolean isRunning = new AtomicBoolean(); /** The game instance controlled by this behaviour. */ - protected final T game; + protected final TurnBasedGame game; /** Logger for the subclass to report errors or debug info. */ protected final Logger logger = LogManager.getLogger(this.getClass()); @@ -33,7 +33,7 @@ public abstract class AbstractThreadBehaviour> implem * * @param game the turn-based game to control */ - public AbstractThreadBehaviour(T game) { + public AbstractThreadBehaviour(TurnBasedGame game) { this.game = game; } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AI.java b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AI.java index 8377166..afacfbf 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AI.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AI.java @@ -3,5 +3,5 @@ package org.toop.framework.gameFramework.model.player; import org.toop.framework.gameFramework.model.game.DeepCopyable; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public interface AI> extends MoveProvider, DeepCopyable> { +public interface AI extends MoveProvider, DeepCopyable { } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractAI.java b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractAI.java index 328132d..bdca918 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractAI.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractAI.java @@ -12,6 +12,6 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame; * * @param the specific type of game this AI can play, extending {@link GameR} */ -public abstract class AbstractAI> implements AI { +public abstract class AbstractAI implements AI { // Concrete AI implementations should override findBestMove(T game, int depth) } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java index 57e2f18..f643574 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java @@ -15,7 +15,7 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame; * specific move logic. *

*/ -public abstract class AbstractPlayer> implements Player { +public abstract class AbstractPlayer implements Player { private final Logger logger = LogManager.getLogger(this.getClass()); private final String name; @@ -24,7 +24,7 @@ public abstract class AbstractPlayer> implements Play this.name = name; } - protected AbstractPlayer(AbstractPlayer other) { + protected AbstractPlayer(AbstractPlayer other) { this.name = other.name; } /** @@ -39,7 +39,7 @@ public abstract class AbstractPlayer> implements Play * @return an integer representing the chosen move * @throws UnsupportedOperationException if the method is not overridden */ - public long getMove(T gameCopy) { + public long getMove(TurnBasedGame gameCopy) { logger.error("Method getMove not implemented."); throw new UnsupportedOperationException("Not supported yet."); } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/player/MoveProvider.java b/framework/src/main/java/org/toop/framework/gameFramework/model/player/MoveProvider.java index b9ab448..159addd 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/player/MoveProvider.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/player/MoveProvider.java @@ -2,6 +2,6 @@ package org.toop.framework.gameFramework.model.player; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public interface MoveProvider> { - long getMove(T game); +public interface MoveProvider { + long getMove(TurnBasedGame game); } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java b/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java index e2ff1a8..a3f1b32 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java @@ -3,5 +3,5 @@ package org.toop.framework.gameFramework.model.player; import org.toop.framework.gameFramework.model.game.DeepCopyable; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public interface Player> extends NameProvider, MoveProvider, DeepCopyable> { +public interface Player extends NameProvider, MoveProvider, DeepCopyable { } diff --git a/game/src/main/java/org/toop/game/BitboardGame.java b/game/src/main/java/org/toop/game/BitboardGame.java index 4ebdb95..b4244af 100644 --- a/game/src/main/java/org/toop/game/BitboardGame.java +++ b/game/src/main/java/org/toop/game/BitboardGame.java @@ -7,17 +7,17 @@ import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; // There is AI performance to be gained by getting rid of non-primitives and thus speeding up deepCopy -public abstract class BitboardGame> implements TurnBasedGame { +public abstract class BitboardGame implements TurnBasedGame { private final int columnSize; private final int rowSize; - private Player[] players; + private Player[] players; // long is 64 bits. Every game has a limit of 64 cells maximum. private final long[] playerBitboard; private int currentTurn = 0; - public BitboardGame(int columnSize, int rowSize, int playerCount, Player[] players) { + public BitboardGame(int columnSize, int rowSize, int playerCount, Player[] players) { this.columnSize = columnSize; this.rowSize = rowSize; this.players = players; @@ -26,14 +26,14 @@ public abstract class BitboardGame> implements TurnBas Arrays.fill(playerBitboard, 0L); } - public BitboardGame(BitboardGame other) { + public BitboardGame(BitboardGame other) { this.columnSize = other.columnSize; this.rowSize = other.rowSize; this.playerBitboard = other.playerBitboard.clone(); this.currentTurn = other.currentTurn; this.players = Arrays.stream(other.players) - .map(Player::deepCopy) + .map(Player::deepCopy) .toArray(Player[]::new); } @@ -61,7 +61,7 @@ public abstract class BitboardGame> implements TurnBas return getCurrentPlayerIndex(); } - public Player getPlayer(int index) {return players[index];} + public Player getPlayer(int index) {return players[index];} public int getCurrentPlayerIndex() { return currentTurn % playerBitboard.length; @@ -71,7 +71,7 @@ public abstract class BitboardGame> implements TurnBas return (currentTurn + 1) % playerBitboard.length; } - public Player getCurrentPlayer(){ + public Player getCurrentPlayer(){ return players[getCurrentPlayerIndex()]; } diff --git a/game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java b/game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java index 0a9da7f..670cb2a 100644 --- a/game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java +++ b/game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java @@ -16,7 +16,7 @@ import java.util.function.Consumer; * Runs a separate thread that executes game turns at a fixed frequency (default 60 updates/sec), * applying player moves, updating the game state, and dispatching UI events. */ -public class LocalFixedRateThreadBehaviour> extends AbstractThreadBehaviour implements Runnable { +public class LocalFixedRateThreadBehaviour extends AbstractThreadBehaviour implements Runnable { /** @@ -24,7 +24,7 @@ public class LocalFixedRateThreadBehaviour> extends A * * @param game the game instance */ - public LocalFixedRateThreadBehaviour(T game) { + public LocalFixedRateThreadBehaviour(TurnBasedGame game) { super(game); } @@ -59,7 +59,7 @@ public class LocalFixedRateThreadBehaviour> extends A if (now >= nextUpdate) { nextUpdate += UPDATE_INTERVAL; - Player currentPlayer = game.getPlayer(game.getCurrentTurn()); + Player currentPlayer = game.getPlayer(game.getCurrentTurn()); long move = currentPlayer.getMove(game.deepCopy()); PlayResult result = game.play(move); diff --git a/game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java b/game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java index 79c57f9..fab1b96 100644 --- a/game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java +++ b/game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java @@ -16,14 +16,14 @@ import java.util.function.Consumer; * Repeatedly gets the current player's move, applies it to the game, * updates the UI, and stops when the game ends or {@link #stop()} is called. */ -public class LocalThreadBehaviour> extends AbstractThreadBehaviour implements Runnable { +public class LocalThreadBehaviour extends AbstractThreadBehaviour implements Runnable { /** * Creates a new behaviour for a local turn-based game. * * @param game the game instance */ - public LocalThreadBehaviour(T game) { + public LocalThreadBehaviour(TurnBasedGame game) { super(game); } @@ -48,7 +48,7 @@ public class LocalThreadBehaviour> extends AbstractTh @Override public void run() { while (isRunning.get()) { - Player currentPlayer = game.getPlayer(game.getCurrentTurn()); + Player currentPlayer = game.getPlayer(game.getCurrentTurn()); long move = currentPlayer.getMove(game.deepCopy()); PlayResult result = game.play(move); diff --git a/game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java b/game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java index ae9aa88..97c339a 100644 --- a/game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java +++ b/game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java @@ -14,17 +14,17 @@ import org.toop.game.players.OnlinePlayer; * Reacts to server events, sending moves and updating the game state * for the local player while receiving moves from other players. */ -public class OnlineThreadBehaviour> extends AbstractThreadBehaviour implements SupportsOnlinePlay { +public class OnlineThreadBehaviour extends AbstractThreadBehaviour implements SupportsOnlinePlay { /** * Creates behaviour and sets the first local player * (non-online player) from the given array. */ - public OnlineThreadBehaviour(T game) { + public OnlineThreadBehaviour(TurnBasedGame game) { super(game); } /** Finds the first non-online player in the array. */ - private int getFirstNotOnlinePlayer(Player[] players) { + private int getFirstNotOnlinePlayer(Player[] players) { for (int i = 0; i < players.length; i++) { if (!(players[i] instanceof OnlinePlayer)) { return i; diff --git a/game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java b/game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java index a666f8d..ad20969 100644 --- a/game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java +++ b/game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java @@ -10,14 +10,14 @@ import org.toop.framework.networking.events.NetworkEvents; * This is identical to {@link OnlineThreadBehaviour}, but inserts a * short sleep before delegating to the base implementation. */ -public class OnlineWithSleepThreadBehaviour> extends OnlineThreadBehaviour { +public class OnlineWithSleepThreadBehaviour extends OnlineThreadBehaviour { /** * Creates the behaviour and forwards the players to the base class. * * @param game the online-capable turn-based game */ - public OnlineWithSleepThreadBehaviour(T game) { + public OnlineWithSleepThreadBehaviour(TurnBasedGame game) { super(game); } diff --git a/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java b/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java index f380bef..867322f 100644 --- a/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java +++ b/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java @@ -5,14 +5,14 @@ import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.player.Player; import org.toop.game.BitboardGame; -public class BitboardReversi extends BitboardGame { +public class BitboardReversi extends BitboardGame { public record Score(int black, int white) {} private final long notAFile = 0xfefefefefefefefeL; private final long notHFile = 0x7f7f7f7f7f7f7f7fL; - public BitboardReversi(Player[] players) { + public BitboardReversi(Player[] players) { super(8, 8, 2, players); // Black (player 0) diff --git a/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java b/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java index 0927431..c467e97 100644 --- a/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java +++ b/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java @@ -5,7 +5,7 @@ import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.player.Player; import org.toop.game.BitboardGame; -public class BitboardTicTacToe extends BitboardGame { +public class BitboardTicTacToe extends BitboardGame { private final long[] winningLines = { 0b111000000L, // top row 0b000111000L, // middle row @@ -17,7 +17,7 @@ public class BitboardTicTacToe extends BitboardGame { 0b001010100L // anti-diagonal }; - public BitboardTicTacToe(Player[] players) { + public BitboardTicTacToe(Player[] players) { super(3, 3, 2, players); } public BitboardTicTacToe(BitboardTicTacToe other) { diff --git a/game/src/main/java/org/toop/game/players/ArtificialPlayer.java b/game/src/main/java/org/toop/game/players/ArtificialPlayer.java index 418cbed..5cfecff 100644 --- a/game/src/main/java/org/toop/game/players/ArtificialPlayer.java +++ b/game/src/main/java/org/toop/game/players/ArtificialPlayer.java @@ -12,22 +12,22 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame; * * @param the specific type of game this AI player can play */ -public class ArtificialPlayer> extends AbstractPlayer { +public class ArtificialPlayer extends AbstractPlayer { /** The AI instance used to calculate moves. */ - private final AI ai; + private final AI ai; /** * Constructs a new ArtificialPlayer using the specified AI. * * @param ai the AI instance that determines moves for this player */ - public ArtificialPlayer(AI ai, String name) { + public ArtificialPlayer(AI ai, String name) { super(name); this.ai = ai; } - public ArtificialPlayer(ArtificialPlayer other) { + public ArtificialPlayer(ArtificialPlayer other) { super(other); this.ai = other.ai.deepCopy(); } @@ -44,12 +44,12 @@ public class ArtificialPlayer> extends AbstractPlayer * @return the integer representing the chosen move * @throws ClassCastException if {@code gameCopy} is not of type {@code T} */ - public long getMove(T gameCopy) { + public long getMove(TurnBasedGame gameCopy) { return ai.getMove(gameCopy); } @Override - public ArtificialPlayer deepCopy() { - return new ArtificialPlayer(this); + public ArtificialPlayer deepCopy() { + return new ArtificialPlayer(this); } } diff --git a/game/src/main/java/org/toop/game/players/LocalPlayer.java b/game/src/main/java/org/toop/game/players/LocalPlayer.java index 8f3b94d..47ed5db 100644 --- a/game/src/main/java/org/toop/game/players/LocalPlayer.java +++ b/game/src/main/java/org/toop/game/players/LocalPlayer.java @@ -7,7 +7,7 @@ import org.toop.framework.gameFramework.model.player.Player; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -public class LocalPlayer> extends AbstractPlayer { +public class LocalPlayer extends AbstractPlayer { // Future can be used with event system, IF unsubscribeAfterSuccess works... // private CompletableFuture LastMove = new CompletableFuture<>(); @@ -17,12 +17,12 @@ public class LocalPlayer> extends AbstractPlayer { super(name); } - public LocalPlayer(LocalPlayer other) { + public LocalPlayer(LocalPlayer other) { super(other); } @Override - public long getMove(T gameCopy) { + public long getMove(TurnBasedGame gameCopy) { return getValidMove(gameCopy); } @@ -36,7 +36,7 @@ public class LocalPlayer> extends AbstractPlayer { return false; } - private long getMove2(T gameCopy) { + private long getMove2(TurnBasedGame gameCopy) { LastMove = new CompletableFuture<>(); long move = 0; try { @@ -49,7 +49,7 @@ public class LocalPlayer> extends AbstractPlayer { return move; } - protected long getValidMove(T gameCopy){ + protected long getValidMove(TurnBasedGame gameCopy){ // Get this player's valid moves long validMoves = gameCopy.getLegalMoves(); // Make sure provided move is valid @@ -64,8 +64,8 @@ public class LocalPlayer> extends AbstractPlayer { } @Override - public LocalPlayer deepCopy() { - return new LocalPlayer(this.getName()); + public LocalPlayer deepCopy() { + return new LocalPlayer(this.getName()); } /*public void register() { diff --git a/game/src/main/java/org/toop/game/players/MiniMaxAI.java b/game/src/main/java/org/toop/game/players/MiniMaxAI.java index 440bb50..39e254d 100644 --- a/game/src/main/java/org/toop/game/players/MiniMaxAI.java +++ b/game/src/main/java/org/toop/game/players/MiniMaxAI.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; -public class MiniMaxAI> extends AbstractAI { +public class MiniMaxAI extends AbstractAI { private final int maxDepth; private final Random random = new Random(); @@ -18,17 +18,17 @@ public class MiniMaxAI> extends AbstractAI { this.maxDepth = depth; } - public MiniMaxAI(MiniMaxAI other) { + public MiniMaxAI(MiniMaxAI other) { this.maxDepth = other.maxDepth; } @Override - public MiniMaxAI deepCopy() { - return new MiniMaxAI<>(this); + public MiniMaxAI deepCopy() { + return new MiniMaxAI(this); } @Override - public long getMove(T game) { + public long getMove(TurnBasedGame game) { long legalMoves = game.getLegalMoves(); if (legalMoves == 0) return 0; @@ -39,7 +39,7 @@ public class MiniMaxAI> extends AbstractAI { long movesLoop = legalMoves; while (movesLoop != 0) { long move = 1L << Long.numberOfTrailingZeros(movesLoop); - T copy = game.deepCopy(); + TurnBasedGame copy = game.deepCopy(); PlayResult result = copy.play(move); int score; @@ -75,7 +75,7 @@ public class MiniMaxAI> extends AbstractAI { * @param beta Beta value * @return score of the position */ - private int getMoveScore(T game, int depth, boolean maximizing, int aiPlayer, int alpha, int beta) { + private int getMoveScore(TurnBasedGame game, int depth, boolean maximizing, int aiPlayer, int alpha, int beta) { long legalMoves = game.getLegalMoves(); // Terminal state @@ -95,7 +95,7 @@ public class MiniMaxAI> extends AbstractAI { while (movesLoop != 0) { long move = 1L << Long.numberOfTrailingZeros(movesLoop); - T copy = game.deepCopy(); + TurnBasedGame copy = game.deepCopy(); PlayResult result = copy.play(move); int score; @@ -130,7 +130,7 @@ public class MiniMaxAI> extends AbstractAI { * @param aiPlayer AI's player index * @return heuristic score */ - private int evaluateBoard(T game, int aiPlayer) { + private int evaluateBoard(TurnBasedGame game, int aiPlayer) { long[] board = game.getBoard(); int aiCount = 0; int opponentCount = 0; diff --git a/game/src/main/java/org/toop/game/players/OnlinePlayer.java b/game/src/main/java/org/toop/game/players/OnlinePlayer.java index 9f011c0..96cc7a1 100644 --- a/game/src/main/java/org/toop/game/players/OnlinePlayer.java +++ b/game/src/main/java/org/toop/game/players/OnlinePlayer.java @@ -12,7 +12,7 @@ import org.toop.framework.gameFramework.model.player.Player; * Currently, this class is a placeholder and does not implement move logic. *

*/ -public class OnlinePlayer> extends AbstractPlayer { +public class OnlinePlayer extends AbstractPlayer { /** * Constructs a new OnlinePlayer. @@ -25,12 +25,12 @@ public class OnlinePlayer> extends AbstractPlayer super(name); } - public OnlinePlayer(OnlinePlayer other) { + public OnlinePlayer(OnlinePlayer other) { super(other); } @Override - public Player deepCopy() { - return new OnlinePlayer<>(this); + public Player deepCopy() { + return new OnlinePlayer(this); } } diff --git a/game/src/main/java/org/toop/game/players/RandomAI.java b/game/src/main/java/org/toop/game/players/RandomAI.java index 2d0fe02..9a867a7 100644 --- a/game/src/main/java/org/toop/game/players/RandomAI.java +++ b/game/src/main/java/org/toop/game/players/RandomAI.java @@ -6,19 +6,19 @@ import org.toop.framework.gameFramework.model.player.AbstractAI; import java.util.Random; -public class RandomAI> extends AbstractAI { +public class RandomAI extends AbstractAI { public RandomAI() { super(); } @Override - public RandomAI deepCopy() { - return new RandomAI(); + public RandomAI deepCopy() { + return new RandomAI(); } @Override - public long getMove(T game) { + public long getMove(TurnBasedGame game) { long legalMoves = game.getLegalMoves(); int move = new Random().nextInt(Long.bitCount(legalMoves)); return nthBitIndex(legalMoves, move); From c30c118c0430ca6e196e081f0c2a5712c14fd2f7 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Fri, 12 Dec 2025 16:04:12 +0100 Subject: [PATCH 05/35] Code cleanup --- app/src/main/java/org/toop/app/App.java | 9 +++++---- .../main/java/org/toop/app/canvas/BitGameCanvas.java | 2 +- .../java/org/toop/app/canvas/ReversiBitCanvas.java | 8 ++------ .../java/org/toop/app/canvas/TicTacToeBitCanvas.java | 8 ++------ .../framework/game/games/reversi/BitboardReversi.java | 5 +++-- .../game/games/tictactoe/BitboardTicTacToe.java | 4 ++-- .../org/toop/framework/networking/server/Game.java | 10 +++++----- .../framework/networking/server/GameDefinition.java | 2 ++ .../framework/networking/server/MasterServer.java | 3 ++- .../toop/framework/networking/server/OnlineGame.java | 4 ++-- .../org/toop/framework/networking/server/Server.java | 11 ++++++----- .../toop/framework/networking/server/ServerTest.java | 11 +++++++++++ 12 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 framework/src/test/java/org/toop/framework/networking/server/ServerTest.java diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index b9e0d8d..2bce12a 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -22,6 +22,7 @@ import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.GlobalEventBus; import org.toop.framework.game.BitboardGame; import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.networking.connection.NetworkingClientEventListener; import org.toop.framework.networking.connection.NetworkingClientManager; import org.toop.framework.networking.server.GameDefinition; @@ -141,9 +142,9 @@ public final class App extends Application { stage.show(); - var games = new ConcurrentHashMap>>(); - games.put("tictactoe", new GameDefinition<>("tictactoe", BitboardReversi.class)); - games.put("reversi", new GameDefinition<>("reversi", BitboardReversi.class)); + var games = new ConcurrentHashMap>(); + games.put("tictactoe", BitboardReversi.class); + games.put("reversi", BitboardReversi.class); var s = new MasterServer(6666, games); try { s.start(); @@ -156,7 +157,7 @@ public final class App extends Application { private void setKeybinds(StackPane root) { root.addEventHandler(KeyEvent.KEY_PRESSED,event -> { if (event.getCode() == KeyCode.ESCAPE) { - escapePopup(); + escapePopup(); } }); stage.setFullScreenExitKeyCombination( diff --git a/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java index 5beb7af..2f00e10 100644 --- a/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java @@ -14,7 +14,7 @@ import org.toop.framework.gameFramework.view.GUIEvents; import java.util.function.Consumer; -public abstract class BitGameCanvas implements GameCanvas { +public abstract class BitGameCanvas implements GameCanvas { protected record Cell(float x, float y, float width, float height) { public boolean isInside(double x, double y) { return x >= this.x && x <= this.x + width && diff --git a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java index 016d6b3..685a985 100644 --- a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java @@ -3,10 +3,6 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import org.toop.game.games.reversi.BitboardReversi; - -import java.util.Arrays; -import java.util.function.Consumer; public class ReversiBitCanvas extends BitGameCanvas { public ReversiBitCanvas() { @@ -37,7 +33,7 @@ public class ReversiBitCanvas extends BitGameCanvas { public void redraw(TurnBasedGame gameCopy) { clearAll(); long[] board = gameCopy.getBoard(); - loopOverBoard(board[0], (i) -> drawDot(Color.WHITE, (int)i)); - loopOverBoard(board[1], (i) -> drawDot(Color.BLACK, (int)i)); + loopOverBoard(board[0], (i) -> drawDot(Color.WHITE, i)); + loopOverBoard(board[1], (i) -> drawDot(Color.BLACK, i)); } } diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java index 3520417..7c9b5b4 100644 --- a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java @@ -3,10 +3,6 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import org.toop.game.games.tictactoe.BitboardTicTacToe; - -import java.util.Arrays; -import java.util.function.Consumer; public class TicTacToeBitCanvas extends BitGameCanvas{ public TicTacToeBitCanvas() { @@ -29,8 +25,8 @@ public class TicTacToeBitCanvas extends BitGameCanvas{ } private void drawMoves(long[] gameBoard){ - loopOverBoard(gameBoard[0], (i) -> drawX(Color.RED, (int)i)); - loopOverBoard(gameBoard[1], (i) -> drawO(Color.BLUE, (Integer) i)); + loopOverBoard(gameBoard[0], (i) -> drawX(Color.RED, i)); + loopOverBoard(gameBoard[1], (i) -> drawO(Color.BLUE, i)); } diff --git a/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java index 867322f..d3661ac 100644 --- a/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java +++ b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java @@ -1,9 +1,10 @@ -package org.toop.game.games.reversi; +package org.toop.framework.game.games.reversi; +import org.toop.framework.game.BitboardGame; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.BitboardGame; +import org.toop.framework.game.BitboardGame; public class BitboardReversi extends BitboardGame { diff --git a/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java b/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java index c467e97..378db7d 100644 --- a/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java +++ b/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java @@ -1,9 +1,9 @@ -package org.toop.game.games.tictactoe; +package org.toop.framework.game.games.tictactoe; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.BitboardGame; +import org.toop.framework.game.BitboardGame; public class BitboardTicTacToe extends BitboardGame { private final long[] winningLines = { diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/Game.java index 057e37c..48f5036 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Game.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Game.java @@ -1,14 +1,14 @@ package org.toop.framework.networking.server; -import org.toop.framework.game.BitboardGame; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public class Game implements OnlineGame { +public class Game implements OnlineGame { private long id; private User[] users; - private GameDefinition> game; + private TurnBasedGame game; - public Game(GameDefinition game, User... users) { + public Game(TurnBasedGame game, User... users) { this.game = game; this.users = users; } @@ -19,7 +19,7 @@ public class Game implements OnlineGame { } @Override - public GameDefinition game() { + public TurnBasedGame game() { return game; } diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java b/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java index 629bc24..bd8252d 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java +++ b/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java @@ -1,5 +1,7 @@ package org.toop.framework.networking.server; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; + import java.lang.reflect.InvocationTargetException; public class GameDefinition { diff --git a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java index 634ae6f..3f607b4 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java @@ -13,6 +13,7 @@ import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import org.toop.framework.SnowflakeGenerator; import org.toop.framework.game.BitboardGame; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; import java.util.Map; @@ -20,7 +21,7 @@ public class MasterServer { private final int port; public final Server gs; - public MasterServer(int port, Map>> gameTypes) { + public MasterServer(int port, Map> gameTypes) { this.port = port; this.gs = new Server(gameTypes); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java index 59b3399..1b072a9 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java +++ b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java @@ -1,7 +1,7 @@ package org.toop.framework.networking.server; -public interface OnlineGame { +public interface OnlineGame { long id(); - GameDefinition game(); + T game(); User[] users(); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index 757c490..d2cc414 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -1,6 +1,7 @@ package org.toop.framework.networking.server; import org.toop.framework.game.BitboardGame; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; import java.util.ArrayList; import java.util.List; @@ -9,11 +10,11 @@ import java.util.concurrent.ConcurrentHashMap; public class Server implements GameServer { - final private Map>> gameTypes; - public List games = new ArrayList<>(); + final private Map> gameTypes; + public List> games = new ArrayList<>(); final private Map users = new ConcurrentHashMap<>(); - public Server(Map>> gameTypes) { + public Server(Map> gameTypes) { this.gameTypes = gameTypes; } @@ -37,8 +38,8 @@ public class Server implements GameServer { if (!gameTypes.containsKey(gameType)) return; try { - var game = new Game(gameTypes.get(gameType).create(), users); - games.addLast(new Game(game, users)); + var game = new Game(gameTypes.get(gameType).getDeclaredConstructor().newInstance(), users); + games.addLast(game); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java new file mode 100644 index 0000000..0f473f4 --- /dev/null +++ b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java @@ -0,0 +1,11 @@ +package org.toop.framework.networking.server; + +import org.junit.jupiter.api.BeforeEach; + +public class ServerTest { + + @BeforeEach + public void setup() { + new Server(); + } +} From a60c702306a41733c410d6a2a7d576f515582b1f Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Fri, 12 Dec 2025 16:47:17 +0100 Subject: [PATCH 06/35] Tests and better instantiation --- app/src/main/java/org/toop/app/App.java | 11 --- .../gameControllers/ReversiBitController.java | 3 +- .../TicTacToeBitController.java | 3 +- .../org/toop/framework/game/BitboardGame.java | 12 ++- .../game/games/reversi/BitboardReversi.java | 9 +- .../games/tictactoe/BitboardTicTacToe.java | 10 ++- .../model/game/TurnBasedGame.java | 3 + .../gameFramework/model/player/Player.java | 4 +- .../framework/networking/server/Server.java | 25 ++++-- .../networking/server/ServerTest.java | 90 ++++++++++++++++++- 10 files changed, 136 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 2bce12a..aa2e704 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -141,17 +141,6 @@ public final class App extends Application { } stage.show(); - - var games = new ConcurrentHashMap>(); - games.put("tictactoe", BitboardReversi.class); - games.put("reversi", BitboardReversi.class); - var s = new MasterServer(6666, games); - try { - s.start(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } private void setKeybinds(StackPane root) { diff --git a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java index 1ef96dc..9cd62f3 100644 --- a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java @@ -10,7 +10,8 @@ import org.toop.framework.game.players.OnlinePlayer; public class ReversiBitController extends GenericGameController { public ReversiBitController(Player[] players) { - BitboardReversi game = new BitboardReversi(players); + BitboardReversi game = new BitboardReversi(); + game.init(players); ThreadBehaviour thread = new LocalThreadBehaviour(game); for (Player player : players) { if (player instanceof OnlinePlayer){ diff --git a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java index 1c431b1..18acdfc 100644 --- a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java @@ -10,7 +10,8 @@ import org.toop.framework.game.players.OnlinePlayer; public class TicTacToeBitController extends GenericGameController { public TicTacToeBitController(Player[] players) { - BitboardTicTacToe game = new BitboardTicTacToe(players); + BitboardTicTacToe game = new BitboardTicTacToe(); + game.init(players); ThreadBehaviour thread = new LocalThreadBehaviour(game); for (Player player : players) { if (player instanceof OnlinePlayer){ diff --git a/framework/src/main/java/org/toop/framework/game/BitboardGame.java b/framework/src/main/java/org/toop/framework/game/BitboardGame.java index 4cd92a4..f2781cd 100644 --- a/framework/src/main/java/org/toop/framework/game/BitboardGame.java +++ b/framework/src/main/java/org/toop/framework/game/BitboardGame.java @@ -13,13 +13,19 @@ public abstract class BitboardGame implements TurnBasedGame { private Player[] players; // long is 64 bits. Every game has a limit of 64 cells maximum. - private final long[] playerBitboard; + private long[] playerBitboard; private int currentTurn = 0; + private int playerCount; - public BitboardGame(int columnSize, int rowSize, int playerCount, Player[] players) { + public BitboardGame(int columnSize, int rowSize, int playerCount) { this.columnSize = columnSize; this.rowSize = rowSize; - this.players = players; + this.playerCount = playerCount; + } + + @Override + public void init(Player[] players) { + this.players = players; this.playerBitboard = new long[playerCount]; Arrays.fill(playerBitboard, 0L); diff --git a/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java index d3661ac..c36f236 100644 --- a/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java +++ b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java @@ -13,8 +13,8 @@ public class BitboardReversi extends BitboardGame { private final long notAFile = 0xfefefefefefefefeL; private final long notHFile = 0x7f7f7f7f7f7f7f7fL; - public BitboardReversi(Player[] players) { - super(8, 8, 2, players); + public BitboardReversi() { + super(8, 8, 2); // Black (player 0) setPlayerBitboard(0, (1L << (3 + 4 * 8)) | (1L << (4 + 3 * 8))); @@ -23,6 +23,11 @@ public class BitboardReversi extends BitboardGame { setPlayerBitboard(1, (1L << (3 + 3 * 8)) | (1L << (4 + 4 * 8))); } + @Override + public void init(Player[] players) { + super.init(players); + } + public BitboardReversi(BitboardReversi other) { super(other); } diff --git a/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java b/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java index 378db7d..7115ccc 100644 --- a/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java +++ b/framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java @@ -17,9 +17,15 @@ public class BitboardTicTacToe extends BitboardGame { 0b001010100L // anti-diagonal }; - public BitboardTicTacToe(Player[] players) { - super(3, 3, 2, players); + public BitboardTicTacToe() { + super(3, 3, 2); } + + @Override + public void init(Player[] players) { + super.init(players); + } + public BitboardTicTacToe(BitboardTicTacToe other) { super(other); } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java b/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java index dae192d..1e6642d 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java @@ -1,6 +1,9 @@ package org.toop.framework.gameFramework.model.game; +import org.toop.framework.gameFramework.model.player.Player; + public interface TurnBasedGame extends Playable, PlayerProvider, BoardProvider, DeepCopyable { + void init(Player[] players); int getCurrentTurn(); int getPlayerCount(); int getWinner(); diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java b/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java index a3f1b32..35ea2b2 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java @@ -1,7 +1,5 @@ package org.toop.framework.gameFramework.model.player; import org.toop.framework.gameFramework.model.game.DeepCopyable; -import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public interface Player extends NameProvider, MoveProvider, DeepCopyable { -} +public interface Player extends NameProvider, MoveProvider, DeepCopyable {} diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index d2cc414..deb3d99 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -1,7 +1,9 @@ package org.toop.framework.networking.server; import org.toop.framework.game.BitboardGame; +import org.toop.framework.game.players.LocalPlayer; import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.gameFramework.model.player.Player; import java.util.ArrayList; import java.util.List; @@ -11,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap; public class Server implements GameServer { final private Map> gameTypes; - public List> games = new ArrayList<>(); + final private List> games = new ArrayList<>(); final private Map users = new ConcurrentHashMap<>(); public Server(Map> gameTypes) { @@ -30,20 +32,25 @@ public class Server implements GameServer { return gameTypes.keySet().toArray(new String[0]); } -// public List>> ongoingGames() { -// return List.of(); -// } + public List> ongoingGames() { + return games; + } public void startGame(String gameType, User... users) { if (!gameTypes.containsKey(gameType)) return; try { - var game = new Game(gameTypes.get(gameType).getDeclaredConstructor().newInstance(), users); - games.addLast(game); - } catch (Exception e) { - throw new RuntimeException(e); - } + Player[] players = new Player[users.length]; + for (int i = 0; i < users.length; i++) { + players[i] = new LocalPlayer(users[i].name()); + } + + var game = new Game(gameTypes.get(gameType).getDeclaredConstructor().newInstance(), users); + game.game().init(players); + games.addLast(game); + + } catch (Exception ignored) {} } public String[] onlineUsers() { diff --git a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java index 0f473f4..c85674c 100644 --- a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java +++ b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java @@ -1,11 +1,97 @@ package org.toop.framework.networking.server; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.toop.framework.gameFramework.model.game.PlayResult; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.gameFramework.model.player.Player; + +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerTest { + static class TurnBasedGameMock implements TurnBasedGame { + private Player[] players; + + public TurnBasedGameMock() {} + + @Override + public void init(Player[] players) { + this.players = players; + } + + @Override + public int getCurrentTurn() { + return 0; + } + + @Override + public int getPlayerCount() { + return 0; + } + + @Override + public int getWinner() { + return 0; + } + + @Override + public long[] getBoard() { + return new long[0]; + } + + @Override + public TurnBasedGame deepCopy() { + return null; + } + + @Override + public long getLegalMoves() { + return 0; + } + + @Override + public PlayResult play(long move) { + return null; + } + + @Override + public Player getPlayer(int index) { + return null; + } + + } + + private Server server; + @BeforeEach - public void setup() { - new Server(); + void setup() { + + var games = new ConcurrentHashMap>(); + games.put("tictactoe", TurnBasedGameMock.class); + games.put("reversi", TurnBasedGameMock.class); + + server = new Server(games); + } + + @Test + void testGameTypes() { + String[] expected = {"tictactoe", "reversi"}; + String[] actual = server.gameTypes(); + + Arrays.sort(expected); + Arrays.sort(actual); + + Assertions.assertArrayEquals(expected, actual); + } + + @Test + void testStartGame() { + server.startGame("tictactoe", new User(0, "A"), new User(1, "B")); + Assertions.assertEquals(1, server.ongoingGames().size()); } } From fc47d81b8eac90c45e353dbba3e77acf09d8a2e3 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Fri, 12 Dec 2025 19:47:51 +0100 Subject: [PATCH 07/35] Init challenges --- .../networking/server/GameChallenge.java | 24 ++++++++ .../networking/server/GameChallengeTimer.java | 33 +++++++++++ .../networking/server/MasterServer.java | 5 +- .../framework/networking/server/Server.java | 59 +++++++++++++++++-- .../networking/server/SimpleTimer.java | 7 +++ .../networking/server/ServerTest.java | 54 ++++++++++++++++- 6 files changed, 174 insertions(+), 8 deletions(-) create mode 100644 framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/GameChallengeTimer.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/SimpleTimer.java diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java b/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java new file mode 100644 index 0000000..ba71f89 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java @@ -0,0 +1,24 @@ +package org.toop.framework.networking.server; + +public class GameChallenge { + private final ServerUser from; + private final ServerUser to; + private final SimpleTimer timer; + + private boolean isChallengeAccepted = false; + + public GameChallenge(ServerUser from, ServerUser to, SimpleTimer timer) { + this.from = from; + this.to = to; + this.timer = timer; + } + + public void acceptChallenge() { + isChallengeAccepted = true; + timer.forceExpire(); + } + + public boolean isExpired() { + return timer.isExpired(); + } +} \ No newline at end of file diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameChallengeTimer.java b/framework/src/main/java/org/toop/framework/networking/server/GameChallengeTimer.java new file mode 100644 index 0000000..1406648 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/GameChallengeTimer.java @@ -0,0 +1,33 @@ +package org.toop.framework.networking.server; + +import java.time.Instant; +import java.time.Duration; + +public class GameChallengeTimer implements SimpleTimer { + + private final Instant createdAt; + private final Duration timeout; + + private boolean isExpired = false; + + public GameChallengeTimer(Duration duration) { + this.createdAt = Instant.now(); + this.timeout = duration; + } + + @Override + public void forceExpire() { + this.isExpired = true; + } + + @Override + public boolean isExpired() { + if (this.isExpired) return true; + return Instant.now().isAfter(createdAt.plus(timeout)); + } + + @Override + public long secondsRemaining() { + return Duration.between(Instant.now(), createdAt.plus(timeout)).toSeconds(); + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java index 3f607b4..587a569 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java @@ -15,15 +15,16 @@ import org.toop.framework.SnowflakeGenerator; import org.toop.framework.game.BitboardGame; import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import java.time.Duration; import java.util.Map; public class MasterServer { private final int port; public final Server gs; - public MasterServer(int port, Map> gameTypes) { + public MasterServer(int port, Map> gameTypes, Duration challengeDuration) { this.port = port; - this.gs = new Server(gameTypes); + this.gs = new Server(gameTypes, challengeDuration); } public void start() throws InterruptedException { diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index deb3d99..be209ca 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -1,23 +1,34 @@ package org.toop.framework.networking.server; -import org.toop.framework.game.BitboardGame; import org.toop.framework.game.players.LocalPlayer; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.Player; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.*; +import java.time.Duration; public class Server implements GameServer { final private Map> gameTypes; - final private List> games = new ArrayList<>(); final private Map users = new ConcurrentHashMap<>(); + final private List gameChallenges = new CopyOnWriteArrayList<>(); + final private List> games = new CopyOnWriteArrayList<>(); - public Server(Map> gameTypes) { + final private Duration challengeDuration; + final private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + public Server(Map> gameTypes, Duration challengeDuration) { this.gameTypes = gameTypes; + this.challengeDuration = challengeDuration; + + scheduler.schedule(this::serverTask, 0, TimeUnit.MILLISECONDS); + } + + private void serverTask() { + checkChallenges(); + scheduler.schedule(this::serverTask, 500, TimeUnit.MILLISECONDS); } public void addUser(ServerUser user) { @@ -36,6 +47,37 @@ public class Server implements GameServer { return games; } + public ServerUser getUser(String username) { + return users.values().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().get(); + } + + public ServerUser getUser(long id) { + return users.get(id); + } + + public void challengeUser(String fromUser, String toUser) { + ServerUser from = getUser(fromUser); + if (from == null) { + return; + } + ServerUser to = getUser(toUser); + if (to == null) { + return; + } + + gameChallenges.addLast(new GameChallenge(from, to, new GameChallengeTimer(challengeDuration))); + } + + public void checkChallenges() { + for (int i = gameChallenges.size() - 1; i >= 0; i--) { + if (gameChallenges.get(i).isExpired()) gameChallenges.remove(i); + } + } + + public List gameChallenges() { + return gameChallenges; + } + public void startGame(String gameType, User... users) { if (!gameTypes.containsKey(gameType)) return; @@ -53,6 +95,13 @@ public class Server implements GameServer { } catch (Exception ignored) {} } +// public void checkGames() { +// for (int i = games.size() - 1; i >= 0; i--) { +// var game = games.get(i); +// if (game.game().getWinner() >= 0) games.remove(i); +// } +// } + public String[] onlineUsers() { return users.values().stream().map(ServerUser::name).toArray(String[]::new); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/SimpleTimer.java b/framework/src/main/java/org/toop/framework/networking/server/SimpleTimer.java new file mode 100644 index 0000000..c9c25aa --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/SimpleTimer.java @@ -0,0 +1,7 @@ +package org.toop.framework.networking.server; + +public interface SimpleTimer { + void forceExpire(); + boolean isExpired(); + long secondsRemaining(); +} diff --git a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java index c85674c..6f663bb 100644 --- a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java +++ b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java @@ -7,6 +7,7 @@ import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.Player; +import java.time.Duration; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; @@ -66,7 +67,35 @@ public class ServerTest { } + static class TestUser implements ServerUser { + + final private long id; + + private String name; + + public TestUser(long id, String name) { + this.id = id; + this.name = name; + } + + @Override + public long id() { + return id; + } + + @Override + public String name() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + } + private Server server; + private Duration waitTime = Duration.ofSeconds(2); @BeforeEach void setup() { @@ -75,7 +104,7 @@ public class ServerTest { games.put("tictactoe", TurnBasedGameMock.class); games.put("reversi", TurnBasedGameMock.class); - server = new Server(games); + server = new Server(games, waitTime); } @Test @@ -89,9 +118,32 @@ public class ServerTest { Assertions.assertArrayEquals(expected, actual); } + @Test + void testChallenge() { + server.addUser(new TestUser(1, "test1")); + server.addUser(new TestUser(2, "test2")); + server.challengeUser("test1", "test2"); + + IO.println(server.gameChallenges()); + + Assertions.assertEquals(1, server.gameChallenges().size()); + + try { + Thread.sleep(waitTime); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + Assertions.assertEquals(0, server.gameChallenges().size()); + } + @Test void testStartGame() { server.startGame("tictactoe", new User(0, "A"), new User(1, "B")); Assertions.assertEquals(1, server.ongoingGames().size()); + server.startGame("reversi", new User(0, "A"), new User(1, "B")); + Assertions.assertEquals(2, server.ongoingGames().size()); } + + } From 4d31a8ed4488690cf5ca4fe1c64422c226ca27c3 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Fri, 12 Dec 2025 21:48:57 +0100 Subject: [PATCH 08/35] Working challenges --- app/src/main/java/org/toop/app/App.java | 16 +++- .../networking/server/GameChallenge.java | 23 +++++- .../framework/networking/server/Server.java | 80 +++++++++++++++++-- .../networking/server/ServerHandler.java | 23 +++++- .../networking/server/ServerUser.java | 6 +- .../framework/networking/server/User.java | 28 ++++++- .../networking/server/ServerTest.java | 26 +++++- .../networking/server/servertests.ps1 | 17 ++++ 8 files changed, 202 insertions(+), 17 deletions(-) create mode 100644 framework/src/test/java/org/toop/framework/networking/server/servertests.ps1 diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index aa2e704..9245318 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -22,11 +22,13 @@ import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.GlobalEventBus; import org.toop.framework.game.BitboardGame; import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.networking.connection.NetworkingClientEventListener; import org.toop.framework.networking.connection.NetworkingClientManager; import org.toop.framework.networking.server.GameDefinition; import org.toop.framework.networking.server.MasterServer; +import org.toop.framework.networking.server.Server; import org.toop.framework.resource.ResourceLoader; import org.toop.framework.resource.ResourceManager; import org.toop.framework.resource.events.AssetLoaderEvents; @@ -36,6 +38,7 @@ import org.toop.framework.resource.resources.SoundEffectAsset; import org.toop.local.AppContext; import org.toop.local.AppSettings; +import java.time.Duration; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; @@ -98,7 +101,18 @@ public final class App extends Application { WidgetContainer.setCurrentView(loading); - setOnLoadingSuccess(loading); + var games = new ConcurrentHashMap>(); + games.put("tictactoe", BitboardTicTacToe.class); + games.put("reversi", BitboardReversi.class); + + var a = new MasterServer(6666, games, Duration.ofSeconds(5)); + try { + a.start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + setOnLoadingSuccess(loading); EventFlow loadingFlow = new EventFlow(); diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java b/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java index ba71f89..0c936a5 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java +++ b/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java @@ -1,21 +1,40 @@ package org.toop.framework.networking.server; +import org.toop.framework.SnowflakeGenerator; + public class GameChallenge { + private final long id = SnowflakeGenerator.nextId(); // I don't need this, but the tournament server uses it... + private final ServerUser from; private final ServerUser to; + private final String gameType; private final SimpleTimer timer; private boolean isChallengeAccepted = false; - public GameChallenge(ServerUser from, ServerUser to, SimpleTimer timer) { + public GameChallenge(ServerUser from, ServerUser to, String gameType, SimpleTimer timer) { this.from = from; this.to = to; + this.gameType = gameType; this.timer = timer; } - public void acceptChallenge() { + public long id() { + return id; + } + + public ServerUser[] getUsers() { + return new ServerUser[]{from, to}; + } + + public void forceExpire() { + timer.forceExpire(); + } + + public String acceptChallenge() { isChallengeAccepted = true; timer.forceExpire(); + return gameType; } public boolean isExpired() { diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index be209ca..80e7792 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -4,6 +4,7 @@ import org.toop.framework.game.players.LocalPlayer; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.Player; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.*; @@ -48,29 +49,88 @@ public class Server implements GameServer { } public ServerUser getUser(String username) { - return users.values().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().get(); + return users.values().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().orElse(null); } public ServerUser getUser(long id) { return users.get(id); } - public void challengeUser(String fromUser, String toUser) { + public void challengeUser(String fromUser, String toUser, String gameType) { + ServerUser from = getUser(fromUser); if (from == null) { return; } - ServerUser to = getUser(toUser); - if (to == null) { + + if (!gameTypes.containsKey(gameType)) { + from.sendMessage("ERR gametype not found"); return; } - gameChallenges.addLast(new GameChallenge(from, to, new GameChallengeTimer(challengeDuration))); + ServerUser to = getUser(toUser); + if (to == null) { + from.sendMessage("ERR user not found"); + return; + } + + var ch = new GameChallenge(from, to, gameType, new GameChallengeTimer(challengeDuration)); + + to.sendMessage( + "\"SVR GAME CHALLENGE {CHALLENGER: \"%s\", GAMETYPE: \"%s\", CHALLENGENUMBER: \"%s\"}" + .formatted(from.name(), gameType, ch.id()) + ); + + if (!isValidChallenge(ch)) { + warnUserExpiredChallenge(from, ch.id()); + ch.forceExpire(); + return; + } + + gameChallenges.addLast(ch); + } + + private void warnUserExpiredChallenge(ServerUser user, long challengeId) { + user.sendMessage("SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: \"" + challengeId + "\"}"); + } + + private boolean isValidChallenge(GameChallenge gameChallenge) { + for (var user : gameChallenge.getUsers()) { + if (users.get(user.id()) == null) { + return false; + } + + if (user.games().length > 0) { + return false; + } + + if (gameChallenge.isExpired()) { + return false; + } + } + + return true; } public void checkChallenges() { for (int i = gameChallenges.size() - 1; i >= 0; i--) { - if (gameChallenges.get(i).isExpired()) gameChallenges.remove(i); + var challenge = gameChallenges.get(i); + + if (isValidChallenge(challenge)) continue; + + if (challenge.isExpired()) { + Arrays.stream(challenge.getUsers()).forEach(user -> warnUserExpiredChallenge(user, challenge.id())); + gameChallenges.remove(i); + } + } + } + + public void acceptChallenge(long challengeId) { + for (var challenge : gameChallenges) { + if (challenge.id() == challengeId) { + startGame(challenge.acceptChallenge(), (User[]) challenge.getUsers()); + break; + } } } @@ -105,4 +165,12 @@ public class Server implements GameServer { public String[] onlineUsers() { return users.values().stream().map(ServerUser::name).toArray(String[]::new); } + + public void closeServer() { + scheduler.shutdown(); + gameChallenges.clear(); + games.clear(); + users.clear(); + gameTypes.clear(); + } } diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java index f5da259..8357abb 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java @@ -79,7 +79,28 @@ public class ServerHandler extends SimpleChannelInboundHandler { } private void handleChallenge(ParsedMessage p) { - // TODO + if(!allowedArgs(p.args())) return; + if (p.args().length < 2) return; + + if (p.args()[0].equalsIgnoreCase("accept")) { + try { + long id = Long.parseLong(p.args()[1]); + + if (id <= 0) { + user.sendMessage("ERR id must be a positive number"); + return; + } + + server.acceptChallenge(id); + + } catch (NumberFormatException e) { + user.sendMessage("ERR id is not a valid number or too big"); + return; + } + return; + } + + server.challengeUser(user.name(), p.args()[0], p.args()[1]); } private void handleMove(ParsedMessage p) { diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java index 8e6e7a5..474c26d 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java @@ -1,9 +1,11 @@ package org.toop.framework.networking.server; -import java.net.InetSocketAddress; - public interface ServerUser { long id(); String name(); + Game[] games(); + void addGame(Game game); + void removeGame(Game game); void setName(String name); + void sendMessage(String message); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/User.java b/framework/src/main/java/org/toop/framework/networking/server/User.java index 8ae5026..9863b12 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/User.java +++ b/framework/src/main/java/org/toop/framework/networking/server/User.java @@ -2,11 +2,13 @@ package org.toop.framework.networking.server; import io.netty.channel.ChannelHandlerContext; -import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; public class User implements ServerUser { final private long id; private String name; + private final List games = new ArrayList<>(); private ChannelHandlerContext connectionContext; public User(long userId, String name) { @@ -24,11 +26,34 @@ public class User implements ServerUser { return name; } + + + @Override + public void addGame(Game game) { + games.add(game); + } + + @Override + public void removeGame(Game game) { + games.remove(game); + } + + @Override + public Game[] games() { + return games.toArray(new Game[0]); + } + @Override public void setName(String name) { this.name = name; } + @Override + public void sendMessage(String message) { + IO.println(message); + ctx().channel().writeAndFlush(message); + } + public ChannelHandlerContext ctx() { return connectionContext; } @@ -37,4 +62,5 @@ public class User implements ServerUser { this.connectionContext = ctx; } + } diff --git a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java index 6f663bb..e5dcd09 100644 --- a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java +++ b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java @@ -11,8 +11,6 @@ import java.time.Duration; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; -import static org.junit.jupiter.api.Assertions.assertEquals; - public class ServerTest { static class TurnBasedGameMock implements TurnBasedGame { @@ -88,10 +86,30 @@ public class ServerTest { return name; } + @Override + public Game[] games() { + return new Game[0]; + } + + @Override + public void addGame(Game game) { + + } + + @Override + public void removeGame(Game game) { + + } + @Override public void setName(String name) { this.name = name; } + + @Override + public void sendMessage(String message) { + + } } private Server server; @@ -122,14 +140,14 @@ public class ServerTest { void testChallenge() { server.addUser(new TestUser(1, "test1")); server.addUser(new TestUser(2, "test2")); - server.challengeUser("test1", "test2"); + server.challengeUser("test1", "test2", "tictactoe"); IO.println(server.gameChallenges()); Assertions.assertEquals(1, server.gameChallenges().size()); try { - Thread.sleep(waitTime); + Thread.sleep(waitTime.plusMillis(100)); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/framework/src/test/java/org/toop/framework/networking/server/servertests.ps1 b/framework/src/test/java/org/toop/framework/networking/server/servertests.ps1 new file mode 100644 index 0000000..9edf089 --- /dev/null +++ b/framework/src/test/java/org/toop/framework/networking/server/servertests.ps1 @@ -0,0 +1,17 @@ +$client = New-Object System.Net.Sockets.TcpClient("localhost", 6666) +$stream = $client.GetStream() +$writer = New-Object System.IO.StreamWriter($stream) +$reader = New-Object System.IO.StreamReader($stream) + +$client2 = New-Object System.Net.Sockets.TcpClient("localhost", 6666) +$stream2 = $client2.GetStream() +$writer2 = New-Object System.IO.StreamWriter($stream2) +$reader2 = New-Object System.IO.StreamReader($stream2) + +$writer.WriteLine("login john") +$writer.Flush() +$reader.ReadLine() + +$writer2.WriteLine("login hendrik") +$writer2.Flush() +$reader2.ReadLine() \ No newline at end of file From 9c20fcbc39b990a4638cbbf0e1a5a522887256b6 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:01:28 +0100 Subject: [PATCH 09/35] Fixed bugs, easy to use host button --- app/src/main/java/org/toop/app/App.java | 20 ---------- app/src/main/java/org/toop/app/Server.java | 2 +- .../org/toop/app/widget/view/OnlineView.java | 33 ++++++++++++++- .../connection/events/NetworkEvents.java | 2 +- .../networking/server/GameChallenge.java | 14 ++++--- .../framework/networking/server/Server.java | 40 ++++++++++--------- .../networking/server/ServerHandler.java | 23 +++++++++-- 7 files changed, 83 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 9245318..9645cd5 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -20,15 +20,8 @@ import org.toop.framework.audio.*; import org.toop.framework.audio.events.AudioEvents; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.GlobalEventBus; -import org.toop.framework.game.BitboardGame; -import org.toop.framework.game.games.reversi.BitboardReversi; -import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; -import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.networking.connection.NetworkingClientEventListener; import org.toop.framework.networking.connection.NetworkingClientManager; -import org.toop.framework.networking.server.GameDefinition; -import org.toop.framework.networking.server.MasterServer; -import org.toop.framework.networking.server.Server; import org.toop.framework.resource.ResourceLoader; import org.toop.framework.resource.ResourceManager; import org.toop.framework.resource.events.AssetLoaderEvents; @@ -38,9 +31,7 @@ import org.toop.framework.resource.resources.SoundEffectAsset; import org.toop.local.AppContext; import org.toop.local.AppSettings; -import java.time.Duration; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -101,17 +92,6 @@ public final class App extends Application { WidgetContainer.setCurrentView(loading); - var games = new ConcurrentHashMap>(); - games.put("tictactoe", BitboardTicTacToe.class); - games.put("reversi", BitboardReversi.class); - - var a = new MasterServer(6666, games, Duration.ofSeconds(5)); - try { - a.start(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - setOnLoadingSuccess(loading); EventFlow loadingFlow = new EventFlow(); diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index 6c73aa1..d1eb6bb 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -263,7 +263,7 @@ public final class Server { String gameType = extractQuotedValue(response.gameType()); final String finalGameType = gameType; var a = new ChallengePopup(challengerName, gameType, (playerInformation) -> { - final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", "")); + final long challengeId = Long.parseLong(response.challengeId().replaceAll("\\D", "")); new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent(); isSingleGame.set(true); }); diff --git a/app/src/main/java/org/toop/app/widget/view/OnlineView.java b/app/src/main/java/org/toop/app/widget/view/OnlineView.java index 16ed1df..2dc51a8 100644 --- a/app/src/main/java/org/toop/app/widget/view/OnlineView.java +++ b/app/src/main/java/org/toop/app/widget/view/OnlineView.java @@ -6,6 +6,13 @@ import org.toop.app.widget.complex.LabeledInputWidget; import org.toop.app.widget.complex.ViewWidget; import javafx.geometry.Pos; +import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.networking.server.MasterServer; + +import java.time.Duration; +import java.util.concurrent.ConcurrentHashMap; public class OnlineView extends ViewWidget { public OnlineView() { @@ -23,6 +30,28 @@ public class OnlineView extends ViewWidget { ); }); + var localHostButton = Primitive.button("host!", () -> { + var games = new ConcurrentHashMap>(); + games.put("tictactoe", BitboardTicTacToe.class); + games.put("reversi", BitboardReversi.class); + + var a = new MasterServer(6666, games, Duration.ofSeconds(10)); + + new Thread(() -> { + try { + a.start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }).start(); + + new Server( + "127.0.0.1", + "6666", + "host" + ); + }, false); + add(Pos.CENTER, Primitive.vbox( serverInformationHeader, Primitive.separator(), @@ -32,7 +61,9 @@ public class OnlineView extends ViewWidget { playerNameInput.getNode(), Primitive.separator(), - connectButton + connectButton, + Primitive.separator(), + localHostButton )); } } \ No newline at end of file diff --git a/framework/src/main/java/org/toop/framework/networking/connection/events/NetworkEvents.java b/framework/src/main/java/org/toop/framework/networking/connection/events/NetworkEvents.java index d6c0140..22f9588 100644 --- a/framework/src/main/java/org/toop/framework/networking/connection/events/NetworkEvents.java +++ b/framework/src/main/java/org/toop/framework/networking/connection/events/NetworkEvents.java @@ -102,7 +102,7 @@ public class NetworkEvents extends EventsBase { implements GenericEvent {} /** Requests to accept an existing challenge. */ - public record SendAcceptChallenge(long clientId, int challengeId) + public record SendAcceptChallenge(long clientId, long challengeId) implements GenericEvent {} /** Requests to forfeit the current game. */ diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java b/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java index 0c936a5..50a5c2b 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java +++ b/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java @@ -5,14 +5,14 @@ import org.toop.framework.SnowflakeGenerator; public class GameChallenge { private final long id = SnowflakeGenerator.nextId(); // I don't need this, but the tournament server uses it... - private final ServerUser from; - private final ServerUser to; + private final User from; + private final User to; private final String gameType; private final SimpleTimer timer; private boolean isChallengeAccepted = false; - public GameChallenge(ServerUser from, ServerUser to, String gameType, SimpleTimer timer) { + public GameChallenge(User from, User to, String gameType, SimpleTimer timer) { this.from = from; this.to = to; this.gameType = gameType; @@ -23,8 +23,8 @@ public class GameChallenge { return id; } - public ServerUser[] getUsers() { - return new ServerUser[]{from, to}; + public User[] getUsers() { + return new User[]{from, to}; } public void forceExpire() { @@ -37,6 +37,10 @@ public class GameChallenge { return gameType; } + public boolean isChallengeAccepted() { + return isChallengeAccepted; + } + public boolean isExpired() { return timer.isExpired(); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index 80e7792..dc35906 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -13,7 +13,7 @@ import java.time.Duration; public class Server implements GameServer { final private Map> gameTypes; - final private Map users = new ConcurrentHashMap<>(); + final private Map users = new ConcurrentHashMap<>(); final private List gameChallenges = new CopyOnWriteArrayList<>(); final private List> games = new CopyOnWriteArrayList<>(); @@ -32,53 +32,53 @@ public class Server implements GameServer { scheduler.schedule(this::serverTask, 500, TimeUnit.MILLISECONDS); } - public void addUser(ServerUser user) { + public void addUser(User user) { users.putIfAbsent(user.id(), user); } - public void removeUser(ServerUser user) { + public void removeUser(User user) { users.remove(user.id()); } - public String[] gameTypes() { - return gameTypes.keySet().toArray(new String[0]); + public List gameTypes() { + return gameTypes.keySet().stream().toList(); } public List> ongoingGames() { return games; } - public ServerUser getUser(String username) { + public User getUser(String username) { return users.values().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().orElse(null); } - public ServerUser getUser(long id) { + public User getUser(long id) { return users.get(id); } public void challengeUser(String fromUser, String toUser, String gameType) { - ServerUser from = getUser(fromUser); + User from = getUser(fromUser); if (from == null) { return; } if (!gameTypes.containsKey(gameType)) { - from.sendMessage("ERR gametype not found"); + from.sendMessage("ERR gametype not found \n"); return; } - ServerUser to = getUser(toUser); + User to = getUser(toUser); if (to == null) { - from.sendMessage("ERR user not found"); + from.sendMessage("ERR user not found \n"); return; } var ch = new GameChallenge(from, to, gameType, new GameChallengeTimer(challengeDuration)); to.sendMessage( - "\"SVR GAME CHALLENGE {CHALLENGER: \"%s\", GAMETYPE: \"%s\", CHALLENGENUMBER: \"%s\"}" - .formatted(from.name(), gameType, ch.id()) + "SVR GAME CHALLENGE {CHALLENGER: \"%s\", CHALLENGENUMBER: \"%s\", GAMETYPE: \"%s\"} \n" + .formatted(from.name(), ch.id(), gameType) ); if (!isValidChallenge(ch)) { @@ -90,8 +90,8 @@ public class Server implements GameServer { gameChallenges.addLast(ch); } - private void warnUserExpiredChallenge(ServerUser user, long challengeId) { - user.sendMessage("SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: \"" + challengeId + "\"}"); + private void warnUserExpiredChallenge(User user, long challengeId) { + user.sendMessage("SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: \"" + challengeId + "\"}" + "\n"); } private boolean isValidChallenge(GameChallenge gameChallenge) { @@ -119,7 +119,9 @@ public class Server implements GameServer { if (isValidChallenge(challenge)) continue; if (challenge.isExpired()) { - Arrays.stream(challenge.getUsers()).forEach(user -> warnUserExpiredChallenge(user, challenge.id())); + if (!challenge.isChallengeAccepted()) Arrays.stream(challenge.getUsers()) + .forEach(user -> warnUserExpiredChallenge(user, challenge.id())); + gameChallenges.remove(i); } } @@ -128,7 +130,7 @@ public class Server implements GameServer { public void acceptChallenge(long challengeId) { for (var challenge : gameChallenges) { if (challenge.id() == challengeId) { - startGame(challenge.acceptChallenge(), (User[]) challenge.getUsers()); + startGame(challenge.acceptChallenge(), challenge.getUsers()); break; } } @@ -162,8 +164,8 @@ public class Server implements GameServer { // } // } - public String[] onlineUsers() { - return users.values().stream().map(ServerUser::name).toArray(String[]::new); + public List onlineUsers() { + return users.values().stream().toList(); } public void closeServer() { diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java index 8357abb..f31912f 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java @@ -2,8 +2,10 @@ package org.toop.framework.networking.server; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.maven.surefire.shared.utils.StringUtils; import java.util.Arrays; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -17,6 +19,10 @@ public class ServerHandler extends SimpleChannelInboundHandler { this.server = server; } + private String returnQuotedString(Iterator strings) { // TODO more places this could be useful + return "\"" + StringUtils.join(strings, "\",\"") + "\""; + } + @Override public void channelActive(ChannelHandlerContext ctx) { ctx.writeAndFlush("WELCOME " + user.id() + "\n"); @@ -27,6 +33,9 @@ public class ServerHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) { + + IO.println(msg); + ParsedMessage p = parse(msg); if (p == null) return; @@ -61,8 +70,14 @@ public class ServerHandler extends SimpleChannelInboundHandler { if (!allowedArgs(p.args())) return; switch (p.args()[0]) { - case "playerlist" -> user.ctx().writeAndFlush(Arrays.toString(server.onlineUsers())); - case "gamelist" -> user.ctx().writeAndFlush(Arrays.toString(server.gameTypes())); + case "playerlist" -> { + var names = server.onlineUsers().stream().map(ServerUser::name).iterator(); + user.ctx().writeAndFlush("SVR PLAYERLIST " + returnQuotedString(names) + "\n"); + } + case "gamelist" -> { + var names = server.gameTypes().stream().iterator(); + user.ctx().writeAndFlush("SVR GAMELIST " + returnQuotedString(names) + "\n"); + } } } @@ -87,14 +102,14 @@ public class ServerHandler extends SimpleChannelInboundHandler { long id = Long.parseLong(p.args()[1]); if (id <= 0) { - user.sendMessage("ERR id must be a positive number"); + user.sendMessage("ERR id must be a positive number \n"); return; } server.acceptChallenge(id); } catch (NumberFormatException e) { - user.sendMessage("ERR id is not a valid number or too big"); + user.sendMessage("ERR id is not a valid number or too big \n"); return; } return; From 150fb2986fb087d16788ce81ea7e693ebb902dc5 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:17:16 +0100 Subject: [PATCH 10/35] Fixed tic tac toe naming --- app/src/main/java/org/toop/app/widget/view/OnlineView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/toop/app/widget/view/OnlineView.java b/app/src/main/java/org/toop/app/widget/view/OnlineView.java index 2dc51a8..c3b4e39 100644 --- a/app/src/main/java/org/toop/app/widget/view/OnlineView.java +++ b/app/src/main/java/org/toop/app/widget/view/OnlineView.java @@ -32,7 +32,7 @@ public class OnlineView extends ViewWidget { var localHostButton = Primitive.button("host!", () -> { var games = new ConcurrentHashMap>(); - games.put("tictactoe", BitboardTicTacToe.class); + games.put("tic-tac-toe", BitboardTicTacToe.class); games.put("reversi", BitboardReversi.class); var a = new MasterServer(6666, games, Duration.ofSeconds(10)); From 8b85915c741dd3f5bbdf62a853bb478b091ef3f4 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:08:10 +0100 Subject: [PATCH 11/35] Fixes --- .../framework/networking/server/Server.java | 2 +- .../networking/server/ServerHandler.java | 3 ++- .../framework/networking/server/ServerUser.java | 4 ++-- .../toop/framework/networking/server/User.java | 17 ++++++++--------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index dc35906..4db663c 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -100,7 +100,7 @@ public class Server implements GameServer { return false; } - if (user.games().length > 0) { + if (user.game() != null) { return false; } diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java index f31912f..9cfb9d7 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java @@ -119,7 +119,8 @@ public class ServerHandler extends SimpleChannelInboundHandler { } private void handleMove(ParsedMessage p) { - // TODO + if(!allowedArgs(p.args())) return; + } private ParsedMessage parse(String msg) { diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java index 474c26d..e2aa97a 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java @@ -3,9 +3,9 @@ package org.toop.framework.networking.server; public interface ServerUser { long id(); String name(); - Game[] games(); + Game game(); void addGame(Game game); - void removeGame(Game game); + void removeGame(); void setName(String name); void sendMessage(String message); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/User.java b/framework/src/main/java/org/toop/framework/networking/server/User.java index 9863b12..9a52587 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/User.java +++ b/framework/src/main/java/org/toop/framework/networking/server/User.java @@ -2,13 +2,10 @@ package org.toop.framework.networking.server; import io.netty.channel.ChannelHandlerContext; -import java.util.ArrayList; -import java.util.List; - public class User implements ServerUser { final private long id; private String name; - private final List games = new ArrayList<>(); + private Game game; private ChannelHandlerContext connectionContext; public User(long userId, String name) { @@ -30,17 +27,19 @@ public class User implements ServerUser { @Override public void addGame(Game game) { - games.add(game); + if (this.game == null) { + this.game = game; + } } @Override - public void removeGame(Game game) { - games.remove(game); + public void removeGame() { + this.game = null; } @Override - public Game[] games() { - return games.toArray(new Game[0]); + public Game game() { + return this.game; } @Override From c929abc4b86bab6539c01cf879220a8c1e1c38a4 Mon Sep 17 00:00:00 2001 From: Stef Date: Sat, 13 Dec 2025 17:08:34 +0100 Subject: [PATCH 12/35] Removed Generics, pray nothing breaks. --- .../org/toop/framework/game/BitboardGame.java | 8 +-- .../gameThreads/ServerThreadBehaviour.java | 68 +++++++++++++++++++ .../game/games/reversi/BitboardReversi.java | 11 ++- .../framework/game/players/ServerPlayer.java | 46 +++++++++++++ .../framework/networking/server/Game.java | 10 +++ .../networking/server/OnlineGame.java | 1 + .../framework/networking/server/Server.java | 18 +++-- 7 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java create mode 100644 framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java diff --git a/framework/src/main/java/org/toop/framework/game/BitboardGame.java b/framework/src/main/java/org/toop/framework/game/BitboardGame.java index f2781cd..3e26d40 100644 --- a/framework/src/main/java/org/toop/framework/game/BitboardGame.java +++ b/framework/src/main/java/org/toop/framework/game/BitboardGame.java @@ -13,20 +13,20 @@ public abstract class BitboardGame implements TurnBasedGame { private Player[] players; // long is 64 bits. Every game has a limit of 64 cells maximum. - private long[] playerBitboard; + private final long[] playerBitboard; private int currentTurn = 0; - private int playerCount; + private final int playerCount; public BitboardGame(int columnSize, int rowSize, int playerCount) { this.columnSize = columnSize; this.rowSize = rowSize; this.playerCount = playerCount; + this.playerBitboard = new long[playerCount]; } @Override public void init(Player[] players) { this.players = players; - this.playerBitboard = new long[playerCount]; Arrays.fill(playerBitboard, 0L); } @@ -34,7 +34,7 @@ public abstract class BitboardGame implements TurnBasedGame { public BitboardGame(BitboardGame other) { this.columnSize = other.columnSize; this.rowSize = other.rowSize; - + this.playerCount = other.playerCount; this.playerBitboard = other.playerBitboard.clone(); this.currentTurn = other.currentTurn; this.players = Arrays.stream(other.players) diff --git a/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java b/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java new file mode 100644 index 0000000..8789e41 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java @@ -0,0 +1,68 @@ +package org.toop.framework.game.gameThreads; + +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.gameFramework.GameState; +import org.toop.framework.gameFramework.model.game.PlayResult; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.gameFramework.model.game.threadBehaviour.AbstractThreadBehaviour; +import org.toop.framework.gameFramework.model.player.Player; +import org.toop.framework.gameFramework.view.GUIEvents; + +import java.util.function.Consumer; + +public class ServerThreadBehaviour extends AbstractThreadBehaviour implements Runnable { + private Consumer onPlayerMove; + /** + * Creates a new base behaviour for the specified game. + * + * @param game the turn-based game to control + */ + public ServerThreadBehaviour(TurnBasedGame game, Consumer onPlayerMove) { + super(game); + } + + private void notifyPlayerMove(int player) { + onPlayerMove.accept(player); + } + + /** Starts the game loop in a new thread. */ + @Override + public void start() { + if (isRunning.compareAndSet(false, true)) { + new Thread(this).start(); + } + } + + /** Stops the game loop after the current iteration. */ + @Override + public void stop() { + isRunning.set(false); + } + + @Override + public void run() { + while (isRunning.get()) { + Player currentPlayer = game.getPlayer(game.getCurrentTurn()); + long move = currentPlayer.getMove(game.deepCopy()); + PlayResult result = game.play(move); + + GameState state = result.state(); + switch (state) { + case WIN, DRAW -> { + isRunning.set(false); + new EventFlow().addPostEvent( + GUIEvents.GameEnded.class, + state == GameState.WIN, + result.player() + ).postEvent(); + } + case NORMAL, TURN_SKIPPED -> { /* continue normally */ } + default -> { + logger.error("Unexpected state {}", state); + isRunning.set(false); + throw new RuntimeException("Unknown state: " + state); + } + } + } + } +} diff --git a/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java index c36f236..e116c9a 100644 --- a/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java +++ b/framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java @@ -15,17 +15,16 @@ public class BitboardReversi extends BitboardGame { public BitboardReversi() { super(8, 8, 2); - - // Black (player 0) - setPlayerBitboard(0, (1L << (3 + 4 * 8)) | (1L << (4 + 3 * 8))); - - // White (player 1) - setPlayerBitboard(1, (1L << (3 + 3 * 8)) | (1L << (4 + 4 * 8))); } @Override public void init(Player[] players) { super.init(players); + // Black (player 0) + setPlayerBitboard(0, (1L << (3 + 4 * 8)) | (1L << (4 + 3 * 8))); + + // White (player 1) + setPlayerBitboard(1, (1L << (3 + 3 * 8)) | (1L << (4 + 4 * 8))); } public BitboardReversi(BitboardReversi other) { diff --git a/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java b/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java new file mode 100644 index 0000000..535a033 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java @@ -0,0 +1,46 @@ +package org.toop.framework.game.players; + +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.gameFramework.model.player.AbstractPlayer; +import org.toop.framework.gameFramework.model.player.Player; +import org.toop.framework.networking.server.User; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class ServerPlayer extends AbstractPlayer { + private User user; + private CompletableFuture lastMove; + + public ServerPlayer(User user) { + super(user.name()); + this.user = user; + } + + public void setMove(long move) { + lastMove.complete(move); + } + + @Override + public Player deepCopy() { + return null; + } + + @Override + public long getMove(TurnBasedGame game) { + lastMove = new CompletableFuture<>(); + System.out.println("Sending yourturn"); + user.sendMessage("SVR GAME YOURTURN {TURNMESSAGE: \"\"}\n"); + try { + return lastMove.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + return 0; + } + } + + @Override + public String getName() { + return ""; + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/Game.java index 48f5036..87e24f7 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Game.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Game.java @@ -1,5 +1,8 @@ package org.toop.framework.networking.server; +import org.toop.framework.game.gameThreads.LocalThreadBehaviour; +import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; +import org.toop.framework.game.gameThreads.ServerThreadBehaviour; import org.toop.framework.gameFramework.model.game.TurnBasedGame; public class Game implements OnlineGame { @@ -7,9 +10,11 @@ public class Game implements OnlineGame { private long id; private User[] users; private TurnBasedGame game; + private ServerThreadBehaviour gameThread; public Game(TurnBasedGame game, User... users) { this.game = game; + this.gameThread = new ServerThreadBehaviour(game, null); this.users = users; } @@ -27,4 +32,9 @@ public class Game implements OnlineGame { public User[] users() { return users; } + + @Override + public void start(){ + this.gameThread.start(); + } } diff --git a/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java index 1b072a9..b82b4b1 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java +++ b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java @@ -4,4 +4,5 @@ public interface OnlineGame { long id(); T game(); User[] users(); + void start(); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index 4db663c..7f0f71c 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -1,6 +1,9 @@ package org.toop.framework.networking.server; +import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; import org.toop.framework.game.players.LocalPlayer; +import org.toop.framework.game.players.OnlinePlayer; +import org.toop.framework.game.players.ServerPlayer; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.Player; @@ -144,16 +147,23 @@ public class Server implements GameServer { if (!gameTypes.containsKey(gameType)) return; try { - Player[] players = new Player[users.length]; for (int i = 0; i < users.length; i++) { - players[i] = new LocalPlayer(users[i].name()); + players[i] = new ServerPlayer(users[i]); } - + System.out.println("Starting Game"); var game = new Game(gameTypes.get(gameType).getDeclaredConstructor().newInstance(), users); game.game().init(players); games.addLast(game); - + users[0].sendMessage(String.format("SVR GAME MATCH {PLAYERTOMOVE: \"%s\", GAMETYPE: \"%s\", OPPONENT: \"%s\"}\n", + users[0].name(), + gameType, + users[1].name())); + users[1].sendMessage(String.format("SVR GAME MATCH {PLAYERTOMOVE: \"%s\", GAMETYPE: \"%s\", OPPONENT: \"%s\"}\n", + users[0].name(), + gameType, + users[0].name())); + game.start(); } catch (Exception ignored) {} } From edd2c24b651f1835a475a8cbc1c0b57fc72d4c14 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:22:56 +0100 Subject: [PATCH 13/35] Added ability to take ServerPlayer from user --- .../framework/networking/server/Game.java | 2 -- .../networking/server/ServerHandler.java | 15 +++++++------ .../networking/server/ServerUser.java | 7 +++++- .../framework/networking/server/User.java | 22 ++++++++++++------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/Game.java index 87e24f7..eeb75a3 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Game.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Game.java @@ -1,7 +1,5 @@ package org.toop.framework.networking.server; -import org.toop.framework.game.gameThreads.LocalThreadBehaviour; -import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; import org.toop.framework.game.gameThreads.ServerThreadBehaviour; import org.toop.framework.gameFramework.model.game.TurnBasedGame; diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java index 9cfb9d7..de33c62 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java @@ -54,20 +54,19 @@ public class ServerHandler extends SimpleChannelInboundHandler { } } - private boolean allowedArgs(String... args) { - if (args.length < 1) return false; - return true; + private boolean hasArgs(String... args) { + return (args.length >= 1); } private void handleLogin(ParsedMessage p) { - if (!allowedArgs(p.args())) return; + if (hasArgs(p.args())) return; user.setName(p.args()[0]); } private void handleGet(ParsedMessage p) { - if (!allowedArgs(p.args())) return; + if (hasArgs(p.args())) return; switch (p.args()[0]) { case "playerlist" -> { @@ -94,7 +93,7 @@ public class ServerHandler extends SimpleChannelInboundHandler { } private void handleChallenge(ParsedMessage p) { - if(!allowedArgs(p.args())) return; + if(hasArgs(p.args())) return; if (p.args().length < 2) return; if (p.args()[0].equalsIgnoreCase("accept")) { @@ -119,8 +118,10 @@ public class ServerHandler extends SimpleChannelInboundHandler { } private void handleMove(ParsedMessage p) { - if(!allowedArgs(p.args())) return; + if(hasArgs(p.args())) return; + // TODO check if not number + user.serverPlayer().setMove(Integer.parseInt(p.args()[0])); } private ParsedMessage parse(String msg) { diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java index e2aa97a..7fffb4e 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java @@ -1,10 +1,15 @@ package org.toop.framework.networking.server; +import org.toop.framework.game.players.ServerPlayer; + +import java.util.Map; + public interface ServerUser { long id(); String name(); Game game(); - void addGame(Game game); + ServerPlayer serverPlayer(); + void addGame(Game game, ServerPlayer player); void removeGame(); void setName(String name); void sendMessage(String message); diff --git a/framework/src/main/java/org/toop/framework/networking/server/User.java b/framework/src/main/java/org/toop/framework/networking/server/User.java index 9a52587..2eebcba 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/User.java +++ b/framework/src/main/java/org/toop/framework/networking/server/User.java @@ -1,11 +1,15 @@ package org.toop.framework.networking.server; import io.netty.channel.ChannelHandlerContext; +import org.toop.framework.game.players.ServerPlayer; + +import java.util.HashMap; +import java.util.Map; public class User implements ServerUser { final private long id; private String name; - private Game game; + private final Map game = new HashMap<>(); private ChannelHandlerContext connectionContext; public User(long userId, String name) { @@ -23,23 +27,25 @@ public class User implements ServerUser { return name; } - - @Override - public void addGame(Game game) { - if (this.game == null) { - this.game = game; + public void addGame(Game game, ServerPlayer player) { + if (this.game.isEmpty()) { + this.game.put(game, player); } } @Override public void removeGame() { - this.game = null; + this.game.clear(); } @Override public Game game() { - return this.game; + return this.game.keySet().iterator().next(); + } + + public ServerPlayer serverPlayer() { + return this.game.values().iterator().next(); } @Override From 22270e58dc2f61bffde4203b1f977e8d5c1ce587 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:33:14 +0100 Subject: [PATCH 14/35] Added pairs --- .../networking/server/ServerUser.java | 5 +- .../framework/networking/server/User.java | 18 +- .../toop/framework/utils/ImmutablePair.java | 21 ++ .../org/toop/framework/utils/MutablePair.java | 29 ++ .../java/org/toop/framework/utils/Pair.java | 6 + .../networking/server/ServerTest.java | 334 +++++++++--------- 6 files changed, 233 insertions(+), 180 deletions(-) create mode 100644 framework/src/main/java/org/toop/framework/utils/ImmutablePair.java create mode 100644 framework/src/main/java/org/toop/framework/utils/MutablePair.java create mode 100644 framework/src/main/java/org/toop/framework/utils/Pair.java diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java index 7fffb4e..e06098c 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java @@ -1,15 +1,14 @@ package org.toop.framework.networking.server; import org.toop.framework.game.players.ServerPlayer; - -import java.util.Map; +import org.toop.framework.utils.Pair; public interface ServerUser { long id(); String name(); Game game(); ServerPlayer serverPlayer(); - void addGame(Game game, ServerPlayer player); + void addGame(Pair gamePair); void removeGame(); void setName(String name); void sendMessage(String message); diff --git a/framework/src/main/java/org/toop/framework/networking/server/User.java b/framework/src/main/java/org/toop/framework/networking/server/User.java index 2eebcba..3078ca5 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/User.java +++ b/framework/src/main/java/org/toop/framework/networking/server/User.java @@ -2,14 +2,12 @@ package org.toop.framework.networking.server; import io.netty.channel.ChannelHandlerContext; import org.toop.framework.game.players.ServerPlayer; - -import java.util.HashMap; -import java.util.Map; +import org.toop.framework.utils.Pair; public class User implements ServerUser { final private long id; private String name; - private final Map game = new HashMap<>(); + private Pair gamePair; private ChannelHandlerContext connectionContext; public User(long userId, String name) { @@ -28,24 +26,24 @@ public class User implements ServerUser { } @Override - public void addGame(Game game, ServerPlayer player) { - if (this.game.isEmpty()) { - this.game.put(game, player); + public void addGame(Pair gamePair) { + if (this.gamePair == null) { + this.gamePair = gamePair; } } @Override public void removeGame() { - this.game.clear(); + this.gamePair = null; } @Override public Game game() { - return this.game.keySet().iterator().next(); + return this.gamePair.getLeft(); } public ServerPlayer serverPlayer() { - return this.game.values().iterator().next(); + return this.gamePair.getRight(); } @Override diff --git a/framework/src/main/java/org/toop/framework/utils/ImmutablePair.java b/framework/src/main/java/org/toop/framework/utils/ImmutablePair.java new file mode 100644 index 0000000..da8fe91 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/utils/ImmutablePair.java @@ -0,0 +1,21 @@ +package org.toop.framework.utils; + +public class ImmutablePair implements Pair { + final T left; + final K right; + + public ImmutablePair(T left, K right) { + this.left = left; + this.right = right; + } + + @Override + public T getLeft() { + return left; + } + + @Override + public K getRight() { + return right; + } +} diff --git a/framework/src/main/java/org/toop/framework/utils/MutablePair.java b/framework/src/main/java/org/toop/framework/utils/MutablePair.java new file mode 100644 index 0000000..90c9f91 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/utils/MutablePair.java @@ -0,0 +1,29 @@ +package org.toop.framework.utils; + +public class MutablePair implements Pair { + T left; + K right; + + public MutablePair(T left, K right) { + this.left = left; + this.right = right; + } + + @Override + public T getLeft() { + return left; + } + + public void setLeft(T left) { + this.left = left; + } + + @Override + public K getRight() { + return right; + } + + public void setRight(K right) { + this.right = right; + } +} diff --git a/framework/src/main/java/org/toop/framework/utils/Pair.java b/framework/src/main/java/org/toop/framework/utils/Pair.java new file mode 100644 index 0000000..26b74ab --- /dev/null +++ b/framework/src/main/java/org/toop/framework/utils/Pair.java @@ -0,0 +1,6 @@ +package org.toop.framework.utils; + +public interface Pair { + T getLeft(); + K getRight(); +} diff --git a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java index e5dcd09..789d732 100644 --- a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java +++ b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java @@ -1,167 +1,167 @@ -package org.toop.framework.networking.server; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.toop.framework.gameFramework.model.game.PlayResult; -import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import org.toop.framework.gameFramework.model.player.Player; - -import java.time.Duration; -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; - -public class ServerTest { - - static class TurnBasedGameMock implements TurnBasedGame { - private Player[] players; - - public TurnBasedGameMock() {} - - @Override - public void init(Player[] players) { - this.players = players; - } - - @Override - public int getCurrentTurn() { - return 0; - } - - @Override - public int getPlayerCount() { - return 0; - } - - @Override - public int getWinner() { - return 0; - } - - @Override - public long[] getBoard() { - return new long[0]; - } - - @Override - public TurnBasedGame deepCopy() { - return null; - } - - @Override - public long getLegalMoves() { - return 0; - } - - @Override - public PlayResult play(long move) { - return null; - } - - @Override - public Player getPlayer(int index) { - return null; - } - - } - - static class TestUser implements ServerUser { - - final private long id; - - private String name; - - public TestUser(long id, String name) { - this.id = id; - this.name = name; - } - - @Override - public long id() { - return id; - } - - @Override - public String name() { - return name; - } - - @Override - public Game[] games() { - return new Game[0]; - } - - @Override - public void addGame(Game game) { - - } - - @Override - public void removeGame(Game game) { - - } - - @Override - public void setName(String name) { - this.name = name; - } - - @Override - public void sendMessage(String message) { - - } - } - - private Server server; - private Duration waitTime = Duration.ofSeconds(2); - - @BeforeEach - void setup() { - - var games = new ConcurrentHashMap>(); - games.put("tictactoe", TurnBasedGameMock.class); - games.put("reversi", TurnBasedGameMock.class); - - server = new Server(games, waitTime); - } - - @Test - void testGameTypes() { - String[] expected = {"tictactoe", "reversi"}; - String[] actual = server.gameTypes(); - - Arrays.sort(expected); - Arrays.sort(actual); - - Assertions.assertArrayEquals(expected, actual); - } - - @Test - void testChallenge() { - server.addUser(new TestUser(1, "test1")); - server.addUser(new TestUser(2, "test2")); - server.challengeUser("test1", "test2", "tictactoe"); - - IO.println(server.gameChallenges()); - - Assertions.assertEquals(1, server.gameChallenges().size()); - - try { - Thread.sleep(waitTime.plusMillis(100)); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - Assertions.assertEquals(0, server.gameChallenges().size()); - } - - @Test - void testStartGame() { - server.startGame("tictactoe", new User(0, "A"), new User(1, "B")); - Assertions.assertEquals(1, server.ongoingGames().size()); - server.startGame("reversi", new User(0, "A"), new User(1, "B")); - Assertions.assertEquals(2, server.ongoingGames().size()); - } - - -} +//package org.toop.framework.networking.server; +// +//import org.junit.jupiter.api.Assertions; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.toop.framework.gameFramework.model.game.PlayResult; +//import org.toop.framework.gameFramework.model.game.TurnBasedGame; +//import org.toop.framework.gameFramework.model.player.Player; +// +//import java.time.Duration; +//import java.util.Arrays; +//import java.util.concurrent.ConcurrentHashMap; +// +//public class ServerTest { +// +// static class TurnBasedGameMock implements TurnBasedGame { +// private Player[] players; +// +// public TurnBasedGameMock() {} +// +// @Override +// public void init(Player[] players) { +// this.players = players; +// } +// +// @Override +// public int getCurrentTurn() { +// return 0; +// } +// +// @Override +// public int getPlayerCount() { +// return 0; +// } +// +// @Override +// public int getWinner() { +// return 0; +// } +// +// @Override +// public long[] getBoard() { +// return new long[0]; +// } +// +// @Override +// public TurnBasedGame deepCopy() { +// return null; +// } +// +// @Override +// public long getLegalMoves() { +// return 0; +// } +// +// @Override +// public PlayResult play(long move) { +// return null; +// } +// +// @Override +// public Player getPlayer(int index) { +// return null; +// } +// +// } +// +// static class TestUser implements ServerUser { +// +// final private long id; +// +// private String name; +// +// public TestUser(long id, String name) { +// this.id = id; +// this.name = name; +// } +// +// @Override +// public long id() { +// return id; +// } +// +// @Override +// public String name() { +// return name; +// } +// +// @Override +// public Game[] games() { +// return new Game[0]; +// } +// +// @Override +// public void addGame(Game game) { +// +// } +// +// @Override +// public void removeGame(Game game) { +// +// } +// +// @Override +// public void setName(String name) { +// this.name = name; +// } +// +// @Override +// public void sendMessage(String message) { +// +// } +// } +// +// private Server server; +// private Duration waitTime = Duration.ofSeconds(2); +// +// @BeforeEach +// void setup() { +// +// var games = new ConcurrentHashMap>(); +// games.put("tictactoe", TurnBasedGameMock.class); +// games.put("reversi", TurnBasedGameMock.class); +// +// server = new Server(games, waitTime); +// } +// +// @Test +// void testGameTypes() { +// String[] expected = {"tictactoe", "reversi"}; +// String[] actual = server.gameTypes(); +// +// Arrays.sort(expected); +// Arrays.sort(actual); +// +// Assertions.assertArrayEquals(expected, actual); +// } +// +// @Test +// void testChallenge() { +// server.addUser(new TestUser(1, "test1")); +// server.addUser(new TestUser(2, "test2")); +// server.challengeUser("test1", "test2", "tictactoe"); +// +// IO.println(server.gameChallenges()); +// +// Assertions.assertEquals(1, server.gameChallenges().size()); +// +// try { +// Thread.sleep(waitTime.plusMillis(100)); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// +// Assertions.assertEquals(0, server.gameChallenges().size()); +// } +// +// @Test +// void testStartGame() { +// server.startGame("tictactoe", new User(0, "A"), new User(1, "B")); +// Assertions.assertEquals(1, server.ongoingGames().size()); +// server.startGame("reversi", new User(0, "A"), new User(1, "B")); +// Assertions.assertEquals(2, server.ongoingGames().size()); +// } +// +// +//} From 89a9cb1e559ab98a184ae224d93d3c102e4cb0a2 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:37:34 +0100 Subject: [PATCH 15/35] Using pairs now in server.java --- .../toop/framework/networking/server/Server.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index 7f0f71c..bc8742a 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -1,11 +1,8 @@ package org.toop.framework.networking.server; -import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; -import org.toop.framework.game.players.LocalPlayer; -import org.toop.framework.game.players.OnlinePlayer; import org.toop.framework.game.players.ServerPlayer; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import org.toop.framework.gameFramework.model.player.Player; +import org.toop.framework.utils.ImmutablePair; import java.util.Arrays; import java.util.List; @@ -147,14 +144,18 @@ public class Server implements GameServer { if (!gameTypes.containsKey(gameType)) return; try { - Player[] players = new Player[users.length]; + ServerPlayer[] players = new ServerPlayer[users.length]; + var game = new Game(gameTypes.get(gameType).getDeclaredConstructor().newInstance(), users); + for (int i = 0; i < users.length; i++) { players[i] = new ServerPlayer(users[i]); + users[i].addGame(new ImmutablePair<>(game, players[i])); } System.out.println("Starting Game"); - var game = new Game(gameTypes.get(gameType).getDeclaredConstructor().newInstance(), users); + game.game().init(players); games.addLast(game); + users[0].sendMessage(String.format("SVR GAME MATCH {PLAYERTOMOVE: \"%s\", GAMETYPE: \"%s\", OPPONENT: \"%s\"}\n", users[0].name(), gameType, From cd5736afc80160af690d45c86a054ea50a7b8e5d Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:38:36 +0100 Subject: [PATCH 16/35] Removed space in naming --- .../src/main/java/org/toop/framework/utils/ImmutablePair.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/toop/framework/utils/ImmutablePair.java b/framework/src/main/java/org/toop/framework/utils/ImmutablePair.java index da8fe91..52abab3 100644 --- a/framework/src/main/java/org/toop/framework/utils/ImmutablePair.java +++ b/framework/src/main/java/org/toop/framework/utils/ImmutablePair.java @@ -1,6 +1,6 @@ package org.toop.framework.utils; -public class ImmutablePair implements Pair { +public class ImmutablePair implements Pair { final T left; final K right; From c015100ebf45278c96b2a99552fd0280a038bde2 Mon Sep 17 00:00:00 2001 From: Stef Date: Sat, 13 Dec 2025 17:49:54 +0100 Subject: [PATCH 17/35] Werkt nog niet --- .../gameThreads/ServerThreadBehaviour.java | 22 +++++++++++-------- .../framework/networking/server/Game.java | 7 +++++- .../networking/server/ServerHandler.java | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java b/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java index 8789e41..91f9186 100644 --- a/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java +++ b/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java @@ -7,22 +7,26 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.game.threadBehaviour.AbstractThreadBehaviour; import org.toop.framework.gameFramework.model.player.Player; import org.toop.framework.gameFramework.view.GUIEvents; +import org.toop.framework.utils.ImmutablePair; import java.util.function.Consumer; +import static org.toop.framework.gameFramework.GameState.TURN_SKIPPED; +import static org.toop.framework.gameFramework.GameState.WIN; + public class ServerThreadBehaviour extends AbstractThreadBehaviour implements Runnable { - private Consumer onPlayerMove; + private Consumer> onPlayerMove; /** * Creates a new base behaviour for the specified game. * * @param game the turn-based game to control */ - public ServerThreadBehaviour(TurnBasedGame game, Consumer onPlayerMove) { + public ServerThreadBehaviour(TurnBasedGame game, Consumer> onPlayerMove) { super(game); } - private void notifyPlayerMove(int player) { - onPlayerMove.accept(player); + private void notifyPlayerMove(ImmutablePair pair) { + onPlayerMove.accept(pair); } /** Starts the game loop in a new thread. */ @@ -47,14 +51,14 @@ public class ServerThreadBehaviour extends AbstractThreadBehaviour implements Ru PlayResult result = game.play(move); GameState state = result.state(); + + if (state != TURN_SKIPPED){ + notifyPlayerMove(new ImmutablePair<>(currentPlayer.getName(), Long.numberOfTrailingZeros(move))); + } + switch (state) { case WIN, DRAW -> { isRunning.set(false); - new EventFlow().addPostEvent( - GUIEvents.GameEnded.class, - state == GameState.WIN, - result.player() - ).postEvent(); } case NORMAL, TURN_SKIPPED -> { /* continue normally */ } default -> { diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/Game.java index eeb75a3..2c9a803 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Game.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Game.java @@ -12,10 +12,15 @@ public class Game implements OnlineGame { public Game(TurnBasedGame game, User... users) { this.game = game; - this.gameThread = new ServerThreadBehaviour(game, null); + this.gameThread = new ServerThreadBehaviour(game, (pair) -> notifyMoveMade(pair.getLeft(), pair.getRight())); this.users = users; } + private void notifyMoveMade(String speler, int move){ + users[0].sendMessage(String.format("SVR GAME MOVE {PLAYER: \"%s\", DETAILS: \"\", MOVE: \"%s\"}", speler, move)); + users[1].sendMessage(String.format("SVR GAME MOVE {PLAYER: \"%s\", DETAILS: \"\", MOVE: \"%s\"}", speler, move)); + } + @Override public long id() { return id; diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java index de33c62..48bc77e 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java @@ -121,7 +121,7 @@ public class ServerHandler extends SimpleChannelInboundHandler { if(hasArgs(p.args())) return; // TODO check if not number - user.serverPlayer().setMove(Integer.parseInt(p.args()[0])); + user.serverPlayer().setMove(1L << Integer.parseInt(p.args()[0])); } private ParsedMessage parse(String msg) { From afcd9be71ebbada6fc37684c9a5188ad1bfde353 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:53:31 +0100 Subject: [PATCH 18/35] Fixed hasArgs --- .../{ServerHandler.java => ConnectionHandler.java} | 14 +++++++------- .../framework/networking/server/MasterServer.java | 3 +-- .../toop/framework/networking/server/Server.java | 7 ------- 3 files changed, 8 insertions(+), 16 deletions(-) rename framework/src/main/java/org/toop/framework/networking/server/{ServerHandler.java => ConnectionHandler.java} (92%) diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ConnectionHandler.java similarity index 92% rename from framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java rename to framework/src/main/java/org/toop/framework/networking/server/ConnectionHandler.java index 48bc77e..8c20f32 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/server/ConnectionHandler.java @@ -9,12 +9,12 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; -public class ServerHandler extends SimpleChannelInboundHandler { +public class ConnectionHandler extends SimpleChannelInboundHandler { private final User user; private final Server server; - public ServerHandler(User user, Server server) { + public ConnectionHandler(User user, Server server) { this.user = user; this.server = server; } @@ -54,19 +54,19 @@ public class ServerHandler extends SimpleChannelInboundHandler { } } + // DO NOT INVERT private boolean hasArgs(String... args) { return (args.length >= 1); } private void handleLogin(ParsedMessage p) { - - if (hasArgs(p.args())) return; + if (!hasArgs(p.args())) return; user.setName(p.args()[0]); } private void handleGet(ParsedMessage p) { - if (hasArgs(p.args())) return; + if (!hasArgs(p.args())) return; switch (p.args()[0]) { case "playerlist" -> { @@ -93,7 +93,7 @@ public class ServerHandler extends SimpleChannelInboundHandler { } private void handleChallenge(ParsedMessage p) { - if(hasArgs(p.args())) return; + if (!hasArgs(p.args())) return; if (p.args().length < 2) return; if (p.args()[0].equalsIgnoreCase("accept")) { @@ -118,7 +118,7 @@ public class ServerHandler extends SimpleChannelInboundHandler { } private void handleMove(ParsedMessage p) { - if(hasArgs(p.args())) return; + if(!hasArgs(p.args())) return; // TODO check if not number user.serverPlayer().setMove(1L << Integer.parseInt(p.args()[0])); diff --git a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java index 587a569..4259fcd 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java @@ -12,7 +12,6 @@ import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import org.toop.framework.SnowflakeGenerator; -import org.toop.framework.game.BitboardGame; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import java.time.Duration; @@ -53,7 +52,7 @@ public class MasterServer { long userid = SnowflakeGenerator.nextId(); User user = new User(userid, ""+userid); - pipeline.addLast(new ServerHandler(user, gs)); + pipeline.addLast(new ConnectionHandler(user, gs)); } } ); diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index bc8742a..4da5c4c 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -168,13 +168,6 @@ public class Server implements GameServer { } catch (Exception ignored) {} } -// public void checkGames() { -// for (int i = games.size() - 1; i >= 0; i--) { -// var game = games.get(i); -// if (game.game().getWinner() >= 0) games.remove(i); -// } -// } - public List onlineUsers() { return users.values().stream().toList(); } From cbcce297802ad23a18973ac0f8a5e973e7f5de5b Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 18:38:31 +0100 Subject: [PATCH 19/35] Closable server --- app/src/main/java/org/toop/app/Server.java | 15 ++++++++-- .../org/toop/app/widget/view/OnlineView.java | 3 +- .../networking/server/MasterServer.java | 28 +++++++++++++++---- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index d1eb6bb..cdea35a 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -22,6 +22,7 @@ import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; import org.toop.framework.game.players.ArtificialPlayer; import org.toop.framework.game.players.OnlinePlayer; import org.toop.framework.game.players.RandomAI; +import org.toop.framework.networking.server.MasterServer; import org.toop.local.AppContext; import java.util.List; @@ -32,7 +33,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public final class Server { - // TODO: Keep track of listeners. Remove them on Server connection close so reference is deleted. + private MasterServer masterServer; + private String user = ""; private long clientId = -1; @@ -60,10 +62,13 @@ public final class Server { return null; } + public Server(String ip, String port, String user) { + this(ip, port, user, null); + } // Server has to deal with ALL network related listen events. This "server" can then interact with the manager to make stuff happen. // This prevents data races where events get sent to the game manager but the manager isn't ready yet. - public Server(String ip, String port, String user) { + public Server(String ip, String port, String user, MasterServer masterServer) { if (ip.split("\\.").length < 4) { new ErrorPopup("\"" + ip + "\" " + AppContext.getString("is-not-a-valid-ip-address")); return; @@ -83,6 +88,8 @@ public final class Server { return; } + this.masterServer = masterServer; + final int reconnectAttempts = 10; LoadingWidget loading = new LoadingWidget( @@ -281,6 +288,10 @@ public final class Server { stopScheduler(); connectFlow.unsubscribeAll(); + if (masterServer != null) { + masterServer.stop(); + } + WidgetContainer.getCurrentView().transitionPrevious(); } diff --git a/app/src/main/java/org/toop/app/widget/view/OnlineView.java b/app/src/main/java/org/toop/app/widget/view/OnlineView.java index c3b4e39..95d7603 100644 --- a/app/src/main/java/org/toop/app/widget/view/OnlineView.java +++ b/app/src/main/java/org/toop/app/widget/view/OnlineView.java @@ -48,7 +48,8 @@ public class OnlineView extends ViewWidget { new Server( "127.0.0.1", "6666", - "host" + "host", + a ); }, false); diff --git a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java index 4259fcd..b13dc58 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java @@ -19,7 +19,11 @@ import java.util.Map; public class MasterServer { private final int port; - public final Server gs; + private final Server gs; + + ChannelFuture future; + EventLoopGroup bossGroup; + EventLoopGroup workerGroup; public MasterServer(int port, Map> gameTypes, Duration challengeDuration) { this.port = port; @@ -28,11 +32,10 @@ public class MasterServer { public void start() throws InterruptedException { - EventLoopGroup bossGroup = new NioEventLoopGroup(1); - EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); + bossGroup = new NioEventLoopGroup(1); + workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); try { - ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(workerGroup); bootstrap.channel(NioServerSocketChannel.class); @@ -57,8 +60,7 @@ public class MasterServer { } ); - ChannelFuture future = bootstrap.bind(port).sync(); - System.out.println("MasterServer listening on port " + port); + future = bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); } finally { @@ -66,4 +68,18 @@ public class MasterServer { workerGroup.shutdownGracefully(); } } + + public void stop() { + if (future == null) { + return; + } + + future.channel().close(); + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + + future = null; + bossGroup = null; + workerGroup = null; + } } From 73d71f2a2d2f304c750ce6b207d469fb1cc07797 Mon Sep 17 00:00:00 2001 From: Stef Date: Sat, 13 Dec 2025 18:53:10 +0100 Subject: [PATCH 20/35] Making moves works. Game notifies when game has ended. --- .../gameThreads/ServerThreadBehaviour.java | 24 ++++++++++++------ .../framework/game/players/ServerPlayer.java | 5 ---- .../model/player/AbstractPlayer.java | 3 ++- .../framework/networking/server/Game.java | 25 ++++++++++++++++--- .../framework/networking/server/User.java | 3 +++ 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java b/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java index 91f9186..a4c5ac5 100644 --- a/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java +++ b/framework/src/main/java/org/toop/framework/game/gameThreads/ServerThreadBehaviour.java @@ -8,6 +8,7 @@ import org.toop.framework.gameFramework.model.game.threadBehaviour.AbstractThrea import org.toop.framework.gameFramework.model.player.Player; import org.toop.framework.gameFramework.view.GUIEvents; import org.toop.framework.utils.ImmutablePair; +import org.toop.framework.utils.Pair; import java.util.function.Consumer; @@ -15,18 +16,29 @@ import static org.toop.framework.gameFramework.GameState.TURN_SKIPPED; import static org.toop.framework.gameFramework.GameState.WIN; public class ServerThreadBehaviour extends AbstractThreadBehaviour implements Runnable { - private Consumer> onPlayerMove; + private final Consumer> onPlayerMove; + private final Consumer> onGameEnd; /** * Creates a new base behaviour for the specified game. * * @param game the turn-based game to control */ - public ServerThreadBehaviour(TurnBasedGame game, Consumer> onPlayerMove) { + public ServerThreadBehaviour(TurnBasedGame game, Consumer> onPlayerMove, Consumer> onGameEnd) { + this.onPlayerMove = onPlayerMove; + this.onGameEnd = onGameEnd; super(game); } private void notifyPlayerMove(ImmutablePair pair) { - onPlayerMove.accept(pair); + if (onPlayerMove != null) { + onPlayerMove.accept(pair); + } + } + + private void notifyGameEnd(ImmutablePair pair) { + if (onGameEnd != null) { + onGameEnd.accept(pair); + } } /** Starts the game loop in a new thread. */ @@ -51,14 +63,12 @@ public class ServerThreadBehaviour extends AbstractThreadBehaviour implements Ru PlayResult result = game.play(move); GameState state = result.state(); - - if (state != TURN_SKIPPED){ - notifyPlayerMove(new ImmutablePair<>(currentPlayer.getName(), Long.numberOfTrailingZeros(move))); - } + notifyPlayerMove(new ImmutablePair<>(currentPlayer.getName(), Long.numberOfTrailingZeros(move))); switch (state) { case WIN, DRAW -> { isRunning.set(false); + notifyGameEnd(new ImmutablePair<>(state, game.getWinner())); } case NORMAL, TURN_SKIPPED -> { /* continue normally */ } default -> { diff --git a/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java b/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java index 535a033..519f857 100644 --- a/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java +++ b/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java @@ -38,9 +38,4 @@ public class ServerPlayer extends AbstractPlayer { return 0; } } - - @Override - public String getName() { - return ""; - } } diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java index f643574..8a294e8 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java @@ -21,6 +21,7 @@ public abstract class AbstractPlayer implements Player { private final String name; protected AbstractPlayer(String name) { + System.out.println("Player " + name + " has been created"); this.name = name; } @@ -44,7 +45,7 @@ public abstract class AbstractPlayer implements Player { throw new UnsupportedOperationException("Not supported yet."); } - public String getName(){ + public final String getName(){ return this.name; } } diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/Game.java index 2c9a803..6903120 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Game.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Game.java @@ -1,6 +1,7 @@ package org.toop.framework.networking.server; import org.toop.framework.game.gameThreads.ServerThreadBehaviour; +import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.TurnBasedGame; public class Game implements OnlineGame { @@ -12,13 +13,31 @@ public class Game implements OnlineGame { public Game(TurnBasedGame game, User... users) { this.game = game; - this.gameThread = new ServerThreadBehaviour(game, (pair) -> notifyMoveMade(pair.getLeft(), pair.getRight())); + this.gameThread = new ServerThreadBehaviour( + game, + (pair) -> notifyMoveMade(pair.getLeft(), pair.getRight()), + (pair) -> notifyGameEnd(pair.getLeft(), pair.getRight()) + ); this.users = users; } private void notifyMoveMade(String speler, int move){ - users[0].sendMessage(String.format("SVR GAME MOVE {PLAYER: \"%s\", DETAILS: \"\", MOVE: \"%s\"}", speler, move)); - users[1].sendMessage(String.format("SVR GAME MOVE {PLAYER: \"%s\", DETAILS: \"\", MOVE: \"%s\"}", speler, move)); + for (User user : users) { + user.sendMessage(String.format("SVR GAME MOVE {PLAYER: \"%s\", MOVE: \"%s\", DETAILS: \"\"}\n", speler, move)); + } + } + + private void notifyGameEnd(GameState state, int winner){ + if (state == GameState.DRAW){ + for (User user : users) { + user.sendMessage(String.format("SVR GAME DRAW {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"Client disconnected\"}\n")); + } + } + else{ + users[winner].sendMessage(String.format("SVR GAME WIN {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"Client disconnected\"}\n")); + users[(winner + 1)%2].sendMessage(String.format("SVR GAME LOSS {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"Client disconnected\"}\n")); + } + } @Override diff --git a/framework/src/main/java/org/toop/framework/networking/server/User.java b/framework/src/main/java/org/toop/framework/networking/server/User.java index 3078ca5..34c1211 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/User.java +++ b/framework/src/main/java/org/toop/framework/networking/server/User.java @@ -39,6 +39,9 @@ public class User implements ServerUser { @Override public Game game() { + if (this.gamePair == null) { + return null; + } return this.gamePair.getLeft(); } From 0956286616b3046f811e0ecdc5aeababc46cc4cb Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 21:11:26 +0100 Subject: [PATCH 21/35] Partial server refactor --- app/src/main/java/org/toop/app/Server.java | 14 +- .../org/toop/app/widget/view/OnlineView.java | 12 +- .../framework/game/players/ServerPlayer.java | 12 +- .../networking/server/ConnectionHandler.java | 155 ------------------ .../framework/networking/server/Game.java | 23 +-- .../networking/server/GameDefinition.java | 28 ---- .../networking/server/GameServer.java | 2 +- .../networking/server/MessageStore.java | 7 - .../networking/server/OnlineGame.java | 4 +- .../framework/networking/server/Parser.java | 4 - .../networking/server/ServableGame.java | 4 - .../framework/networking/server/Server.java | 99 ++++++----- .../networking/server/ServerMessageStore.java | 27 --- .../networking/server/ServerUser.java | 15 -- .../gamechallenge}/GameChallenge.java | 14 +- .../gamechallenge}/GameChallengeTimer.java | 4 +- .../networking/server/client/Client.java | 19 +++ .../{User.java => client/NettyClient.java} | 26 ++- .../connectionHandler/ClientSession.java | 7 + .../connectionHandler/NettyClientSession.java | 64 ++++++++ .../server/gateway/GatewayServer.java | 7 + .../NettyGatewayServer.java} | 37 ++++- .../networking/server/handlers/Handler.java | 5 + .../server/handlers/MessageHandler.java | 104 ++++++++++++ .../server/{ => parsing}/ParsedMessage.java | 2 +- .../networking/server/parsing/Parser.java | 24 +++ .../networking/server/stores/ClientStore.java | 7 + .../networking/server/stores/GameStore.java | 3 + .../server/stores/NettyClientStore.java | 34 ++++ .../networking/server/stores/Store.java | 10 ++ .../server/stores/TurnBasedGameStore.java | 36 ++++ .../server/stores/TurnBasedGameTypeStore.java | 32 ++++ .../server => utils}/SimpleTimer.java | 2 +- .../java/org/toop/framework/utils/Utils.java | 11 ++ .../networking/server/ServerTest.java | 6 +- 35 files changed, 511 insertions(+), 349 deletions(-) delete mode 100644 framework/src/main/java/org/toop/framework/networking/server/ConnectionHandler.java delete mode 100644 framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java delete mode 100644 framework/src/main/java/org/toop/framework/networking/server/MessageStore.java delete mode 100644 framework/src/main/java/org/toop/framework/networking/server/Parser.java delete mode 100644 framework/src/main/java/org/toop/framework/networking/server/ServableGame.java delete mode 100644 framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java delete mode 100644 framework/src/main/java/org/toop/framework/networking/server/ServerUser.java rename framework/src/main/java/org/toop/framework/networking/server/{ => challenges/gamechallenge}/GameChallenge.java (66%) rename framework/src/main/java/org/toop/framework/networking/server/{ => challenges/gamechallenge}/GameChallengeTimer.java (86%) create mode 100644 framework/src/main/java/org/toop/framework/networking/server/client/Client.java rename framework/src/main/java/org/toop/framework/networking/server/{User.java => client/NettyClient.java} (69%) create mode 100644 framework/src/main/java/org/toop/framework/networking/server/connectionHandler/ClientSession.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/gateway/GatewayServer.java rename framework/src/main/java/org/toop/framework/networking/server/{MasterServer.java => gateway/NettyGatewayServer.java} (64%) create mode 100644 framework/src/main/java/org/toop/framework/networking/server/handlers/Handler.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java rename framework/src/main/java/org/toop/framework/networking/server/{ => parsing}/ParsedMessage.java (54%) create mode 100644 framework/src/main/java/org/toop/framework/networking/server/parsing/Parser.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/stores/GameStore.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/stores/NettyClientStore.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/stores/Store.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameStore.java create mode 100644 framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameTypeStore.java rename framework/src/main/java/org/toop/framework/{networking/server => utils}/SimpleTimer.java (70%) create mode 100644 framework/src/main/java/org/toop/framework/utils/Utils.java diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index cdea35a..0b10983 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -17,12 +17,10 @@ import org.toop.framework.gameFramework.model.player.Player; import org.toop.framework.networking.connection.clients.TournamentNetworkingClient; import org.toop.framework.networking.connection.events.NetworkEvents; import org.toop.framework.networking.connection.types.NetworkingConnector; -import org.toop.framework.game.games.reversi.BitboardReversi; -import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; import org.toop.framework.game.players.ArtificialPlayer; import org.toop.framework.game.players.OnlinePlayer; import org.toop.framework.game.players.RandomAI; -import org.toop.framework.networking.server.MasterServer; +import org.toop.framework.networking.server.gateway.NettyGatewayServer; import org.toop.local.AppContext; import java.util.List; @@ -33,7 +31,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public final class Server { - private MasterServer masterServer; + private NettyGatewayServer nettyGatewayServer; private String user = ""; private long clientId = -1; @@ -68,7 +66,7 @@ public final class Server { // Server has to deal with ALL network related listen events. This "server" can then interact with the manager to make stuff happen. // This prevents data races where events get sent to the game manager but the manager isn't ready yet. - public Server(String ip, String port, String user, MasterServer masterServer) { + public Server(String ip, String port, String user, NettyGatewayServer nettyGatewayServer) { if (ip.split("\\.").length < 4) { new ErrorPopup("\"" + ip + "\" " + AppContext.getString("is-not-a-valid-ip-address")); return; @@ -88,7 +86,7 @@ public final class Server { return; } - this.masterServer = masterServer; + this.nettyGatewayServer = nettyGatewayServer; final int reconnectAttempts = 10; @@ -288,8 +286,8 @@ public final class Server { stopScheduler(); connectFlow.unsubscribeAll(); - if (masterServer != null) { - masterServer.stop(); + if (nettyGatewayServer != null) { + nettyGatewayServer.stop(); } WidgetContainer.getCurrentView().transitionPrevious(); diff --git a/app/src/main/java/org/toop/app/widget/view/OnlineView.java b/app/src/main/java/org/toop/app/widget/view/OnlineView.java index 95d7603..4ae8692 100644 --- a/app/src/main/java/org/toop/app/widget/view/OnlineView.java +++ b/app/src/main/java/org/toop/app/widget/view/OnlineView.java @@ -9,7 +9,8 @@ import javafx.geometry.Pos; import org.toop.framework.game.games.reversi.BitboardReversi; import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import org.toop.framework.networking.server.MasterServer; +import org.toop.framework.networking.server.gateway.NettyGatewayServer; +import org.toop.framework.networking.server.stores.TurnBasedGameTypeStore; import java.time.Duration; import java.util.concurrent.ConcurrentHashMap; @@ -31,11 +32,12 @@ public class OnlineView extends ViewWidget { }); var localHostButton = Primitive.button("host!", () -> { - var games = new ConcurrentHashMap>(); - games.put("tic-tac-toe", BitboardTicTacToe.class); - games.put("reversi", BitboardReversi.class); - var a = new MasterServer(6666, games, Duration.ofSeconds(10)); + var tps = new TurnBasedGameTypeStore(); + tps.register("tic-tac-toe", BitboardTicTacToe::new); + tps.register("reversi", BitboardReversi::new); + + var a = new NettyGatewayServer(6666, tps, Duration.ofSeconds(10)); new Thread(() -> { try { diff --git a/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java b/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java index 519f857..f4a7ed0 100644 --- a/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java +++ b/framework/src/main/java/org/toop/framework/game/players/ServerPlayer.java @@ -3,18 +3,18 @@ package org.toop.framework.game.players; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.AbstractPlayer; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.framework.networking.server.User; +import org.toop.framework.networking.server.client.NettyClient; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class ServerPlayer extends AbstractPlayer { - private User user; + private NettyClient client; private CompletableFuture lastMove; - public ServerPlayer(User user) { - super(user.name()); - this.user = user; + public ServerPlayer(NettyClient client) { + super(client.name()); + this.client = client; } public void setMove(long move) { @@ -30,7 +30,7 @@ public class ServerPlayer extends AbstractPlayer { public long getMove(TurnBasedGame game) { lastMove = new CompletableFuture<>(); System.out.println("Sending yourturn"); - user.sendMessage("SVR GAME YOURTURN {TURNMESSAGE: \"\"}\n"); + client.send("SVR GAME YOURTURN {TURNMESSAGE: \"\"}\n"); try { return lastMove.get(); } catch (InterruptedException | ExecutionException e) { diff --git a/framework/src/main/java/org/toop/framework/networking/server/ConnectionHandler.java b/framework/src/main/java/org/toop/framework/networking/server/ConnectionHandler.java deleted file mode 100644 index 8c20f32..0000000 --- a/framework/src/main/java/org/toop/framework/networking/server/ConnectionHandler.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.toop.framework.networking.server; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import org.apache.maven.surefire.shared.utils.StringUtils; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -public class ConnectionHandler extends SimpleChannelInboundHandler { - - private final User user; - private final Server server; - - public ConnectionHandler(User user, Server server) { - this.user = user; - this.server = server; - } - - private String returnQuotedString(Iterator strings) { // TODO more places this could be useful - return "\"" + StringUtils.join(strings, "\",\"") + "\""; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush("WELCOME " + user.id() + "\n"); - - user.setCtx(ctx); - server.addUser(user); // TODO set correct name on login - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, String msg) { - - IO.println(msg); - - ParsedMessage p = parse(msg); - if (p == null) return; - - IO.println(p.command() + " " + Arrays.toString(p.args())); - - switch (p.command()) { - case "ping" -> ctx.writeAndFlush("PONG\n"); - case "login" -> handleLogin(p); - case "get" -> handleGet(p); - case "subscribe" -> handleSubscribe(p); - case "move" -> handleMove(p); - case "challenge" -> handleChallenge(p); - case "message" -> handleMessage(p); - case "help" -> handleHelp(p); - default -> ctx.writeAndFlush("ERROR Unknown command\n"); - } - } - - // DO NOT INVERT - private boolean hasArgs(String... args) { - return (args.length >= 1); - } - - private void handleLogin(ParsedMessage p) { - if (!hasArgs(p.args())) return; - - user.setName(p.args()[0]); - } - - private void handleGet(ParsedMessage p) { - if (!hasArgs(p.args())) return; - - switch (p.args()[0]) { - case "playerlist" -> { - var names = server.onlineUsers().stream().map(ServerUser::name).iterator(); - user.ctx().writeAndFlush("SVR PLAYERLIST " + returnQuotedString(names) + "\n"); - } - case "gamelist" -> { - var names = server.gameTypes().stream().iterator(); - user.ctx().writeAndFlush("SVR GAMELIST " + returnQuotedString(names) + "\n"); - } - } - } - - private void handleSubscribe(ParsedMessage p) { - // TODO - } - - private void handleHelp(ParsedMessage p) { - // TODO - } - - private void handleMessage(ParsedMessage p) { - // TODO - } - - private void handleChallenge(ParsedMessage p) { - if (!hasArgs(p.args())) return; - if (p.args().length < 2) return; - - if (p.args()[0].equalsIgnoreCase("accept")) { - try { - long id = Long.parseLong(p.args()[1]); - - if (id <= 0) { - user.sendMessage("ERR id must be a positive number \n"); - return; - } - - server.acceptChallenge(id); - - } catch (NumberFormatException e) { - user.sendMessage("ERR id is not a valid number or too big \n"); - return; - } - return; - } - - server.challengeUser(user.name(), p.args()[0], p.args()[1]); - } - - private void handleMove(ParsedMessage p) { - if(!hasArgs(p.args())) return; - - // TODO check if not number - user.serverPlayer().setMove(1L << Integer.parseInt(p.args()[0])); - } - - private ParsedMessage parse(String msg) { - // TODO, what if empty string. - - if (msg.isEmpty()) return null; - - msg = msg.trim().toLowerCase(); - - List parts = new LinkedList<>(List.of(msg.split(" "))); - - if (parts.size() > 1) { - String command = parts.removeFirst(); - return new ParsedMessage(command, parts.toArray(String[]::new)); - } - else { - return new ParsedMessage(msg); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - server.removeUser(user); - } -} diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/Game.java index 6903120..3ac78b8 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Game.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Game.java @@ -3,39 +3,40 @@ package org.toop.framework.networking.server; import org.toop.framework.game.gameThreads.ServerThreadBehaviour; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.networking.server.client.NettyClient; public class Game implements OnlineGame { private long id; - private User[] users; + private NettyClient[] clients; private TurnBasedGame game; private ServerThreadBehaviour gameThread; - public Game(TurnBasedGame game, User... users) { + public Game(TurnBasedGame game, NettyClient... clients) { this.game = game; this.gameThread = new ServerThreadBehaviour( game, (pair) -> notifyMoveMade(pair.getLeft(), pair.getRight()), (pair) -> notifyGameEnd(pair.getLeft(), pair.getRight()) ); - this.users = users; + this.clients = clients; } private void notifyMoveMade(String speler, int move){ - for (User user : users) { - user.sendMessage(String.format("SVR GAME MOVE {PLAYER: \"%s\", MOVE: \"%s\", DETAILS: \"\"}\n", speler, move)); + for (NettyClient client : clients) { + client.send(String.format("SVR GAME MOVE {PLAYER: \"%s\", MOVE: \"%s\", DETAILS: \"\"}\n", speler, move)); } } private void notifyGameEnd(GameState state, int winner){ if (state == GameState.DRAW){ - for (User user : users) { - user.sendMessage(String.format("SVR GAME DRAW {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"Client disconnected\"}\n")); + for (NettyClient client : clients) { + client.send(String.format("SVR GAME DRAW {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"NettyClient disconnected\"}\n")); } } else{ - users[winner].sendMessage(String.format("SVR GAME WIN {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"Client disconnected\"}\n")); - users[(winner + 1)%2].sendMessage(String.format("SVR GAME LOSS {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"Client disconnected\"}\n")); + clients[winner].send(String.format("SVR GAME WIN {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"NettyClient disconnected\"}\n")); + clients[(winner + 1)%2].send(String.format("SVR GAME LOSS {PLAYERONESCORE: \"\", PLAYERTWOSCORE: \"\", COMMENT: \"NettyClient disconnected\"}\n")); } } @@ -51,8 +52,8 @@ public class Game implements OnlineGame { } @Override - public User[] users() { - return users; + public NettyClient[] users() { + return clients; } @Override diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java b/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java deleted file mode 100644 index bd8252d..0000000 --- a/framework/src/main/java/org/toop/framework/networking/server/GameDefinition.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.toop.framework.networking.server; - -import org.toop.framework.gameFramework.model.game.TurnBasedGame; - -import java.lang.reflect.InvocationTargetException; - -public class GameDefinition { - private final String name; - private final Class game; - - public GameDefinition(String name, Class game) { - this.name = name; - this.game = game; - } - - public String name() { - return name; - } - - public T create(String... users) { - try { - return game.getDeclaredConstructor().newInstance(users); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameServer.java b/framework/src/main/java/org/toop/framework/networking/server/GameServer.java index 39a03f7..00993a0 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/GameServer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/GameServer.java @@ -3,6 +3,6 @@ package org.toop.framework.networking.server; public interface GameServer { // List gameTypes(); // List ongoingGames(); -// void startGame(String gameType, User... users); +// void startGame(String gameType, NettyClient... users); // String[] onlineUsers(); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/MessageStore.java b/framework/src/main/java/org/toop/framework/networking/server/MessageStore.java deleted file mode 100644 index 6fbba3b..0000000 --- a/framework/src/main/java/org/toop/framework/networking/server/MessageStore.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.toop.framework.networking.server; - -public interface MessageStore { - void add(String message); - String get(); - void reset(); -} diff --git a/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java index b82b4b1..39739e6 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java +++ b/framework/src/main/java/org/toop/framework/networking/server/OnlineGame.java @@ -1,8 +1,10 @@ package org.toop.framework.networking.server; +import org.toop.framework.networking.server.client.NettyClient; + public interface OnlineGame { long id(); T game(); - User[] users(); + NettyClient[] users(); void start(); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/Parser.java b/framework/src/main/java/org/toop/framework/networking/server/Parser.java deleted file mode 100644 index 016f797..0000000 --- a/framework/src/main/java/org/toop/framework/networking/server/Parser.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.toop.framework.networking.server; - -public class Parser { -} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServableGame.java b/framework/src/main/java/org/toop/framework/networking/server/ServableGame.java deleted file mode 100644 index 17dd3a9..0000000 --- a/framework/src/main/java/org/toop/framework/networking/server/ServableGame.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.toop.framework.networking.server; - -public interface ServableGame { -} diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index 4da5c4c..cb4f543 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -2,27 +2,41 @@ package org.toop.framework.networking.server; import org.toop.framework.game.players.ServerPlayer; import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.networking.server.challenges.gamechallenge.GameChallenge; +import org.toop.framework.networking.server.challenges.gamechallenge.GameChallengeTimer; +import org.toop.framework.networking.server.client.NettyClient; +import org.toop.framework.networking.server.stores.ClientStore; +import org.toop.framework.networking.server.stores.TurnBasedGameStore; +import org.toop.framework.networking.server.stores.TurnBasedGameTypeStore; import org.toop.framework.utils.ImmutablePair; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.concurrent.*; import java.time.Duration; public class Server implements GameServer { - final private Map> gameTypes; - final private Map users = new ConcurrentHashMap<>(); + final private TurnBasedGameTypeStore gameTypesStore; + final private ClientStore clientStore; final private List gameChallenges = new CopyOnWriteArrayList<>(); - final private List> games = new CopyOnWriteArrayList<>(); + final private TurnBasedGameStore gameStore; final private Duration challengeDuration; final private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); - public Server(Map> gameTypes, Duration challengeDuration) { - this.gameTypes = gameTypes; + public Server( + Duration challengeDuration, + TurnBasedGameTypeStore turnBasedGameTypeStore, + ClientStore clientStore, + TurnBasedGameStore gameStore + + ) { + this.gameTypesStore = turnBasedGameTypeStore; this.challengeDuration = challengeDuration; + this.clientStore = clientStore; + this.gameStore = gameStore; scheduler.schedule(this::serverTask, 0, TimeUnit.MILLISECONDS); } @@ -32,51 +46,51 @@ public class Server implements GameServer { scheduler.schedule(this::serverTask, 500, TimeUnit.MILLISECONDS); } - public void addUser(User user) { - users.putIfAbsent(user.id(), user); + public void addUser(NettyClient client) { + clientStore.add(client); } - public void removeUser(User user) { - users.remove(user.id()); + public void removeUser(NettyClient client) { + clientStore.remove(client.id()); } public List gameTypes() { - return gameTypes.keySet().stream().toList(); + return new ArrayList<>(gameTypesStore.all().keySet()); } public List> ongoingGames() { - return games; + return gameStore.all().stream().toList(); } - public User getUser(String username) { - return users.values().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().orElse(null); + public NettyClient getUser(String username) { + return clientStore.all().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().orElse(null); } - public User getUser(long id) { - return users.get(id); + public NettyClient getUser(long id) { + return clientStore.get(id); } public void challengeUser(String fromUser, String toUser, String gameType) { - User from = getUser(fromUser); + NettyClient from = getUser(fromUser); if (from == null) { return; } - if (!gameTypes.containsKey(gameType)) { - from.sendMessage("ERR gametype not found \n"); + if (!gameTypesStore.all().containsKey(gameType)) { + from.send("ERR gametype not found \n"); return; } - User to = getUser(toUser); + NettyClient to = getUser(toUser); if (to == null) { - from.sendMessage("ERR user not found \n"); + from.send("ERR user not found \n"); return; } var ch = new GameChallenge(from, to, gameType, new GameChallengeTimer(challengeDuration)); - to.sendMessage( + to.send( "SVR GAME CHALLENGE {CHALLENGER: \"%s\", CHALLENGENUMBER: \"%s\", GAMETYPE: \"%s\"} \n" .formatted(from.name(), ch.id(), gameType) ); @@ -90,13 +104,13 @@ public class Server implements GameServer { gameChallenges.addLast(ch); } - private void warnUserExpiredChallenge(User user, long challengeId) { - user.sendMessage("SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: \"" + challengeId + "\"}" + "\n"); + private void warnUserExpiredChallenge(NettyClient client, long challengeId) { + client.send("SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: \"" + challengeId + "\"}" + "\n"); } private boolean isValidChallenge(GameChallenge gameChallenge) { for (var user : gameChallenge.getUsers()) { - if (users.get(user.id()) == null) { + if (clientStore.get(user.id()) == null) { return false; } @@ -140,43 +154,40 @@ public class Server implements GameServer { return gameChallenges; } - public void startGame(String gameType, User... users) { - if (!gameTypes.containsKey(gameType)) return; + public void startGame(String gameType, NettyClient... clients) { + if (!gameTypesStore.all().containsKey(gameType)) return; try { - ServerPlayer[] players = new ServerPlayer[users.length]; - var game = new Game(gameTypes.get(gameType).getDeclaredConstructor().newInstance(), users); + ServerPlayer[] players = new ServerPlayer[clients.length]; + var game = new Game(gameTypesStore.create(gameType), clients); - for (int i = 0; i < users.length; i++) { - players[i] = new ServerPlayer(users[i]); - users[i].addGame(new ImmutablePair<>(game, players[i])); + for (int i = 0; i < clients.length; i++) { + players[i] = new ServerPlayer(clients[i]); + clients[i].addGame(new ImmutablePair<>(game, players[i])); } System.out.println("Starting Game"); game.game().init(players); - games.addLast(game); + gameStore.add(game); - users[0].sendMessage(String.format("SVR GAME MATCH {PLAYERTOMOVE: \"%s\", GAMETYPE: \"%s\", OPPONENT: \"%s\"}\n", - users[0].name(), + clients[0].send(String.format("SVR GAME MATCH {PLAYERTOMOVE: \"%s\", GAMETYPE: \"%s\", OPPONENT: \"%s\"}\n", + clients[0].name(), gameType, - users[1].name())); - users[1].sendMessage(String.format("SVR GAME MATCH {PLAYERTOMOVE: \"%s\", GAMETYPE: \"%s\", OPPONENT: \"%s\"}\n", - users[0].name(), + clients[1].name())); + clients[1].send(String.format("SVR GAME MATCH {PLAYERTOMOVE: \"%s\", GAMETYPE: \"%s\", OPPONENT: \"%s\"}\n", + clients[0].name(), gameType, - users[0].name())); + clients[0].name())); game.start(); } catch (Exception ignored) {} } - public List onlineUsers() { - return users.values().stream().toList(); + public List onlineUsers() { + return clientStore.all().stream().toList(); } public void closeServer() { scheduler.shutdown(); gameChallenges.clear(); - games.clear(); - users.clear(); - gameTypes.clear(); } } diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java b/framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java deleted file mode 100644 index 22af1d7..0000000 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerMessageStore.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.toop.framework.networking.server; - -import java.util.Queue; - -public class ServerMessageStore implements MessageStore { - - Queue messageQueue; - - public ServerMessageStore(Queue messageQueue) { - this.messageQueue = messageQueue; - } - - @Override - public void add(String message) { - messageQueue.offer(message); - } - - @Override - public String get() { - return messageQueue.poll(); - } - - @Override - public void reset() { - messageQueue.clear(); - } -} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java b/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java deleted file mode 100644 index e06098c..0000000 --- a/framework/src/main/java/org/toop/framework/networking/server/ServerUser.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.toop.framework.networking.server; - -import org.toop.framework.game.players.ServerPlayer; -import org.toop.framework.utils.Pair; - -public interface ServerUser { - long id(); - String name(); - Game game(); - ServerPlayer serverPlayer(); - void addGame(Pair gamePair); - void removeGame(); - void setName(String name); - void sendMessage(String message); -} diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java b/framework/src/main/java/org/toop/framework/networking/server/challenges/gamechallenge/GameChallenge.java similarity index 66% rename from framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java rename to framework/src/main/java/org/toop/framework/networking/server/challenges/gamechallenge/GameChallenge.java index 50a5c2b..c9fefa7 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/GameChallenge.java +++ b/framework/src/main/java/org/toop/framework/networking/server/challenges/gamechallenge/GameChallenge.java @@ -1,18 +1,20 @@ -package org.toop.framework.networking.server; +package org.toop.framework.networking.server.challenges.gamechallenge; import org.toop.framework.SnowflakeGenerator; +import org.toop.framework.networking.server.client.NettyClient; +import org.toop.framework.utils.SimpleTimer; public class GameChallenge { private final long id = SnowflakeGenerator.nextId(); // I don't need this, but the tournament server uses it... - private final User from; - private final User to; + private final NettyClient from; + private final NettyClient to; private final String gameType; private final SimpleTimer timer; private boolean isChallengeAccepted = false; - public GameChallenge(User from, User to, String gameType, SimpleTimer timer) { + public GameChallenge(NettyClient from, NettyClient to, String gameType, SimpleTimer timer) { this.from = from; this.to = to; this.gameType = gameType; @@ -23,8 +25,8 @@ public class GameChallenge { return id; } - public User[] getUsers() { - return new User[]{from, to}; + public NettyClient[] getUsers() { + return new NettyClient[]{from, to}; } public void forceExpire() { diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameChallengeTimer.java b/framework/src/main/java/org/toop/framework/networking/server/challenges/gamechallenge/GameChallengeTimer.java similarity index 86% rename from framework/src/main/java/org/toop/framework/networking/server/GameChallengeTimer.java rename to framework/src/main/java/org/toop/framework/networking/server/challenges/gamechallenge/GameChallengeTimer.java index 1406648..bf277b7 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/GameChallengeTimer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/challenges/gamechallenge/GameChallengeTimer.java @@ -1,4 +1,6 @@ -package org.toop.framework.networking.server; +package org.toop.framework.networking.server.challenges.gamechallenge; + +import org.toop.framework.utils.SimpleTimer; import java.time.Instant; import java.time.Duration; diff --git a/framework/src/main/java/org/toop/framework/networking/server/client/Client.java b/framework/src/main/java/org/toop/framework/networking/server/client/Client.java new file mode 100644 index 0000000..96ea515 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/client/Client.java @@ -0,0 +1,19 @@ +package org.toop.framework.networking.server.client; + +import org.toop.framework.networking.server.Game; +import org.toop.framework.utils.Pair; + +public interface Client { + long id(); + + String name(); + void setName(String name); + + Game game(); + P player(); + + void addGame(Pair gamePair); + void clearGame(); + + void send(String message); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/User.java b/framework/src/main/java/org/toop/framework/networking/server/client/NettyClient.java similarity index 69% rename from framework/src/main/java/org/toop/framework/networking/server/User.java rename to framework/src/main/java/org/toop/framework/networking/server/client/NettyClient.java index 34c1211..37eeb4b 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/User.java +++ b/framework/src/main/java/org/toop/framework/networking/server/client/NettyClient.java @@ -1,16 +1,17 @@ -package org.toop.framework.networking.server; +package org.toop.framework.networking.server.client; import io.netty.channel.ChannelHandlerContext; import org.toop.framework.game.players.ServerPlayer; +import org.toop.framework.networking.server.Game; import org.toop.framework.utils.Pair; -public class User implements ServerUser { +public class NettyClient implements Client { final private long id; + private ChannelHandlerContext ctx; private String name; private Pair gamePair; - private ChannelHandlerContext connectionContext; - public User(long userId, String name) { + public NettyClient(long userId, String name) { this.id = userId; this.name = name; } @@ -33,7 +34,7 @@ public class User implements ServerUser { } @Override - public void removeGame() { + public void clearGame() { this.gamePair = null; } @@ -45,7 +46,8 @@ public class User implements ServerUser { return this.gamePair.getLeft(); } - public ServerPlayer serverPlayer() { + @Override + public ServerPlayer player() { return this.gamePair.getRight(); } @@ -55,18 +57,12 @@ public class User implements ServerUser { } @Override - public void sendMessage(String message) { + public void send(String message) { IO.println(message); - ctx().channel().writeAndFlush(message); - } - - public ChannelHandlerContext ctx() { - return connectionContext; + ctx.channel().writeAndFlush(message + "\r\n"); } public void setCtx(ChannelHandlerContext ctx) { - this.connectionContext = ctx; + this.ctx = ctx; } - - } diff --git a/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/ClientSession.java b/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/ClientSession.java new file mode 100644 index 0000000..06c9a65 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/ClientSession.java @@ -0,0 +1,7 @@ +package org.toop.framework.networking.server.connectionHandler; + +import org.toop.framework.networking.server.client.Client; + +public interface ClientSession { + Client client(); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java b/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java new file mode 100644 index 0000000..ae1f071 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java @@ -0,0 +1,64 @@ +package org.toop.framework.networking.server.connectionHandler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.toop.framework.game.players.ServerPlayer; +import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.client.NettyClient; +import org.toop.framework.networking.server.handlers.Handler; +import org.toop.framework.networking.server.parsing.ParsedMessage; +import org.toop.framework.networking.server.Server; +import org.toop.framework.networking.server.client.Client; +import org.toop.framework.networking.server.parsing.Parser; + +import java.util.Arrays; + +public class NettyClientSession extends SimpleChannelInboundHandler implements ClientSession { + + private final NettyClient client; + private final Server server; + private final Handler handler; + + public NettyClientSession(NettyClient client, Server server, Handler handler) { + this.client = client; + this.server = server; + this.handler = handler; + } + + @Override + public Client client() { + return client; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + ctx.writeAndFlush("Welcome " + client.id() + " please login" + "\n"); + + client.setCtx(ctx); + server.addUser(client); // TODO set correct name on login + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, String msg) { + + IO.println(msg); + + ParsedMessage p = Parser.parse(msg); + if (p == null) return; + + IO.println(p.command() + " " + Arrays.toString(p.args())); + + handler.handle(p); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + server.removeUser(client); + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/gateway/GatewayServer.java b/framework/src/main/java/org/toop/framework/networking/server/gateway/GatewayServer.java new file mode 100644 index 0000000..9aef1d6 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/gateway/GatewayServer.java @@ -0,0 +1,7 @@ +package org.toop.framework.networking.server.gateway; + +public interface GatewayServer { + void start() throws Exception; + void stop(); + int port(); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java b/framework/src/main/java/org/toop/framework/networking/server/gateway/NettyGatewayServer.java similarity index 64% rename from framework/src/main/java/org/toop/framework/networking/server/MasterServer.java rename to framework/src/main/java/org/toop/framework/networking/server/gateway/NettyGatewayServer.java index b13dc58..9a4a73f 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/MasterServer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/gateway/NettyGatewayServer.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.server; +package org.toop.framework.networking.server.gateway; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; @@ -13,11 +13,20 @@ import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import org.toop.framework.SnowflakeGenerator; import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.networking.server.client.NettyClient; +import org.toop.framework.networking.server.connectionHandler.NettyClientSession; +import org.toop.framework.networking.server.Server; +import org.toop.framework.networking.server.handlers.MessageHandler; +import org.toop.framework.networking.server.stores.NettyClientStore; +import org.toop.framework.networking.server.stores.TurnBasedGameStore; +import org.toop.framework.networking.server.stores.TurnBasedGameTypeStore; import java.time.Duration; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; -public class MasterServer { +public class NettyGatewayServer implements GatewayServer { private final int port; private final Server gs; @@ -25,11 +34,21 @@ public class MasterServer { EventLoopGroup bossGroup; EventLoopGroup workerGroup; - public MasterServer(int port, Map> gameTypes, Duration challengeDuration) { + public NettyGatewayServer( + int port, + TurnBasedGameTypeStore turnBasedGameTypeStore, + Duration challengeDuration + ) { this.port = port; - this.gs = new Server(gameTypes, challengeDuration); + this.gs = new Server( + challengeDuration, + turnBasedGameTypeStore, + new NettyClientStore(new ConcurrentHashMap<>()), + new TurnBasedGameStore(new CopyOnWriteArrayList<>()) + ); } + @Override public void start() throws InterruptedException { bossGroup = new NioEventLoopGroup(1); @@ -54,8 +73,8 @@ public class MasterServer { pipeline.addLast(new StringEncoder()); long userid = SnowflakeGenerator.nextId(); - User user = new User(userid, ""+userid); - pipeline.addLast(new ConnectionHandler(user, gs)); + NettyClient client = new NettyClient(userid, ""+userid); + pipeline.addLast(new NettyClientSession(client, gs, new MessageHandler(gs, client))); } } ); @@ -69,6 +88,7 @@ public class MasterServer { } } + @Override public void stop() { if (future == null) { return; @@ -82,4 +102,9 @@ public class MasterServer { bossGroup = null; workerGroup = null; } + + @Override + public int port() { + return port; + } } diff --git a/framework/src/main/java/org/toop/framework/networking/server/handlers/Handler.java b/framework/src/main/java/org/toop/framework/networking/server/handlers/Handler.java new file mode 100644 index 0000000..70b4ed6 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/handlers/Handler.java @@ -0,0 +1,5 @@ +package org.toop.framework.networking.server.handlers; + +public interface Handler { + void handle(T message); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java b/framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java new file mode 100644 index 0000000..28bc24a --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java @@ -0,0 +1,104 @@ +package org.toop.framework.networking.server.handlers; + +import org.toop.framework.game.players.ServerPlayer; +import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.Server; +import org.toop.framework.networking.server.client.Client; +import org.toop.framework.networking.server.parsing.ParsedMessage; +import org.toop.framework.utils.Utils; + +public class MessageHandler implements Handler { + + private final Server server; + private final Client client; + + public MessageHandler(Server server, Client client) { + this.server = server; + this.client = client; + } + + @Override + public void handle(ParsedMessage message) { + switch (message.command()) { + case "ping" -> client.send("PONG"); + case "login" -> handleLogin(message, client); + case "get" -> handleGet(message, client); + case "subscribe" -> handleSubscribe(message, client); + case "move" -> handleMove(message, client); + case "challenge" -> handleChallenge(message, client); + case "message" -> handleMessage(message, client); + case "help" -> handleHelp(message, client); + default -> client.send("ERROR Unknown command"); + } + } + + // DO NOT INVERT + private boolean hasArgs(String... args) { + return (args.length >= 1); + } + + private void handleLogin(ParsedMessage p, Client client) { + if (!hasArgs(p.args())) return; + + client.setName(p.args()[0]); + } + + private void handleSubscribe(ParsedMessage p, Client client) { + // TODO + } + + private void handleHelp(ParsedMessage p, Client client) { + // TODO + } + + private void handleMessage(ParsedMessage p, Client client) { + // TODO + } + + private void handleGet(ParsedMessage p, Client client) { + if (!hasArgs(p.args())) return; + + switch (p.args()[0]) { + case "playerlist" -> { + var names = server.onlineUsers().stream().map(Client::name).iterator(); + client.send("SVR PLAYERLIST " + Utils.returnQuotedString(names)); + } + case "gamelist" -> { + var names = server.gameTypes().stream().iterator(); + client.send("SVR GAMELIST " + Utils.returnQuotedString(names)); + } + } + } + + private void handleChallenge(ParsedMessage p, Client client) { + if (!hasArgs(p.args())) return; + if (p.args().length < 2) return; + + if (p.args()[0].equalsIgnoreCase("accept")) { + try { + long id = Long.parseLong(p.args()[1]); + + if (id <= 0) { + client.send("ERR id must be a positive number"); + return; + } + + server.acceptChallenge(id); + + } catch (NumberFormatException e) { + client.send("ERR id is not a valid number or too big"); + return; + } + return; + } + + server.challengeUser(client.name(), p.args()[0], p.args()[1]); + } + + private void handleMove(ParsedMessage p, Client client) { + if(!hasArgs(p.args())) return; + + // TODO check if not number + client.player().setMove(1L << Integer.parseInt(p.args()[0])); + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/ParsedMessage.java b/framework/src/main/java/org/toop/framework/networking/server/parsing/ParsedMessage.java similarity index 54% rename from framework/src/main/java/org/toop/framework/networking/server/ParsedMessage.java rename to framework/src/main/java/org/toop/framework/networking/server/parsing/ParsedMessage.java index 5f8e550..e788ae3 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/ParsedMessage.java +++ b/framework/src/main/java/org/toop/framework/networking/server/parsing/ParsedMessage.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.server; +package org.toop.framework.networking.server.parsing; public record ParsedMessage(String command, String... args) {} diff --git a/framework/src/main/java/org/toop/framework/networking/server/parsing/Parser.java b/framework/src/main/java/org/toop/framework/networking/server/parsing/Parser.java new file mode 100644 index 0000000..cb5cb97 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/parsing/Parser.java @@ -0,0 +1,24 @@ +package org.toop.framework.networking.server.parsing; + +import java.util.LinkedList; +import java.util.List; + +public class Parser { + public static ParsedMessage parse(String msg) { + // TODO, what if empty string. + + if (msg.isEmpty()) return null; + + msg = msg.trim().toLowerCase(); + + List parts = new LinkedList<>(List.of(msg.split(" "))); + + if (parts.size() > 1) { + String command = parts.removeFirst(); + return new ParsedMessage(command, parts.toArray(String[]::new)); + } + else { + return new ParsedMessage(msg); + } + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java b/framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java new file mode 100644 index 0000000..9d13127 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java @@ -0,0 +1,7 @@ +package org.toop.framework.networking.server.stores; + +import org.toop.framework.game.players.ServerPlayer; +import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.client.Client; + +public interface ClientStore> extends Store {} diff --git a/framework/src/main/java/org/toop/framework/networking/server/stores/GameStore.java b/framework/src/main/java/org/toop/framework/networking/server/stores/GameStore.java new file mode 100644 index 0000000..656df1d --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/stores/GameStore.java @@ -0,0 +1,3 @@ +package org.toop.framework.networking.server.stores; + +public interface GameStore extends Store {} diff --git a/framework/src/main/java/org/toop/framework/networking/server/stores/NettyClientStore.java b/framework/src/main/java/org/toop/framework/networking/server/stores/NettyClientStore.java new file mode 100644 index 0000000..f2b42cb --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/stores/NettyClientStore.java @@ -0,0 +1,34 @@ +package org.toop.framework.networking.server.stores; + +import org.toop.framework.networking.server.client.NettyClient; + +import java.util.Collection; +import java.util.Map; + +public class NettyClientStore implements ClientStore { + final private Map users; + + public NettyClientStore(Map usersMap) { + this.users = usersMap; + } + + @Override + public void add(NettyClient adding) { + users.putIfAbsent(adding.id(), adding); + } + + @Override + public void remove(Long remover) { + users.remove(remover); + } + + @Override + public NettyClient get(Long getter) { + return users.get(getter); + } + + @Override + public Collection all() { + return users.values(); + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/stores/Store.java b/framework/src/main/java/org/toop/framework/networking/server/stores/Store.java new file mode 100644 index 0000000..cec7311 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/stores/Store.java @@ -0,0 +1,10 @@ +package org.toop.framework.networking.server.stores; + +import java.util.Collection; + +public interface Store { + void add(STORED adding); + void remove(IDENTIFIER remover); + STORED get(IDENTIFIER getter); + Collection all(); +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameStore.java b/framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameStore.java new file mode 100644 index 0000000..d8c47f2 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameStore.java @@ -0,0 +1,36 @@ +package org.toop.framework.networking.server.stores; + +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.networking.server.OnlineGame; + +import java.util.Collection; +import java.util.List; + +public class TurnBasedGameStore implements GameStore, OnlineGame> { + + private List> gameList; + + public TurnBasedGameStore(List> initGameList) { + this.gameList = initGameList; + } + + @Override + public void add(OnlineGame adding) { + gameList.addLast(adding); + } + + @Override + public void remove(OnlineGame remover) { + gameList.remove(remover); + } + + @Override + public OnlineGame get(OnlineGame getter) { + return gameList.stream().filter(game->game.equals(getter)).findFirst().orElse(null); + } + + @Override + public Collection> all() { + return gameList; + } +} diff --git a/framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameTypeStore.java b/framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameTypeStore.java new file mode 100644 index 0000000..0a564b9 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/server/stores/TurnBasedGameTypeStore.java @@ -0,0 +1,32 @@ +package org.toop.framework.networking.server.stores; + +import org.toop.framework.gameFramework.model.game.TurnBasedGame; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class TurnBasedGameTypeStore { + + private final Map> gameFactories = new ConcurrentHashMap<>(); + + public TurnBasedGameTypeStore() {} + + public void register(String key, Supplier factory) { + gameFactories.put(key, factory); + } + + public void unregister(String key) { + gameFactories.remove(key); + } + + public TurnBasedGame create(String key) { + Supplier factory = gameFactories.get(key); + if (factory == null) throw new IllegalArgumentException("Unknown game type: " + key); + return factory.get(); + } + + public Map> all() { + return Map.copyOf(gameFactories); + } +} \ No newline at end of file diff --git a/framework/src/main/java/org/toop/framework/networking/server/SimpleTimer.java b/framework/src/main/java/org/toop/framework/utils/SimpleTimer.java similarity index 70% rename from framework/src/main/java/org/toop/framework/networking/server/SimpleTimer.java rename to framework/src/main/java/org/toop/framework/utils/SimpleTimer.java index c9c25aa..bf70f5c 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/SimpleTimer.java +++ b/framework/src/main/java/org/toop/framework/utils/SimpleTimer.java @@ -1,4 +1,4 @@ -package org.toop.framework.networking.server; +package org.toop.framework.utils; public interface SimpleTimer { void forceExpire(); diff --git a/framework/src/main/java/org/toop/framework/utils/Utils.java b/framework/src/main/java/org/toop/framework/utils/Utils.java new file mode 100644 index 0000000..0bc4856 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/utils/Utils.java @@ -0,0 +1,11 @@ +package org.toop.framework.utils; + +import org.apache.maven.surefire.shared.utils.StringUtils; + +import java.util.Iterator; + +public class Utils { + public static String returnQuotedString(Iterator strings) { // TODO more places this could be useful + return "\"" + StringUtils.join(strings, "\",\"") + "\""; + } +} diff --git a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java index 789d732..6529fd3 100644 --- a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java +++ b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java @@ -65,7 +65,7 @@ // // } // -// static class TestUser implements ServerUser { +// static class TestUser implements Client { // // final private long id; // @@ -157,9 +157,9 @@ // // @Test // void testStartGame() { -// server.startGame("tictactoe", new User(0, "A"), new User(1, "B")); +// server.startGame("tictactoe", new NettyClient(0, "A"), new NettyClient(1, "B")); // Assertions.assertEquals(1, server.ongoingGames().size()); -// server.startGame("reversi", new User(0, "A"), new User(1, "B")); +// server.startGame("reversi", new NettyClient(0, "A"), new NettyClient(1, "B")); // Assertions.assertEquals(2, server.ongoingGames().size()); // } // From c2f1df7143def5232ef0990147ca9b60fe47337b Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:44:13 +0100 Subject: [PATCH 22/35] Refactor done, added ability to subscribe --- .../GenericGameController.java | 2 +- .../framework/game/players/MiniMaxAI.java | 2 +- .../framework/gameFramework/GameState.java | 6 +- .../networking/server/GameServer.java | 24 ++- .../{Game.java => OnlineTurnBasedGame.java} | 6 +- .../framework/networking/server/Server.java | 176 +++++++++++------- .../networking/server/client/Client.java | 4 +- .../networking/server/client/NettyClient.java | 10 +- .../connectionHandler/NettyClientSession.java | 10 +- .../server/handlers/MessageHandler.java | 30 +-- .../networking/server/stores/ClientStore.java | 4 +- .../networking/server/ServerTest.java | 16 +- 12 files changed, 178 insertions(+), 112 deletions(-) rename framework/src/main/java/org/toop/framework/networking/server/{Game.java => OnlineTurnBasedGame.java} (89%) diff --git a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java index 37429a0..1b20204 100644 --- a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java +++ b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java @@ -93,7 +93,7 @@ public class GenericGameController implements GameController { } private void onGameFinish(GUIEvents.GameEnded event){ - logger.info("Game Finished"); + logger.info("OnlineTurnBasedGame Finished"); String name = event.winner() == -1 ? null : getPlayer(event.winner()).getName(); gameView.gameOver(event.winOrTie(), name); stop(); diff --git a/framework/src/main/java/org/toop/framework/game/players/MiniMaxAI.java b/framework/src/main/java/org/toop/framework/game/players/MiniMaxAI.java index 972ddea..666d432 100644 --- a/framework/src/main/java/org/toop/framework/game/players/MiniMaxAI.java +++ b/framework/src/main/java/org/toop/framework/game/players/MiniMaxAI.java @@ -126,7 +126,7 @@ public class MiniMaxAI extends AbstractAI { * Simple heuristic evaluation for Reversi-like games. * Positive = good for AI, Negative = good for opponent. * - * @param game Game state + * @param game OnlineTurnBasedGame state * @param aiPlayer AI's player index * @return heuristic score */ diff --git a/framework/src/main/java/org/toop/framework/gameFramework/GameState.java b/framework/src/main/java/org/toop/framework/gameFramework/GameState.java index bc5a2bf..df802fe 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/GameState.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/GameState.java @@ -4,13 +4,13 @@ package org.toop.framework.gameFramework; * Represents the current state of a turn-based game. */ public enum GameState { - /** Game is ongoing and no special condition applies. */ + /** OnlineTurnBasedGame is ongoing and no special condition applies. */ NORMAL, - /** Game ended in a draw. */ + /** OnlineTurnBasedGame ended in a draw. */ DRAW, - /** Game ended with a win for a player. */ + /** OnlineTurnBasedGame ended with a win for a player. */ WIN, /** Next player's turn was skipped. */ diff --git a/framework/src/main/java/org/toop/framework/networking/server/GameServer.java b/framework/src/main/java/org/toop/framework/networking/server/GameServer.java index 00993a0..37a4905 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/GameServer.java +++ b/framework/src/main/java/org/toop/framework/networking/server/GameServer.java @@ -1,8 +1,22 @@ package org.toop.framework.networking.server; -public interface GameServer { -// List gameTypes(); -// List ongoingGames(); -// void startGame(String gameType, NettyClient... users); -// String[] onlineUsers(); +import java.util.List; + +public interface GameServer { + void startGame(String gameType, CLIENT... clients); + + void addClient(CLIENT client); + void removeClient(CLIENT client); + + void challengeClient(String fromClientName, String toClientName, String gameTypeKey); + void acceptChallenge(CHALLENGEIDTYPE challengeId); + + void subscribeClient(String clientName, String gameTypeKey); + void unsubscribeClient(String clientName); + + List gameTypes(); + List> ongoingGames(); + + List onlineUsers(); + void shutdown(); } diff --git a/framework/src/main/java/org/toop/framework/networking/server/Game.java b/framework/src/main/java/org/toop/framework/networking/server/OnlineTurnBasedGame.java similarity index 89% rename from framework/src/main/java/org/toop/framework/networking/server/Game.java rename to framework/src/main/java/org/toop/framework/networking/server/OnlineTurnBasedGame.java index 3ac78b8..3248922 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Game.java +++ b/framework/src/main/java/org/toop/framework/networking/server/OnlineTurnBasedGame.java @@ -5,14 +5,14 @@ import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.networking.server.client.NettyClient; -public class Game implements OnlineGame { +public class OnlineTurnBasedGame implements OnlineGame { private long id; private NettyClient[] clients; private TurnBasedGame game; private ServerThreadBehaviour gameThread; - public Game(TurnBasedGame game, NettyClient... clients) { + public OnlineTurnBasedGame(TurnBasedGame game, NettyClient... clients) { this.game = game; this.gameThread = new ServerThreadBehaviour( game, @@ -47,7 +47,7 @@ public class Game implements OnlineGame { } @Override - public TurnBasedGame game() { + public org.toop.framework.gameFramework.model.game.TurnBasedGame game() { return game; } diff --git a/framework/src/main/java/org/toop/framework/networking/server/Server.java b/framework/src/main/java/org/toop/framework/networking/server/Server.java index cb4f543..6b82050 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/Server.java +++ b/framework/src/main/java/org/toop/framework/networking/server/Server.java @@ -10,19 +10,19 @@ import org.toop.framework.networking.server.stores.TurnBasedGameStore; import org.toop.framework.networking.server.stores.TurnBasedGameTypeStore; import org.toop.framework.utils.ImmutablePair; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; import java.util.concurrent.*; import java.time.Duration; -public class Server implements GameServer { +public class Server implements GameServer { final private TurnBasedGameTypeStore gameTypesStore; final private ClientStore clientStore; final private List gameChallenges = new CopyOnWriteArrayList<>(); final private TurnBasedGameStore gameStore; + final private ConcurrentHashMap> subscriptions; // TODO move to own store / manager + final private Duration challengeDuration; final private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); @@ -37,40 +37,33 @@ public class Server implements GameServer { this.challengeDuration = challengeDuration; this.clientStore = clientStore; this.gameStore = gameStore; + this.subscriptions = new ConcurrentHashMap<>(); scheduler.schedule(this::serverTask, 0, TimeUnit.MILLISECONDS); } - private void serverTask() { - checkChallenges(); - scheduler.schedule(this::serverTask, 500, TimeUnit.MILLISECONDS); - } - - public void addUser(NettyClient client) { + @Override + public void addClient(NettyClient client) { clientStore.add(client); } - public void removeUser(NettyClient client) { + @Override + public void removeClient(NettyClient client) { clientStore.remove(client.id()); } + @Override public List gameTypes() { return new ArrayList<>(gameTypesStore.all().keySet()); } + @Override public List> ongoingGames() { return gameStore.all().stream().toList(); } - public NettyClient getUser(String username) { - return clientStore.all().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().orElse(null); - } - - public NettyClient getUser(long id) { - return clientStore.get(id); - } - - public void challengeUser(String fromUser, String toUser, String gameType) { + @Override + public void challengeClient(String fromUser, String toUser, String gameType) { NettyClient from = getUser(fromUser); if (from == null) { @@ -104,44 +97,8 @@ public class Server implements GameServer { gameChallenges.addLast(ch); } - private void warnUserExpiredChallenge(NettyClient client, long challengeId) { - client.send("SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: \"" + challengeId + "\"}" + "\n"); - } - - private boolean isValidChallenge(GameChallenge gameChallenge) { - for (var user : gameChallenge.getUsers()) { - if (clientStore.get(user.id()) == null) { - return false; - } - - if (user.game() != null) { - return false; - } - - if (gameChallenge.isExpired()) { - return false; - } - } - - return true; - } - - public void checkChallenges() { - for (int i = gameChallenges.size() - 1; i >= 0; i--) { - var challenge = gameChallenges.get(i); - - if (isValidChallenge(challenge)) continue; - - if (challenge.isExpired()) { - if (!challenge.isChallengeAccepted()) Arrays.stream(challenge.getUsers()) - .forEach(user -> warnUserExpiredChallenge(user, challenge.id())); - - gameChallenges.remove(i); - } - } - } - - public void acceptChallenge(long challengeId) { + @Override + public void acceptChallenge(Long challengeId) { for (var challenge : gameChallenges) { if (challenge.id() == challengeId) { startGame(challenge.acceptChallenge(), challenge.getUsers()); @@ -150,22 +107,36 @@ public class Server implements GameServer { } } - public List gameChallenges() { - return gameChallenges; + @Override + public void subscribeClient(String clientName, String gameTypeKey) { + + if (!gameTypesStore.all().containsKey(gameTypeKey)) return; + + subscriptions.forEach((_, clientNames) -> clientNames.remove(clientName)); + subscriptions.computeIfAbsent( + gameTypeKey, + _ -> new ArrayList<>()) + .add(clientName); } + @Override + public void unsubscribeClient(String clientName) { + subscriptions.forEach((_, clientNames) -> clientNames.remove(clientName)); + } + + @Override public void startGame(String gameType, NettyClient... clients) { if (!gameTypesStore.all().containsKey(gameType)) return; try { ServerPlayer[] players = new ServerPlayer[clients.length]; - var game = new Game(gameTypesStore.create(gameType), clients); + var game = new OnlineTurnBasedGame(gameTypesStore.create(gameType), clients); for (int i = 0; i < clients.length; i++) { players[i] = new ServerPlayer(clients[i]); clients[i].addGame(new ImmutablePair<>(game, players[i])); } - System.out.println("Starting Game"); + System.out.println("Starting OnlineTurnBasedGame"); game.game().init(players); gameStore.add(game); @@ -182,12 +153,91 @@ public class Server implements GameServer { } catch (Exception ignored) {} } + @Override public List onlineUsers() { return clientStore.all().stream().toList(); } - public void closeServer() { + @Override + public void shutdown() { scheduler.shutdown(); gameChallenges.clear(); } + + private void serverTask() { + checkChallenges(); + checkSubscriptions(); + scheduler.schedule(this::serverTask, 500, TimeUnit.MILLISECONDS); + } + + private void checkChallenges() { + for (int i = gameChallenges.size() - 1; i >= 0; i--) { + var challenge = gameChallenges.get(i); + + if (isValidChallenge(challenge)) continue; + + if (challenge.isExpired()) { + if (!challenge.isChallengeAccepted()) Arrays.stream(challenge.getUsers()) + .forEach(user -> warnUserExpiredChallenge(user, challenge.id())); + + gameChallenges.remove(i); + } + } + } + + private void checkSubscriptions() { + if (subscriptions.isEmpty()) return; + + List keys = subscriptions.keySet().stream().toList(); + + for (String key : keys) { + var userNames = subscriptions.get(key); + if (userNames.size() < 2) continue; + + Random ran = new Random(); + + while (userNames.size() > 1) { + + var left = ran.nextInt(userNames.size()); + var right = ran.nextInt(userNames.size()); + + while (left == right) left = ran.nextInt(userNames.size()); + + subscriptions.get(key).remove(userNames.get(left)); + subscriptions.get(key).remove(userNames.get(right)); + + startGame(key, getUser(left), getUser(right)); + } + } + } + + private NettyClient getUser(String username) { + return clientStore.all().stream().filter(e -> e.name().equalsIgnoreCase(username)).findFirst().orElse(null); + } + + private NettyClient getUser(long id) { + return clientStore.get(id); + } + + private void warnUserExpiredChallenge(NettyClient client, long challengeId) { + client.send("SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: \"" + challengeId + "\"}" + "\n"); + } + + private boolean isValidChallenge(GameChallenge gameChallenge) { // TODO move to challenge class + for (var user : gameChallenge.getUsers()) { + if (clientStore.get(user.id()) == null) { + return false; + } + + if (user.game() != null) { + return false; + } + + if (gameChallenge.isExpired()) { + return false; + } + } + + return true; + } } diff --git a/framework/src/main/java/org/toop/framework/networking/server/client/Client.java b/framework/src/main/java/org/toop/framework/networking/server/client/Client.java index 96ea515..cf97318 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/client/Client.java +++ b/framework/src/main/java/org/toop/framework/networking/server/client/Client.java @@ -1,6 +1,6 @@ package org.toop.framework.networking.server.client; -import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.OnlineTurnBasedGame; import org.toop.framework.utils.Pair; public interface Client { @@ -9,7 +9,7 @@ public interface Client { String name(); void setName(String name); - Game game(); + OnlineTurnBasedGame game(); P player(); void addGame(Pair gamePair); diff --git a/framework/src/main/java/org/toop/framework/networking/server/client/NettyClient.java b/framework/src/main/java/org/toop/framework/networking/server/client/NettyClient.java index 37eeb4b..22b5254 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/client/NettyClient.java +++ b/framework/src/main/java/org/toop/framework/networking/server/client/NettyClient.java @@ -2,14 +2,14 @@ package org.toop.framework.networking.server.client; import io.netty.channel.ChannelHandlerContext; import org.toop.framework.game.players.ServerPlayer; -import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.OnlineTurnBasedGame; import org.toop.framework.utils.Pair; -public class NettyClient implements Client { +public class NettyClient implements Client { final private long id; private ChannelHandlerContext ctx; private String name; - private Pair gamePair; + private Pair gamePair; public NettyClient(long userId, String name) { this.id = userId; @@ -27,7 +27,7 @@ public class NettyClient implements Client { } @Override - public void addGame(Pair gamePair) { + public void addGame(Pair gamePair) { if (this.gamePair == null) { this.gamePair = gamePair; } @@ -39,7 +39,7 @@ public class NettyClient implements Client { } @Override - public Game game() { + public OnlineTurnBasedGame game() { if (this.gamePair == null) { return null; } diff --git a/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java b/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java index ae1f071..d9df676 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java +++ b/framework/src/main/java/org/toop/framework/networking/server/connectionHandler/NettyClientSession.java @@ -3,7 +3,7 @@ package org.toop.framework.networking.server.connectionHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import org.toop.framework.game.players.ServerPlayer; -import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.OnlineTurnBasedGame; import org.toop.framework.networking.server.client.NettyClient; import org.toop.framework.networking.server.handlers.Handler; import org.toop.framework.networking.server.parsing.ParsedMessage; @@ -13,7 +13,7 @@ import org.toop.framework.networking.server.parsing.Parser; import java.util.Arrays; -public class NettyClientSession extends SimpleChannelInboundHandler implements ClientSession { +public class NettyClientSession extends SimpleChannelInboundHandler implements ClientSession { private final NettyClient client; private final Server server; @@ -26,7 +26,7 @@ public class NettyClientSession extends SimpleChannelInboundHandler impl } @Override - public Client client() { + public Client client() { return client; } @@ -35,7 +35,7 @@ public class NettyClientSession extends SimpleChannelInboundHandler impl ctx.writeAndFlush("Welcome " + client.id() + " please login" + "\n"); client.setCtx(ctx); - server.addUser(client); // TODO set correct name on login + server.addClient(client); // TODO set correct name on login } @Override @@ -59,6 +59,6 @@ public class NettyClientSession extends SimpleChannelInboundHandler impl @Override public void channelInactive(ChannelHandlerContext ctx) { - server.removeUser(client); + server.removeClient(client); } } diff --git a/framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java b/framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java index 28bc24a..b6da22c 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/server/handlers/MessageHandler.java @@ -1,7 +1,7 @@ package org.toop.framework.networking.server.handlers; import org.toop.framework.game.players.ServerPlayer; -import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.OnlineTurnBasedGame; import org.toop.framework.networking.server.Server; import org.toop.framework.networking.server.client.Client; import org.toop.framework.networking.server.parsing.ParsedMessage; @@ -10,9 +10,9 @@ import org.toop.framework.utils.Utils; public class MessageHandler implements Handler { private final Server server; - private final Client client; + private final Client client; - public MessageHandler(Server server, Client client) { + public MessageHandler(Server server, Client client) { this.server = server; this.client = client; } @@ -37,25 +37,27 @@ public class MessageHandler implements Handler { return (args.length >= 1); } - private void handleLogin(ParsedMessage p, Client client) { + private void handleLogin(ParsedMessage p, Client client) { if (!hasArgs(p.args())) return; client.setName(p.args()[0]); } - private void handleSubscribe(ParsedMessage p, Client client) { + private void handleSubscribe(ParsedMessage p, Client client) { + if (!hasArgs(p.args())) return; + + server.subscribeClient(p.args()[0], client.name()); + } + + private void handleHelp(ParsedMessage p, Client client) { // TODO } - private void handleHelp(ParsedMessage p, Client client) { + private void handleMessage(ParsedMessage p, Client client) { // TODO } - private void handleMessage(ParsedMessage p, Client client) { - // TODO - } - - private void handleGet(ParsedMessage p, Client client) { + private void handleGet(ParsedMessage p, Client client) { if (!hasArgs(p.args())) return; switch (p.args()[0]) { @@ -70,7 +72,7 @@ public class MessageHandler implements Handler { } } - private void handleChallenge(ParsedMessage p, Client client) { + private void handleChallenge(ParsedMessage p, Client client) { if (!hasArgs(p.args())) return; if (p.args().length < 2) return; @@ -92,10 +94,10 @@ public class MessageHandler implements Handler { return; } - server.challengeUser(client.name(), p.args()[0], p.args()[1]); + server.challengeClient(client.name(), p.args()[0], p.args()[1]); } - private void handleMove(ParsedMessage p, Client client) { + private void handleMove(ParsedMessage p, Client client) { if(!hasArgs(p.args())) return; // TODO check if not number diff --git a/framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java b/framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java index 9d13127..15fbaae 100644 --- a/framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java +++ b/framework/src/main/java/org/toop/framework/networking/server/stores/ClientStore.java @@ -1,7 +1,7 @@ package org.toop.framework.networking.server.stores; import org.toop.framework.game.players.ServerPlayer; -import org.toop.framework.networking.server.Game; +import org.toop.framework.networking.server.OnlineTurnBasedGame; import org.toop.framework.networking.server.client.Client; -public interface ClientStore> extends Store {} +public interface ClientStore> extends Store {} diff --git a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java index 6529fd3..447a11d 100644 --- a/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java +++ b/framework/src/test/java/org/toop/framework/networking/server/ServerTest.java @@ -4,7 +4,7 @@ //import org.junit.jupiter.api.BeforeEach; //import org.junit.jupiter.api.Test; //import org.toop.framework.gameFramework.model.game.PlayResult; -//import org.toop.framework.gameFramework.model.game.TurnBasedGame; +//import org.toop.framework.gameFramework.model.game.OnlineTurnBasedGame; //import org.toop.framework.gameFramework.model.player.Player; // //import java.time.Duration; @@ -13,7 +13,7 @@ // //public class ServerTest { // -// static class TurnBasedGameMock implements TurnBasedGame { +// static class TurnBasedGameMock implements OnlineTurnBasedGame { // private Player[] players; // // public TurnBasedGameMock() {} @@ -44,7 +44,7 @@ // } // // @Override -// public TurnBasedGame deepCopy() { +// public OnlineTurnBasedGame deepCopy() { // return null; // } // @@ -87,17 +87,17 @@ // } // // @Override -// public Game[] games() { -// return new Game[0]; +// public OnlineTurnBasedGame[] games() { +// return new OnlineTurnBasedGame[0]; // } // // @Override -// public void addGame(Game game) { +// public void addGame(OnlineTurnBasedGame game) { // // } // // @Override -// public void removeGame(Game game) { +// public void removeGame(OnlineTurnBasedGame game) { // // } // @@ -118,7 +118,7 @@ // @BeforeEach // void setup() { // -// var games = new ConcurrentHashMap>(); +// var games = new ConcurrentHashMap>(); // games.put("tictactoe", TurnBasedGameMock.class); // games.put("reversi", TurnBasedGameMock.class); // From 8cb0a86d4eb4f90f71ea1e2f33ce36f6943d7353 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:20:28 +0100 Subject: [PATCH 23/35] Working subscription, button only subs to reversi right now --- app/src/main/java/org/toop/app/Server.java | 2 +- .../org/toop/app/widget/view/ServerView.java | 13 +++++- .../framework/networking/server/Server.java | 41 +++++++++++++------ .../server/gateway/NettyGatewayServer.java | 1 - .../server/handlers/MessageHandler.java | 8 +++- 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index 0b10983..ed5adc9 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -118,7 +118,7 @@ public final class Server { return; } - primary = new ServerView(user, this::sendChallenge); + primary = new ServerView(user, this::sendChallenge, clientId); WidgetContainer.getCurrentView().transitionNextCustom(primary, "disconnect", this::disconnect); a.unsubscribe("connecting"); diff --git a/app/src/main/java/org/toop/app/widget/view/ServerView.java b/app/src/main/java/org/toop/app/widget/view/ServerView.java index 7365e74..3a3ce9b 100644 --- a/app/src/main/java/org/toop/app/widget/view/ServerView.java +++ b/app/src/main/java/org/toop/app/widget/view/ServerView.java @@ -10,16 +10,20 @@ import javafx.application.Platform; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.ListView; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.networking.connection.events.NetworkEvents; public final class ServerView extends ViewWidget { private final String user; private final Consumer onPlayerClicked; + private final long clientId; private final ListView