From c76b7a800e79f4acc9242c5f0df356291b825d1c Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:31:26 +0200 Subject: [PATCH] Server in working state, can be merged with working branch. --- .gitignore | 1 + .idea/dictionaries/project.xml | 1 + .../org/toop/tictactoe/LocalTicTacToe.java | 8 +- .../networking/NetworkingClient.java | 63 ++--- .../networking/NetworkingClientManager.java | 155 ++++++++---- .../NetworkingGameClientHandler.java | 146 ++++++++++- .../networking/events/NetworkEvents.java | 227 ++++++++++-------- 7 files changed, 399 insertions(+), 202 deletions(-) diff --git a/.gitignore b/.gitignore index 12c0ee3..b561d9a 100644 --- a/.gitignore +++ b/.gitignore @@ -100,4 +100,5 @@ build ############################## newgamesver-release-V1.jar server.properties +gameserver.log.* gameserver.log \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml index 5b1b09b..5f1391a 100644 --- a/.idea/dictionaries/project.xml +++ b/.idea/dictionaries/project.xml @@ -5,6 +5,7 @@ clid dcompile errorprone + flushnl gamelist playerlist tictactoe diff --git a/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java b/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java index d390ebd..1e7e017 100644 --- a/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java +++ b/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java @@ -33,7 +33,7 @@ public class LocalTicTacToe { // TODO: Implement runnable private boolean isLocal; private String gameId; - private long connectionId = -1; + private long clientId = -1; private String serverId = null; private boolean[] isAiPlayer = new boolean[2]; @@ -210,13 +210,13 @@ public class LocalTicTacToe { // TODO: Implement runnable } private void receiveMessageAction(NetworkEvents.ReceivedMessage receivedMessage) { - if (receivedMessage.ConnectionId() != this.connectionId) { + if (receivedMessage.clientId() != this.clientId) { return; } try { logger.info( - "Received message from {}: {}", this.connectionId, receivedMessage.message()); + "Received message from {}: {}", this.clientId, receivedMessage.message()); this.receivedQueue.put(receivedMessage.message()); } catch (InterruptedException e) { logger.error("Error waiting for received Message", e); @@ -224,7 +224,7 @@ public class LocalTicTacToe { // TODO: Implement runnable } private void sendCommand(String... args) { - new EventFlow().addPostEvent(NetworkEvents.SendCommand.class, this.connectionId, args).asyncPostEvent(); + new EventFlow().addPostEvent(NetworkEvents.SendCommand.class, this.clientId, args).asyncPostEvent(); } // private void endListeners() { diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingClient.java b/framework/src/main/java/org/toop/framework/networking/NetworkingClient.java index 7b88374..2526896 100644 --- a/framework/src/main/java/org/toop/framework/networking/NetworkingClient.java +++ b/framework/src/main/java/org/toop/framework/networking/NetworkingClient.java @@ -11,6 +11,8 @@ import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.networking.events.NetworkEvents; import java.util.function.Supplier; @@ -18,6 +20,8 @@ public class NetworkingClient { private static final Logger logger = LogManager.getLogger(NetworkingClient.class); private long connectionId; + private String host; + private int port; private Channel channel; private NetworkingGameClientHandler handler; @@ -47,13 +51,23 @@ public class NetworkingClient { }); ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); this.channel = channelFuture.channel(); + this.host = host; + this.port = port; } catch (Exception e) { logger.error("Failed to create networking client instance", e); } } public NetworkingGameClientHandler getHandler() { - return handler; + return this.handler; + } + + public String getHost() { + return this.host; + } + + public int getPort() { + return this.port; } public void setConnectionId(long connectionId) { @@ -83,55 +97,14 @@ public class NetworkingClient { } } - public void login(String username) { - this.writeAndFlush("login " + username + "\n"); - } - - public void logout() { - this.writeAndFlush("logout\n"); - } - - public void sendMove(int move) { - this.writeAndFlush("move " + move + "\n"); // append \n so server receives a full line - } - - public void getGamelist() { - this.writeAndFlush("get gamelist\n"); - } - - public void getPlayerlist() { - this.writeAndFlush("get playerlist\n"); - } - - public void subscribe(String gameType) { - this.writeAndFlush("subscribe " + gameType + "\n"); - } - - public void forfeit() { - this.writeAndFlush("forfeit\n"); - } - - public void challenge(String playerName, String gameType) { - this.writeAndFlush("challenge " + playerName + " " + gameType + "\n"); - } - - public void acceptChallenge(String challengeNumber) { - this.writeAndFlush("challenge accept " + challengeNumber + "\n"); - } - - public void sendChatMessage(String message) { - this.writeAndFlush("message " + "\"" + message + "\"" + "\n"); - } - - public void help(String command) { - this.writeAndFlush("help " + command + "\n"); - } - public void closeConnection() { if (this.channel != null && this.channel.isActive()) { this.channel.close().addListener(future -> { if (future.isSuccess()) { logger.info("Connection {} closed successfully", this.channel.remoteAddress()); + new EventFlow() + .addPostEvent(new NetworkEvents.ClosedConnection(this.connectionId)) + .asyncPostEvent(); } else { logger.error("Error closing connection {}. Error: {}", this.channel.remoteAddress(), diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java b/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java index bbd0bb4..0bcd1e9 100644 --- a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java +++ b/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java @@ -2,7 +2,6 @@ package org.toop.framework.networking; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -23,9 +22,22 @@ public class NetworkingClientManager { new EventFlow() .listen(this::handleStartClient) .listen(this::handleCommand) + .listen(this::handleSendLogin) + .listen(this::handleSendLogout) + .listen(this::handleSendGetPlayerlist) + .listen(this::handleSendGetGamelist) + .listen(this::handleSendSubscribe) + .listen(this::handleSendMove) + .listen(this::handleSendChallenge) + .listen(this::handleSendAcceptChallenge) + .listen(this::handleSendForfeit) + .listen(this::handleSendMessage) + .listen(this::handleSendHelp) + .listen(this::handleSendHelpForCommand) .listen(this::handleCloseClient) - .listen(this::getAllConnections) - .listen(this::shutdownAll); + .listen(this::handleChangeClientHost) + .listen(this::handleGetAllConnections) + .listen(this::handleShutdownAll); logger.info("NetworkingClientManager initialized"); } catch (Exception e) { logger.error("Failed to initialize the client manager", e); @@ -35,7 +47,7 @@ public class NetworkingClientManager { private long startClientRequest(String ip, int port) { long connectionId = new SnowflakeGenerator().nextId(); // TODO: Maybe use the one generated - try { // With EventFlow + try { // With EventFlow NetworkingClient client = new NetworkingClient( () -> new NetworkingGameClientHandler(connectionId), ip, @@ -43,13 +55,30 @@ public class NetworkingClientManager { connectionId); client.setConnectionId(connectionId); this.networkClients.put(connectionId, client); + logger.info("New client started successfully for {}:{}", ip, port); } catch (Exception e) { logger.error(e); } - logger.info("Client {} started", connectionId); return connectionId; } + private long startClientRequest(String ip, int port, long clientId) { + try { // With EventFlow + NetworkingClient client = new NetworkingClient( + () -> new NetworkingGameClientHandler(clientId), + ip, + port, + clientId); + client.setConnectionId(clientId); + this.networkClients.replace(clientId, client); + logger.info("New client started successfully for {}:{}, replaced: {}", ip, port, clientId); + } catch (Exception e) { + logger.error(e); + } + logger.info("Client {} started", clientId); + return clientId; + } + private void handleStartClient(NetworkEvents.StartClient event) { long id = this.startClientRequest(event.ip(), event.port()); new Thread(() -> { @@ -67,54 +96,96 @@ public class NetworkingClientManager { private void handleCommand( NetworkEvents.SendCommand event) { // TODO: Move this to ServerConnection class, keep it internal. - NetworkingClient client = this.networkClients.get(event.connectionId()); - logger.info("Preparing to send command: {} to server: {}", event.args(), client.getId()); + NetworkingClient client = this.networkClients.get(event.clientId()); String args = String.join(" ", event.args()); - client.writeAndFlushnl(args); + sendCommand(client, args); + } + + private void handleSendLogin(NetworkEvents.SendLogin event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, String.format("LOGIN %s", event.username())); + } + + private void handleSendLogout(NetworkEvents.SendLogout event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, "LOGOUT"); + } + + private void handleSendGetPlayerlist(NetworkEvents.SendGetPlayerlist event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, "GET PLAYERLIST"); + } + + private void handleSendGetGamelist(NetworkEvents.SendGetGamelist event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, "GET GAMELIST"); + } + + private void handleSendSubscribe(NetworkEvents.SendSubscribe event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, String.format("SUBSCRIBE %s", event.gameType())); + } + + private void handleSendMove(NetworkEvents.SendMove event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, String.format("MOVE %d", event.moveNumber())); + } + + private void handleSendChallenge(NetworkEvents.SendChallenge event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, String.format("CHALLENGE %s %s", event.usernameToChallenge(), event.gameType())); + } + + private void handleSendAcceptChallenge(NetworkEvents.SendAcceptChallenge event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, String.format("CHALLENGE ACCEPT %d", event.challengeId())); + } + + private void handleSendForfeit(NetworkEvents.SendForfeit event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, "FORFEIT"); + } + + private void handleSendMessage(NetworkEvents.SendMessage event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, String.format("MESSAGE %s", event.message())); + } + + private void handleSendHelp(NetworkEvents.SendHelp event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, "HELP"); + } + + private void handleSendHelpForCommand(NetworkEvents.SendHelpForCommand event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + sendCommand(client, String.format("HELP %s", event.command())); + } + + private void sendCommand(NetworkingClient client, String command) { + logger.info("Preparing to send command: {} to server: {}:{}. clientId: {}", + command.trim(), client.getHost(), client.getPort(), client.getId()); + client.writeAndFlushnl(command); + } + + private void handleChangeClientHost(NetworkEvents.ChangeClientHost event) { + NetworkingClient client = this.networkClients.get(event.clientId()); + client.closeConnection(); + startClientRequest(event.ip(), event.port(), event.clientId()); } private void handleCloseClient(NetworkEvents.CloseClient event) { - NetworkingClient client = this.networkClients.get(event.connectionId()); + NetworkingClient client = this.networkClients.get(event.clientId()); client.closeConnection(); // TODO: Check if not blocking, what if error, mb not remove? - this.networkClients.remove(event.connectionId()); - logger.info("Client {} closed successfully.", event.connectionId()); + this.networkClients.remove(event.clientId()); + logger.info("Client {} closed successfully.", event.clientId()); } -// private void handleReconnect(Events.ServerEvents.Reconnect event) { -// NetworkingClient client = this.networkClients.get(event.connectionId()); -// if (client != null) { -// try { -// client; -// logger.info("Server {} reconnected", event.connectionId()); -// } catch (Exception e) { -// logger.error("Server {} failed to reconnect", event.connectionId(), e); -// GlobalEventBus.post(new Events.ServerEvents.CouldNotConnect(event.connectionId())); -// } -// } -// } // TODO: Reconnect on disconnect - - // private void handleChangeConnection(Events.ServerEvents.ChangeConnection event) { - // ServerConnection serverConnection = this.serverConnections.get(event.connectionId()); - // if (serverConnection != null) { - // try { - // serverConnection.connect(event.ip(), event.port()); - // logger.info("Server {} changed connection to {}:{}", event.connectionId(), - // event.ip(), event.port()); - // } catch (Exception e) { - // logger.error("Server {} failed to change connection", event.connectionId(), - // e); - // GlobalEventBus.post(new - // Events.ServerEvents.CouldNotConnect(event.connectionId())); - // } - // } - // } TODO - - private void getAllConnections(NetworkEvents.RequestsAllClients request) { + private void handleGetAllConnections(NetworkEvents.RequestsAllClients request) { List a = new ArrayList<>(this.networkClients.values()); request.future().complete(a); } - public void shutdownAll(NetworkEvents.ForceCloseAllClients request) { + public void handleShutdownAll(NetworkEvents.ForceCloseAllClients request) { this.networkClients.values().forEach(NetworkingClient::closeConnection); this.networkClients.clear(); logger.info("All servers shut down"); diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java b/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java index a88686c..50517d3 100644 --- a/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java +++ b/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java @@ -2,13 +2,11 @@ package org.toop.framework.networking; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import jdk.jfr.Event; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.networking.events.NetworkEvents; -import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,23 +42,147 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter { } private void parseServerReturn(String rec) { - if (rec.toLowerCase().contains("playerlist")) { - playerListHandler(rec); - } else if (rec.toLowerCase().contains("close")) { - } else {} + String recSrvRemoved = rec.substring("SVR ".length()); + Pattern gamePattern = Pattern.compile("GAME (\\w+)", Pattern.CASE_INSENSITIVE); + Matcher gameMatch = gamePattern.matcher(recSrvRemoved); + + if (gameMatch.find()) { + switch(gameMatch.group(1)) { + case "YOURTURN": gameYourTurnHandler(recSrvRemoved); return; + case "MOVE": gameMoveHandler(recSrvRemoved); return; + case "MATCH": gameMatchHandler(recSrvRemoved); return; + case "CHALLENGE": gameChallengeHandler(recSrvRemoved); return; + case "WIN", + "DRAW", + "LOSE": gameWinConditionHandler(recSrvRemoved); return; + default: return; + } + } else { + + Pattern getPattern = Pattern.compile("(\\w+)", Pattern.CASE_INSENSITIVE); + Matcher getMatch = getPattern.matcher(recSrvRemoved); + + if (getMatch.find()) { + switch(getMatch.group(1)) { + case "PLAYERLIST": playerlistHandler(recSrvRemoved); return; + case "GAMELIST": gamelistHandler(recSrvRemoved); return; + case "HELP": helpHandler(recSrvRemoved); return; + default: return; + } + } else { + return; // TODO: Should be an error. + } + } } - private void playerListHandler(String rec) { - Pattern pattern = Pattern.compile("\"([^\"]+)\""); - String[] players = pattern.matcher(rec) + private void gameMoveHandler(String rec) { + String[] msg = Pattern + .compile("(?:player|details|move):\\s*\"?([^\",}]+)\"?", Pattern.CASE_INSENSITIVE) + .matcher(rec) + .results() + .map(m -> m.group(1).trim()) + .toArray(String[]::new); + + new EventFlow() + .addPostEvent(new NetworkEvents.GameMoveResponse(this.connectionId, msg[0], msg[1], msg[2])) + .asyncPostEvent(); + } + + private void gameWinConditionHandler(String rec) { + String condition = Pattern + .compile("\\b(win|draw|lose)\\b", Pattern.CASE_INSENSITIVE) + .matcher(rec) + .results() + .toString() + .trim(); + + new EventFlow() + .addPostEvent(new NetworkEvents.GameResultResponse(this.connectionId, condition)) + .asyncPostEvent(); + } + + private void gameChallengeHandler(String rec) { + boolean isCancelled = rec.toLowerCase().startsWith("challenge accepted"); + try { + String[] msg = Pattern + .compile("(?:CHALLENGER|GAMETYPE|CHALLENGENUMBER):\\s*\"?(.*?)\"?\\s*(?:,|})") + .matcher(rec) .results() - .map(m -> m.group(1)) + .map(m -> m.group().trim()) .toArray(String[]::new); - new EventFlow() - .addPostEvent(new NetworkEvents.PlayerListResponse(this.connectionId, players)) + if (isCancelled) new EventFlow() + .addPostEvent(new NetworkEvents.ChallengeCancelledResponse(this.connectionId, msg[0])) .asyncPostEvent(); + else new EventFlow() + .addPostEvent(new NetworkEvents.ChallengeResponse(this.connectionId, msg[0], msg[1], msg[2])) + .asyncPostEvent(); + } catch (ArrayIndexOutOfBoundsException e) { + logger.error("Array out of bounds for: {}", rec, e); + } + } + + private void gameMatchHandler(String rec) { + try { + String[] msg = Pattern + .compile("\"([^\"]*)\"") + .matcher(rec) + .results() + .map(m -> m.group(1).trim()) + .toArray(String[]::new); + + // [0] playerToMove, [1] gameType, [2] opponent + new EventFlow() + .addPostEvent(new NetworkEvents.GameMatchResponse(this.connectionId, msg[0], msg[1], msg[2])) + .asyncPostEvent(); + } catch (ArrayIndexOutOfBoundsException e) { + logger.error("Array out of bounds for: {}", rec, e); + } + + } + + private void gameYourTurnHandler(String rec) { + String msg = Pattern + .compile("TURNMESSAGE:\\s*\"([^\"]*)\"") + .matcher(rec) + .results() + .toString() + .trim(); + + new EventFlow() + .addPostEvent(new NetworkEvents.YourTurnResponse(this.connectionId, msg)) + .asyncPostEvent(); + } + + private void playerlistHandler(String rec) { + String[] players = Pattern + .compile("\"([^\"]+)\"") + .matcher(rec) + .results() + .map(m -> m.group(1).trim()) + .toArray(String[]::new); + + new EventFlow() + .addPostEvent(new NetworkEvents.PlayerlistResponse(this.connectionId, players)) + .asyncPostEvent(); + } + + private void gamelistHandler(String rec) { + String[] gameTypes = Pattern + .compile("\"([^\"]+)\"") + .matcher(rec) + .results() + .map(m -> m.group(1).trim()) + .toArray(String[]::new); + + new EventFlow() + .addPostEvent(new NetworkEvents.GamelistResponse(this.connectionId, gameTypes)) + .asyncPostEvent(); + } + + private void helpHandler(String rec) { + logger.info(rec); } @Override diff --git a/framework/src/main/java/org/toop/framework/networking/events/NetworkEvents.java b/framework/src/main/java/org/toop/framework/networking/events/NetworkEvents.java index 9596014..c260296 100644 --- a/framework/src/main/java/org/toop/framework/networking/events/NetworkEvents.java +++ b/framework/src/main/java/org/toop/framework/networking/events/NetworkEvents.java @@ -13,93 +13,107 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +/** + * A collection of networking-related event records for use with the {@link org.toop.framework.eventbus.GlobalEventBus}. + *

