diff --git a/server/src/main/java/com/miti99/caro/server/event/RequestDispatcher.java b/server/src/main/java/com/miti99/caro/server/event/RequestDispatcher.java index ff2ab62..0bb7cab 100644 --- a/server/src/main/java/com/miti99/caro/server/event/RequestDispatcher.java +++ b/server/src/main/java/com/miti99/caro/server/event/RequestDispatcher.java @@ -1,6 +1,19 @@ package com.miti99.caro.server.event; import com.miti99.caro.common.entity.ClientSide; +import com.miti99.caro.server.event.handler.ClientExitHandler; +import com.miti99.caro.server.event.handler.CreatePveRoomHandler; +import com.miti99.caro.server.event.handler.CreateRoomHandler; +import com.miti99.caro.server.event.handler.GameMoveHandler; +import com.miti99.caro.server.event.handler.GameReadyHandler; +import com.miti99.caro.server.event.handler.GameResetHandler; +import com.miti99.caro.server.event.handler.GameStartingHandler; +import com.miti99.caro.server.event.handler.GetRoomsHandler; +import com.miti99.caro.server.event.handler.JoinRoomHandler; +import com.miti99.caro.server.event.handler.SetClientInfoHandler; +import com.miti99.caro.server.event.handler.SetNicknameHandler; +import com.miti99.caro.server.event.handler.WatchGameExitHandler; +import com.miti99.caro.server.event.handler.WatchGameHandler; import com.miti99.caro.server.event.request.ClientExitRequestRecord; import com.miti99.caro.server.event.request.ClientRequest; import com.miti99.caro.server.event.request.CreatePveRoomRequestRecord; @@ -20,9 +33,6 @@ import com.miti99.caro.server.event.request.WatchGameRequestRecord; /** * Routes a {@link ClientRequest} record to its handler via exhaustive pattern * matching. Replaces the old reflection-based {@code ServerEventListener} lookup. - * - *
Phase 02a: only {@code HeartbeatRequestRecord} has a real no-op implementation;
- * every other case throws {@link UnsupportedOperationException} pending Phase 02b.
*/
public final class RequestDispatcher {
@@ -34,23 +44,19 @@ public final class RequestDispatcher {
case HeartbeatRequestRecord r -> {
/* no-op: heartbeat just refreshes IdleStateHandler */
}
- case SetNicknameRequestRecord r -> throw todo("set_nickname");
- case SetClientInfoRequestRecord r -> throw todo("set_client_info");
- case CreateRoomRequestRecord r -> throw todo("create_room");
- case CreatePveRoomRequestRecord r -> throw todo("create_pve_room");
- case GetRoomsRequestRecord r -> throw todo("get_rooms");
- case JoinRoomRequestRecord r -> throw todo("join_room");
- case GameStartingRequestRecord r -> throw todo("game_starting");
- case GameReadyRequestRecord r -> throw todo("game_ready");
- case GameMoveRequestRecord r -> throw todo("game_move");
- case GameResetRequestRecord r -> throw todo("game_reset");
- case WatchGameRequestRecord r -> throw todo("watch_game");
- case WatchGameExitRequestRecord r -> throw todo("watch_game_exit");
- case ClientExitRequestRecord r -> throw todo("client_exit");
+ case SetNicknameRequestRecord r -> SetNicknameHandler.handle(client, r);
+ case SetClientInfoRequestRecord r -> SetClientInfoHandler.handle(client, r);
+ case CreateRoomRequestRecord r -> CreateRoomHandler.handle(client, r);
+ case CreatePveRoomRequestRecord r -> CreatePveRoomHandler.handle(client, r);
+ case GetRoomsRequestRecord r -> GetRoomsHandler.handle(client, r);
+ case JoinRoomRequestRecord r -> JoinRoomHandler.handle(client, r);
+ case GameStartingRequestRecord r -> GameStartingHandler.handle(client, r);
+ case GameReadyRequestRecord r -> GameReadyHandler.handle(client, r);
+ case GameMoveRequestRecord r -> GameMoveHandler.handle(client, r);
+ case GameResetRequestRecord r -> GameResetHandler.handle(client, r);
+ case WatchGameRequestRecord r -> WatchGameHandler.handle(client, r);
+ case WatchGameExitRequestRecord r -> WatchGameExitHandler.handle(client, r);
+ case ClientExitRequestRecord r -> ClientExitHandler.handle(client, r);
}
}
-
- private static UnsupportedOperationException todo(String name) {
- return new UnsupportedOperationException("TODO phase 02b: " + name);
- }
}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/ClientExitHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/ClientExitHandler.java
new file mode 100644
index 0000000..e0e4e3a
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/ClientExitHandler.java
@@ -0,0 +1,46 @@
+package com.miti99.caro.server.event.handler;
+
+import com.miti99.caro.common.channel.ChannelUtils;
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.common.entity.Room;
+import com.miti99.caro.protocol.ClientExitResponse;
+import com.miti99.caro.protocol.Response;
+import com.miti99.caro.server.ServerContains;
+import com.miti99.caro.server.event.request.ClientExitRequestRecord;
+
+public final class ClientExitHandler {
+
+ private static final Object LOCK = new Object();
+
+ private ClientExitHandler() {
+ }
+
+ public static void handle(ClientSide clientSide, ClientExitRequestRecord req) {
+ synchronized (LOCK) {
+ Room room = ServerContains.getRoom(clientSide.getRoomId());
+ if (room == null) {
+ return;
+ }
+
+ Response notice = Response.newBuilder()
+ .setClientExit(ClientExitResponse.newBuilder()
+ .setRoomId(room.getId())
+ .setExitClientId(clientSide.getId())
+ .setExitClientNickname(clientSide.getNickname() == null ? "" : clientSide.getNickname()))
+ .build();
+
+ for (ClientSide client : room.getClientSideList()) {
+ if (client.getChannel() != null) {
+ ChannelUtils.push(client.getChannel(), notice);
+ client.init();
+ }
+ }
+
+ for (ClientSide watcher : room.getWatcherList()) {
+ ChannelUtils.push(watcher.getChannel(), notice);
+ }
+
+ ServerContains.removeRoom(room.getId());
+ }
+ }
+}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/ClientOfflineHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/ClientOfflineHandler.java
new file mode 100644
index 0000000..86268b6
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/ClientOfflineHandler.java
@@ -0,0 +1,52 @@
+package com.miti99.caro.server.event.handler;
+
+import com.miti99.caro.common.channel.ChannelUtils;
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.common.entity.Room;
+import com.miti99.caro.protocol.ClientExitResponse;
+import com.miti99.caro.protocol.Response;
+import com.miti99.caro.server.ServerContains;
+
+/**
+ * Triggered directly by the Netty pipeline on channel close or idle timeout —
+ * NOT routed through the {@code RequestDispatcher}. There is no wire-level
+ * request for this event; it is observed server-side only.
+ */
+public final class ClientOfflineHandler {
+
+ private ClientOfflineHandler() {
+ }
+
+ public static void handle(ClientSide clientSide) {
+ if (clientSide == null) {
+ return;
+ }
+ Room room = ServerContains.getRoom(clientSide.getRoomId());
+
+ if (room == null) {
+ ServerContains.CLIENT_SIDE_MAP.remove(clientSide.getId());
+ return;
+ }
+
+ if (room.getWatcherList().contains(clientSide)) {
+ room.getWatcherList().remove(clientSide);
+ return;
+ }
+
+ Response notice = Response.newBuilder()
+ .setClientExit(ClientExitResponse.newBuilder()
+ .setRoomId(room.getId())
+ .setExitClientId(clientSide.getId())
+ .setExitClientNickname(clientSide.getNickname() == null ? "" : clientSide.getNickname()))
+ .build();
+
+ for (ClientSide client : room.getClientSideList()) {
+ if (client.getChannel() != null && client.getId() != clientSide.getId()) {
+ ChannelUtils.push(client.getChannel(), notice);
+ client.init();
+ }
+ }
+
+ ServerContains.removeRoom(room.getId());
+ }
+}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/CreatePveRoomHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/CreatePveRoomHandler.java
new file mode 100644
index 0000000..4a359d1
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/CreatePveRoomHandler.java
@@ -0,0 +1,66 @@
+package com.miti99.caro.server.event.handler;
+
+import com.miti99.caro.common.channel.ChannelUtils;
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.common.entity.Room;
+import com.miti99.caro.common.enums.ClientRole;
+import com.miti99.caro.common.enums.ClientStatus;
+import com.miti99.caro.common.enums.RoomStatus;
+import com.miti99.caro.common.enums.RoomType;
+import com.miti99.caro.protocol.PveDifficultyNotSupportResponse;
+import com.miti99.caro.protocol.Response;
+import com.miti99.caro.server.ServerContains;
+import com.miti99.caro.server.event.request.CreatePveRoomRequestRecord;
+import com.miti99.caro.server.event.request.GameStartingRequestRecord;
+
+public final class CreatePveRoomHandler {
+
+ private CreatePveRoomHandler() {
+ }
+
+ public static void handle(ClientSide clientSide, CreatePveRoomRequestRecord req) {
+ int difficulty = req.difficulty();
+ if (difficulty < 1 || difficulty > 3) {
+ ChannelUtils.push(clientSide.getChannel(), Response.newBuilder()
+ .setPveDifficultyNotSupport(PveDifficultyNotSupportResponse.getDefaultInstance())
+ .build());
+ return;
+ }
+
+ Room room = new Room(ServerContains.getServerId());
+ room.setType(RoomType.PVE);
+ room.setStatus(RoomStatus.WAIT);
+ room.setRoomOwner(clientSide.getNickname());
+ room.setDifficultyCoefficient(difficulty);
+ room.setCreateTime(System.currentTimeMillis());
+ room.setLastFlushTime(System.currentTimeMillis());
+
+ // Add human player
+ room.getClientSideMap().put(clientSide.getId(), clientSide);
+ room.getClientSideList().add(clientSide);
+ clientSide.setRoomId(room.getId());
+ clientSide.setRole(ClientRole.BLACK_PLAYER);
+
+ // Add AI robot (1 robot for 2-player Gomoku)
+ ClientSide robot = new ClientSide(-ServerContains.getClientId(), ClientStatus.PLAYING, null);
+ robot.setNickname("AI_" + getDifficultyName(difficulty));
+ robot.setRole(ClientRole.WHITE_PLAYER);
+ robot.setRoomId(room.getId());
+ room.getClientSideMap().put(robot.getId(), robot);
+ room.getClientSideList().add(robot);
+ ServerContains.CLIENT_SIDE_MAP.put(robot.getId(), robot);
+
+ ServerContains.addRoom(room);
+
+ // Auto-start game
+ GameStartingHandler.handle(clientSide, new GameStartingRequestRecord());
+ }
+
+ private static String getDifficultyName(int difficulty) {
+ return switch (difficulty) {
+ case 2 -> "Medium";
+ case 3 -> "Hard";
+ default -> "Easy";
+ };
+ }
+}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/CreateRoomHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/CreateRoomHandler.java
new file mode 100644
index 0000000..a5e22ca
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/CreateRoomHandler.java
@@ -0,0 +1,43 @@
+package com.miti99.caro.server.event.handler;
+
+import com.miti99.caro.common.channel.ChannelUtils;
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.common.entity.Room;
+import com.miti99.caro.common.enums.ClientRole;
+import com.miti99.caro.common.enums.ClientStatus;
+import com.miti99.caro.common.enums.RoomStatus;
+import com.miti99.caro.common.enums.RoomType;
+import com.miti99.caro.protocol.Response;
+import com.miti99.caro.protocol.RoomCreateSuccessResponse;
+import com.miti99.caro.server.ServerContains;
+import com.miti99.caro.server.event.request.CreateRoomRequestRecord;
+
+public final class CreateRoomHandler {
+
+ private CreateRoomHandler() {
+ }
+
+ public static void handle(ClientSide clientSide, CreateRoomRequestRecord req) {
+ Room room = new Room(ServerContains.getServerId());
+ room.setStatus(RoomStatus.WAIT);
+ room.setType(RoomType.PVP);
+ room.setRoomOwner(clientSide.getNickname());
+ room.getClientSideMap().put(clientSide.getId(), clientSide);
+ room.getClientSideList().add(clientSide);
+ room.setCreateTime(System.currentTimeMillis());
+ room.setLastFlushTime(System.currentTimeMillis());
+
+ clientSide.setRoomId(room.getId());
+ clientSide.setRole(ClientRole.BLACK_PLAYER);
+ clientSide.setStatus(ClientStatus.NO_READY);
+
+ ServerContains.addRoom(room);
+
+ ChannelUtils.push(clientSide.getChannel(), Response.newBuilder()
+ .setRoomCreateSuccess(RoomCreateSuccessResponse.newBuilder()
+ .setId(room.getId())
+ .setRoomOwner(room.getRoomOwner())
+ .setRoomType(room.getType().name()))
+ .build());
+ }
+}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/GameMoveHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/GameMoveHandler.java
new file mode 100644
index 0000000..f5662bb
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/GameMoveHandler.java
@@ -0,0 +1,146 @@
+package com.miti99.caro.server.event.handler;
+
+import com.miti99.caro.common.channel.ChannelUtils;
+import com.miti99.caro.common.entity.Board;
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.common.entity.GameMove;
+import com.miti99.caro.common.entity.Room;
+import com.miti99.caro.common.enums.GameResult;
+import com.miti99.caro.common.enums.PieceType;
+import com.miti99.caro.common.enums.RoomType;
+import com.miti99.caro.common.helper.GomokuHelper;
+import com.miti99.caro.common.robot.GomokuAI;
+import com.miti99.caro.protocol.GameMoveNotYourTurnResponse;
+import com.miti99.caro.protocol.GameMoveOccupiedResponse;
+import com.miti99.caro.protocol.GameMoveOutOfBoundsResponse;
+import com.miti99.caro.protocol.GameMoveSuccessResponse;
+import com.miti99.caro.protocol.GameOverResponse;
+import com.miti99.caro.protocol.Response;
+import com.miti99.caro.protocol.RoomPlayFailNotFoundResponse;
+import com.miti99.caro.server.ServerContains;
+import com.miti99.caro.server.event.request.GameMoveRequestRecord;
+
+public final class GameMoveHandler {
+
+ private GameMoveHandler() {
+ }
+
+ public static void handle(ClientSide clientSide, GameMoveRequestRecord req) {
+ Room room = ServerContains.getRoom(clientSide.getRoomId());
+ if (room == null) {
+ ChannelUtils.push(clientSide.getChannel(), Response.newBuilder()
+ .setRoomPlayFailNotFound(RoomPlayFailNotFoundResponse.getDefaultInstance())
+ .build());
+ return;
+ }
+
+ int row = req.row();
+ int col = req.col();
+
+ if (!room.isPlayerTurn(clientSide.getId())) {
+ ChannelUtils.push(clientSide.getChannel(), Response.newBuilder()
+ .setGameMoveNotYourTurn(GameMoveNotYourTurnResponse.getDefaultInstance())
+ .build());
+ return;
+ }
+
+ Board board = room.getGameBoard();
+
+ if (row < 0 || row >= Board.BOARD_SIZE || col < 0 || col >= Board.BOARD_SIZE) {
+ ChannelUtils.push(clientSide.getChannel(), Response.newBuilder()
+ .setGameMoveOutOfBounds(GameMoveOutOfBoundsResponse.getDefaultInstance())
+ .build());
+ return;
+ }
+
+ if (board.getPiece(row, col) != PieceType.EMPTY) {
+ ChannelUtils.push(clientSide.getChannel(), Response.newBuilder()
+ .setGameMoveOccupied(GameMoveOccupiedResponse.getDefaultInstance())
+ .build());
+ return;
+ }
+
+ GameResult result = GomokuHelper.makeMove(room, row, col, clientSide.getId());
+
+ broadcastToRoom(room, Response.newBuilder()
+ .setGameMoveSuccess(GameMoveSuccessResponse.newBuilder()
+ .setRow(row)
+ .setCol(col)
+ .setPiece(room.getPlayerPiece(clientSide.getId()).name())
+ .setPlayerNickname(clientSide.getNickname() == null ? "" : clientSide.getNickname())
+ .setPlayerId(clientSide.getId()))
+ .build());
+
+ if (result != GameResult.IN_PROGRESS) {
+ handleGameOver(room, result);
+ return;
+ }
+
+ if (room.getType() == RoomType.PVE) {
+ handleAIMove(room);
+ }
+ }
+
+ private static void handleAIMove(Room room) {
+ ClientSide aiPlayer = null;
+ for (ClientSide client : room.getClientSideList()) {
+ if (client.getChannel() == null) {
+ aiPlayer = client;
+ break;
+ }
+ }
+ if (aiPlayer == null || !room.isPlayerTurn(aiPlayer.getId())) {
+ return;
+ }
+
+ PieceType aiPiece = room.getPlayerPiece(aiPlayer.getId());
+ GomokuAI ai = new GomokuAI(aiPiece);
+ GameMove aiMove = ai.getNextMove(room.getGameBoard(), room.getDifficultyCoefficient());
+ if (aiMove == null) {
+ return;
+ }
+
+ GameResult result = GomokuHelper.makeMove(room, aiMove.getRow(), aiMove.getCol(), aiPlayer.getId());
+
+ broadcastToRoom(room, Response.newBuilder()
+ .setGameMoveSuccess(GameMoveSuccessResponse.newBuilder()
+ .setRow(aiMove.getRow())
+ .setCol(aiMove.getCol())
+ .setPiece(aiPiece.name())
+ .setPlayerNickname(aiPlayer.getNickname() == null ? "" : aiPlayer.getNickname())
+ .setPlayerId(aiPlayer.getId()))
+ .build());
+
+ if (result != GameResult.IN_PROGRESS) {
+ handleGameOver(room, result);
+ }
+ }
+
+ private static void handleGameOver(Room room, GameResult result) {
+ String winnerNickname = "";
+ if (result == GameResult.BLACK_WIN) {
+ ClientSide winner = room.getClientSideMap().get(room.getBlackPlayerId());
+ winnerNickname = winner != null ? winner.getNickname() : "Black";
+ } else if (result == GameResult.WHITE_WIN) {
+ ClientSide winner = room.getClientSideMap().get(room.getWhitePlayerId());
+ winnerNickname = winner != null ? winner.getNickname() : "White";
+ }
+
+ broadcastToRoom(room, Response.newBuilder()
+ .setGameOver(GameOverResponse.newBuilder()
+ .setResult(result.name())
+ .setWinnerNickname(winnerNickname == null ? "" : winnerNickname))
+ .build());
+ }
+
+ private static void broadcastToRoom(Room room, Response response) {
+ for (ClientSide client : room.getClientSideList()) {
+ if (client.getChannel() != null) {
+ ChannelUtils.push(client.getChannel(), response);
+ }
+ }
+ for (ClientSide watcher : room.getWatcherList()) {
+ ChannelUtils.push(watcher.getChannel(), response);
+ }
+ }
+}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/GameReadyHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/GameReadyHandler.java
new file mode 100644
index 0000000..937ddb0
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/GameReadyHandler.java
@@ -0,0 +1,57 @@
+package com.miti99.caro.server.event.handler;
+
+import com.miti99.caro.common.channel.ChannelUtils;
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.common.entity.Room;
+import com.miti99.caro.common.enums.ClientStatus;
+import com.miti99.caro.common.enums.RoomStatus;
+import com.miti99.caro.protocol.GameReadyResponse;
+import com.miti99.caro.protocol.Response;
+import com.miti99.caro.server.ServerContains;
+import com.miti99.caro.server.event.request.GameReadyRequestRecord;
+import com.miti99.caro.server.event.request.GameStartingRequestRecord;
+
+public final class GameReadyHandler {
+
+ private GameReadyHandler() {
+ }
+
+ public static void handle(ClientSide clientSide, GameReadyRequestRecord req) {
+ Room room = ServerContains.getRoom(clientSide.getRoomId());
+ if (room == null || room.getStatus() == RoomStatus.STARTING) {
+ return;
+ }
+ if (clientSide.getStatus() == ClientStatus.PLAYING) {
+ return;
+ }
+
+ // Toggle ready state
+ clientSide.setStatus(clientSide.getStatus() == ClientStatus.READY ? ClientStatus.NO_READY : ClientStatus.READY);
+
+ Response response = Response.newBuilder()
+ .setGameReady(GameReadyResponse.newBuilder()
+ .setClientNickname(clientSide.getNickname() == null ? "" : clientSide.getNickname())
+ .setStatus(clientSide.getStatus().name())
+ .setClientId(clientSide.getId()))
+ .build();
+
+ // Check if all human players are ready (need 2 players)
+ boolean allReady = room.getClientSideMap().size() >= 2;
+ for (ClientSide client : room.getClientSideList()) {
+ if (client.getChannel() != null && client.getStatus() != ClientStatus.READY) {
+ allReady = false;
+ }
+ }
+
+ // Notify all human players
+ for (ClientSide client : room.getClientSideList()) {
+ if (client.getChannel() != null) {
+ ChannelUtils.push(client.getChannel(), response);
+ }
+ }
+
+ if (allReady) {
+ GameStartingHandler.handle(clientSide, new GameStartingRequestRecord());
+ }
+ }
+}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/GameResetHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/GameResetHandler.java
new file mode 100644
index 0000000..124528a
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/GameResetHandler.java
@@ -0,0 +1,19 @@
+package com.miti99.caro.server.event.handler;
+
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.server.event.request.GameResetRequestRecord;
+
+/**
+ * Placeholder for the {@code game_reset} request. The original server never
+ * implemented this listener — see the audit in the migration plan. Left as a
+ * no-op so the sealed dispatch remains exhaustive.
+ */
+public final class GameResetHandler {
+
+ private GameResetHandler() {
+ }
+
+ public static void handle(ClientSide client, GameResetRequestRecord req) {
+ // Not implemented — intentional no-op.
+ }
+}
diff --git a/server/src/main/java/com/miti99/caro/server/event/handler/GameStartingHandler.java b/server/src/main/java/com/miti99/caro/server/event/handler/GameStartingHandler.java
new file mode 100644
index 0000000..8699310
--- /dev/null
+++ b/server/src/main/java/com/miti99/caro/server/event/handler/GameStartingHandler.java
@@ -0,0 +1,70 @@
+package com.miti99.caro.server.event.handler;
+
+import java.util.LinkedList;
+
+import com.miti99.caro.common.channel.ChannelUtils;
+import com.miti99.caro.common.entity.ClientSide;
+import com.miti99.caro.common.entity.Room;
+import com.miti99.caro.common.enums.ClientRole;
+import com.miti99.caro.common.enums.ClientStatus;
+import com.miti99.caro.common.enums.PieceType;
+import com.miti99.caro.common.enums.RoomStatus;
+import com.miti99.caro.protocol.GameStartingResponse;
+import com.miti99.caro.protocol.Response;
+import com.miti99.caro.server.ServerContains;
+import com.miti99.caro.server.event.request.GameStartingRequestRecord;
+
+public final class GameStartingHandler {
+
+ private static final int BOARD_SIZE = 15;
+
+ private GameStartingHandler() {
+ }
+
+ public static void handle(ClientSide clientSide, GameStartingRequestRecord req) {
+ Room room = ServerContains.getRoom(clientSide.getRoomId());
+ if (room == null) {
+ return;
+ }
+
+ LinkedList