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] 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()); // } //