+ * This class defines all the events that can be posted or listened to in the networking subsystem. + * Events are separated into those with unique IDs (EventWithSnowflake) and those without (EventWithoutSnowflake). + *

+ */ public class NetworkEvents extends EventsBase { /** - * BLOCKING Requests all active connections. The result is returned via the provided - * CompletableFuture. + * Requests all active client connections. + *

+ * This is a blocking event. The result will be delivered via the provided {@link CompletableFuture}. + *

* - * @param future List of all connections in string form. + * @param future CompletableFuture to receive the list of active {@link NetworkingClient} instances. */ public record RequestsAllClients(CompletableFuture> future) implements EventWithoutSnowflake {} - /** Forces closing all active connections immediately. */ + /** Forces all active client connections to close immediately. */ public record ForceCloseAllClients() implements EventWithoutSnowflake {} - public record PlayerListResponse(long clientId, String[] playerlist) implements EventWithoutSnowflake {} + /** Response indicating a challenge was cancelled. */ + public record ChallengeCancelledResponse(long clientId, String challengeId) implements EventWithoutSnowflake {} - public record CloseClient(long connectionId) implements EventWithoutSnowflake {} + /** Response indicating a challenge was received. */ + public record ChallengeResponse(long clientId, String challengerName, String gameType, String challengeId) + implements EventWithoutSnowflake {} + + /** Response containing a list of players for a client. */ + public record PlayerlistResponse(long clientId, String[] playerlist) implements EventWithoutSnowflake {} + + /** Response containing a list of games for a client. */ + public record GamelistResponse(long clientId, String[] gamelist) implements EventWithoutSnowflake {} + + /** Response indicating a game match information for a client. */ + public record GameMatchResponse(long clientId, String playerToMove, String gameType, String opponent) + implements EventWithoutSnowflake {} + + /** Response indicating the result of a game. */ + public record GameResultResponse(long clientId, String condition) implements EventWithoutSnowflake {} + + /** Response indicating a game move occurred. */ + public record GameMoveResponse(long clientId, String player, String details, String move) + implements EventWithoutSnowflake {} + + /** Response indicating it is the player's turn. */ + public record YourTurnResponse(long clientId, String message) implements EventWithoutSnowflake {} + + /** Request to send login credentials for a client. */ + public record SendLogin(long clientId, String username) implements EventWithoutSnowflake {} + + /** Request to log out a client. */ + public record SendLogout(long clientId) implements EventWithoutSnowflake {} + + /** Request to retrieve the player list for a client. */ + public record SendGetPlayerlist(long clientId) implements EventWithoutSnowflake {} + + /** Request to retrieve the game list for a client. */ + public record SendGetGamelist(long clientId) implements EventWithoutSnowflake {} + + /** Request to subscribe a client to a game type. */ + public record SendSubscribe(long clientId, String gameType) implements EventWithoutSnowflake {} + + /** Request to make a move in a game. */ + public record SendMove(long clientId, short moveNumber) implements EventWithoutSnowflake {} + + /** Request to challenge another player. */ + public record SendChallenge(long clientId, String usernameToChallenge, String gameType) + implements EventWithoutSnowflake {} + + /** Request to accept a challenge. */ + public record SendAcceptChallenge(long clientId, int challengeId) implements EventWithoutSnowflake {} + + /** Request to forfeit a game. */ + public record SendForfeit(long clientId) implements EventWithoutSnowflake {} + + /** Request to send a message from a client. */ + public record SendMessage(long clientId, String message) implements EventWithoutSnowflake {} + + /** Request to display help to a client. */ + public record SendHelp(long clientId) implements EventWithoutSnowflake {} + + /** Request to display help for a specific command. */ + public record SendHelpForCommand(long clientId, String command) implements EventWithoutSnowflake {} + + /** Request to close a specific client connection. */ + public record CloseClient(long clientId) implements EventWithoutSnowflake {} /** - * Event to start a new client connection to a server. + * Event to start a new client connection. *

- * This event is typically posted to the {@code GlobalEventBus} to initiate the creation of - * a client connection, and carries all information needed to establish that connection: - *
- * - A factory for creating the Netty handler that will manage the connection - *
- * - The server's IP address and port - *
- * - A unique event identifier for correlation with follow-up events + * Carries IP, port, and a unique event ID for correlation with responses. *

* - *

- * The {@link #eventSnowflake()} allows callers to correlate the {@code StartClient} event - * with subsequent success/failure events. For example, a {@code StartClientSuccess} - * or {@code StartClientFailure} event may carry the same {@code eventId}. - *

- * - * @param ip The IP address of the server to connect to. - * @param port The port number of the server to connect to. - * @param eventSnowflake A unique identifier for this event, typically injected - * automatically by the {@link org.toop.framework.eventbus.EventFlow}. + * @param ip Server IP address. + * @param port Server port. + * @param eventSnowflake Unique event identifier for correlation. */ - public record StartClient( - String ip, - int port, - long eventSnowflake - ) implements EventWithSnowflake { + public record StartClient(String ip, int port, long eventSnowflake) implements EventWithSnowflake { - /** - * Returns a map representation of this event, where keys are record component names - * and values are their corresponding values. Useful for generic logging, debugging, - * or serializing events without hardcoding field names. - * - * @return a {@code Map} containing field names and values - */ - @Override - public Map result() { - return Stream.of(this.getClass().getRecordComponents()) - .collect(Collectors.toMap( - RecordComponent::getName, - rc -> { - try { - return rc.getAccessor().invoke(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - )); - } - - /** - * Returns the unique event identifier used for correlating this event. - * - * @return the event ID string - */ - @Override - public long eventSnowflake() { - return this.eventSnowflake; - } - } - - /** - * - * @param clientId The ID of the client to be used in requests. - * @param eventSnowflake The eventID used in checking if event is for you. - */ - public record StartClientResponse(long clientId, long eventSnowflake) - implements EventWithSnowflake { @Override public Map result() { return Stream.of(this.getClass().getRecordComponents()) @@ -122,52 +136,67 @@ public class NetworkEvents extends EventsBase { } /** + * Response confirming a client was started. * - * @param clientId The ID of the client that received the response. + * @param clientId The client ID assigned to the new connection. + * @param eventSnowflake Event ID used for correlation. */ + public record StartClientResponse(long clientId, long eventSnowflake) implements EventWithSnowflake { + @Override + public Map result() { + return Stream.of(this.getClass().getRecordComponents()) + .collect(Collectors.toMap( + RecordComponent::getName, + rc -> { + try { + return rc.getAccessor().invoke(this); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + )); + } + + @Override + public long eventSnowflake() { + return this.eventSnowflake; + } + } + + /** Generic server response. */ public record ServerResponse(long clientId) implements EventWithoutSnowflake {} /** - * Triggers sending a command to a server. + * Request to send a command to a server. * - * @param connectionId The UUID of the connection to send the command on. + * @param clientId The client connection ID. * @param args The command arguments. */ - public record SendCommand(long connectionId, String... args) implements EventWithoutSnowflake {} - /** - * Triggers reconnecting to a previous address. - * - * @param connectionId The identifier of the connection being reconnected. - */ - public record Reconnect(long connectionId) implements EventWithoutSnowflake {} + public record SendCommand(long clientId, String... args) implements EventWithoutSnowflake {} + /** WIP (Not working) Request to reconnect a client to a previous address. */ + public record Reconnect(long clientId) implements EventWithoutSnowflake {} /** - * Triggers when the server client receives a message. + * Response triggered when a message is received from a server. * - * @param ConnectionId The snowflake id of the connection that received the message. - * @param message The message received. + * @param clientId The connection ID that received the message. + * @param message The message content. */ - public record ReceivedMessage(long ConnectionId, String message) implements EventWithoutSnowflake {} + public record ReceivedMessage(long clientId, String message) implements EventWithoutSnowflake {} /** - * Triggers changing connection to a new address. + * Request to change a client connection to a new server. * - * @param connectionId The identifier of the connection being changed. - * @param ip The new IP address. - * @param port The new port. + * @param clientId The client connection ID. + * @param ip The new server IP. + * @param port The new server port. */ - public record ChangeClient(long connectionId, String ip, int port) implements EventWithoutSnowflake {} + public record ChangeClientHost(long clientId, String ip, int port) implements EventWithoutSnowflake {} + /** WIP (Not working) Response indicating that the client could not connect. */ + public record CouldNotConnect(long clientId) implements EventWithoutSnowflake {} - /** - * Triggers when the server couldn't connect to the desired address. - * - * @param connectionId The identifier of the connection that failed. - */ - public record CouldNotConnect(long connectionId) implements EventWithoutSnowflake {} - - /** WIP Triggers when a connection closes. */ - public record ClosedConnection() implements EventWithoutSnowflake {} - + /** Event indicating a client connection was closed. */ + public record ClosedConnection(long clientId) implements EventWithoutSnowflake {} }