From 13ee8ea7f396c45ccf7d020f2c66719aa19e525b Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Thu, 9 Apr 2026 17:25:56 +0700 Subject: [PATCH] feat(server): rewrite event handlers for Gomoku game flow - Create ServerEventListener_CODE_GAME_MOVE with move validation, win detection, and inline AI response for PVE - Rewrite GAME_STARTING for 2-player Gomoku (black/white assignment) - Rewrite ROOM_JOIN to auto-start at 2 players - Rewrite ROOM_CREATE_PVE for single AI opponent - Simplify RoomClearTask (remove robot substitution logic) - Fix ClientRole.PLAYER references in handlers - Replace Chinese comments with English --- .../ServerEventListener_CODE_CLIENT_EXIT.java | 25 ++- ...rverEventListener_CODE_CLIENT_OFFLINE.java | 10 +- .../ServerEventListener_CODE_GAME_MOVE.java | 143 ++++++++++++++++++ .../ServerEventListener_CODE_GAME_READY.java | 40 +++-- ...erverEventListener_CODE_GAME_STARTING.java | 111 +++++--------- .../ServerEventListener_CODE_GAME_WATCH.java | 2 +- ...verEventListener_CODE_GAME_WATCH_EXIT.java | 2 +- .../ServerEventListener_CODE_ROOM_CREATE.java | 8 +- ...verEventListener_CODE_ROOM_CREATE_PVE.java | 63 ++++---- .../ServerEventListener_CODE_ROOM_JOIN.java | 45 ++---- .../handler/ProtobufTransferHandler.java | 2 +- .../handler/WebsocketTransferHandler.java | 2 +- .../landlords/server/timer/RoomClearTask.java | 116 +++----------- 13 files changed, 288 insertions(+), 281 deletions(-) create mode 100644 landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_MOVE.java diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_EXIT.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_EXIT.java index 71f25d1..a55cd9e 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_EXIT.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_EXIT.java @@ -4,7 +4,6 @@ import org.nico.ratel.landlords.channel.ChannelUtils; import org.nico.ratel.landlords.entity.ClientSide; import org.nico.ratel.landlords.entity.Room; import org.nico.ratel.landlords.enums.ClientEventCode; -import org.nico.ratel.landlords.enums.ClientRole; import org.nico.ratel.landlords.helper.MapHelper; import org.nico.ratel.landlords.server.ServerContains; @@ -14,37 +13,31 @@ public class ServerEventListener_CODE_CLIENT_EXIT implements ServerEventListener @Override public void call(ClientSide clientSide, String data) { - synchronized (locked){ + synchronized (locked) { Room room = ServerContains.getRoom(clientSide.getRoomId()); if (room == null) { return; } + String result = MapHelper.newInstance() .put("roomId", room.getId()) .put("exitClientId", clientSide.getId()) .put("exitClientNickname", clientSide.getNickname()) .json(); + for (ClientSide client : room.getClientSideList()) { - if (client.getRole() == ClientRole.PLAYER) { + if (client.getChannel() != null) { ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_CLIENT_EXIT, result); client.init(); } } - notifyWatcherClientExit(room, clientSide); + // Notify spectators + for (ClientSide watcher : room.getWatcherList()) { + ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_CLIENT_EXIT, clientSide.getNickname()); + } + ServerContains.removeRoom(room.getId()); } } - - /** - * 通知所有观战者玩家退出游戏 - * - * @param room 房间 - * @param player 退出游戏玩家 - */ - private void notifyWatcherClientExit(Room room, ClientSide player) { - for (ClientSide watcher : room.getWatcherList()) { - ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_CLIENT_EXIT, player.getNickname()); - } - } } diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_OFFLINE.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_OFFLINE.java index 3dcc64c..5dd2b86 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_OFFLINE.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_CLIENT_OFFLINE.java @@ -4,7 +4,6 @@ import org.nico.ratel.landlords.channel.ChannelUtils; import org.nico.ratel.landlords.entity.ClientSide; import org.nico.ratel.landlords.entity.Room; import org.nico.ratel.landlords.enums.ClientEventCode; -import org.nico.ratel.landlords.enums.ClientRole; import org.nico.ratel.landlords.helper.MapHelper; import org.nico.ratel.landlords.server.ServerContains; @@ -12,7 +11,6 @@ public class ServerEventListener_CODE_CLIENT_OFFLINE implements ServerEventListe @Override public void call(ClientSide clientSide, String data) { - Room room = ServerContains.getRoom(clientSide.getRoomId()); if (room == null) { @@ -21,6 +19,7 @@ public class ServerEventListener_CODE_CLIENT_OFFLINE implements ServerEventListe } if (room.getWatcherList().contains(clientSide)) { + room.getWatcherList().remove(clientSide); return; } @@ -29,15 +28,14 @@ public class ServerEventListener_CODE_CLIENT_OFFLINE implements ServerEventListe .put("exitClientId", clientSide.getId()) .put("exitClientNickname", clientSide.getNickname()) .json(); + for (ClientSide client : room.getClientSideList()) { - if (client.getRole() != ClientRole.PLAYER) { - continue; - } - if (client.getId() != clientSide.getId()) { + if (client.getChannel() != null && client.getId() != clientSide.getId()) { ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_CLIENT_EXIT, result); client.init(); } } + ServerContains.removeRoom(room.getId()); } } diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_MOVE.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_MOVE.java new file mode 100644 index 0000000..e905fcd --- /dev/null +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_MOVE.java @@ -0,0 +1,143 @@ +package org.nico.ratel.landlords.server.event; + +import org.nico.ratel.landlords.channel.ChannelUtils; +import org.nico.ratel.landlords.entity.ClientSide; +import org.nico.ratel.landlords.entity.Board; +import org.nico.ratel.landlords.entity.GameMove; +import org.nico.ratel.landlords.entity.Room; +import org.nico.ratel.landlords.enums.ClientEventCode; +import org.nico.ratel.landlords.enums.GameResult; +import org.nico.ratel.landlords.enums.PieceType; +import org.nico.ratel.landlords.enums.RoomType; +import org.nico.ratel.landlords.helper.GomokuHelper; +import org.nico.ratel.landlords.helper.MapHelper; +import org.nico.ratel.landlords.robot.GomokuAI; +import org.nico.ratel.landlords.server.ServerContains; + +import java.util.Map; + +public class ServerEventListener_CODE_GAME_MOVE implements ServerEventListener { + + @Override + public void call(ClientSide clientSide, String data) { + Room room = ServerContains.getRoom(clientSide.getRoomId()); + if (room == null) { + ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_PLAY_FAIL_BY_INEXIST, null); + return; + } + + Map map = MapHelper.parser(data); + int row = (int) map.get("row"); + int col = (int) map.get("col"); + + // Check turn + if (!room.isPlayerTurn(clientSide.getId())) { + ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_MOVE_NOT_YOUR_TURN, null); + return; + } + + Board board = room.getGameBoard(); + + // Check bounds + if (row < 0 || row >= Board.BOARD_SIZE || col < 0 || col >= Board.BOARD_SIZE) { + ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_MOVE_OUT_OF_BOUNDS, null); + return; + } + + // Check occupied + if (board.getPiece(row, col) != PieceType.EMPTY) { + ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_MOVE_OCCUPIED, null); + return; + } + + // Make the move + GameResult result = GomokuHelper.makeMove(room, row, col, clientSide.getId()); + + // Broadcast move to all players and spectators + String moveResult = MapHelper.newInstance() + .put("row", row) + .put("col", col) + .put("piece", room.getPlayerPiece(clientSide.getId()).name()) + .put("playerNickname", clientSide.getNickname()) + .put("playerId", clientSide.getId()) + .json(); + broadcastToRoom(room, ClientEventCode.CODE_GAME_MOVE_SUCCESS, moveResult); + + // Check game over + if (result != GameResult.IN_PROGRESS) { + handleGameOver(room, result); + return; + } + + // PVE: trigger AI move if next turn is AI + if (room.getType() == RoomType.PVE) { + handleAIMove(room); + } + } + + private void handleAIMove(Room room) { + // Find AI player (the one with null channel) + 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()); + + String moveResult = MapHelper.newInstance() + .put("row", aiMove.getRow()) + .put("col", aiMove.getCol()) + .put("piece", aiPiece.name()) + .put("playerNickname", aiPlayer.getNickname()) + .put("playerId", aiPlayer.getId()) + .json(); + broadcastToRoom(room, ClientEventCode.CODE_GAME_MOVE_SUCCESS, moveResult); + + if (result != GameResult.IN_PROGRESS) { + handleGameOver(room, result); + } + } + + private 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"; + } + + String gameOverData = MapHelper.newInstance() + .put("result", result.name()) + .put("winnerNickname", winnerNickname) + .put("board", GomokuHelper.formatBoardForDisplay(room.getGameBoard())) + .json(); + broadcastToRoom(room, ClientEventCode.CODE_GAME_OVER, gameOverData); + } + + private void broadcastToRoom(Room room, ClientEventCode code, String data) { + for (ClientSide client : room.getClientSideList()) { + if (client.getChannel() != null) { + ChannelUtils.pushToClient(client.getChannel(), code, data); + } + } + for (ClientSide watcher : room.getWatcherList()) { + ChannelUtils.pushToClient(watcher.getChannel(), code, data); + } + } +} diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_READY.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_READY.java index 0752045..ae83f6f 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_READY.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_READY.java @@ -3,49 +3,45 @@ package org.nico.ratel.landlords.server.event; import org.nico.ratel.landlords.channel.ChannelUtils; import org.nico.ratel.landlords.entity.ClientSide; import org.nico.ratel.landlords.entity.Room; -import org.nico.ratel.landlords.enums.*; +import org.nico.ratel.landlords.enums.ClientEventCode; +import org.nico.ratel.landlords.enums.ClientStatus; +import org.nico.ratel.landlords.enums.RoomStatus; +import org.nico.ratel.landlords.enums.ServerEventCode; import org.nico.ratel.landlords.helper.MapHelper; -import org.nico.ratel.landlords.print.SimplePrinter; import org.nico.ratel.landlords.server.ServerContains; -import java.util.concurrent.ConcurrentSkipListMap; - public class ServerEventListener_CODE_GAME_READY implements ServerEventListener { + @Override public void call(ClientSide clientSide, String data) { Room room = ServerContains.getRoom(clientSide.getRoomId()); - if (room == null) { + if (room == null || room.getStatus() == RoomStatus.STARTING) { return; } - SimplePrinter.serverLog("房间状态:" + room.getStatus()); - SimplePrinter.serverLog("玩家状态:" + clientSide.getStatus()); - if (room.getStatus() == RoomStatus.STARTING) { - return; - } - if (clientSide.getStatus() == ClientStatus.PLAYING || clientSide.getStatus() == ClientStatus.TO_CHOOSE || clientSide.getStatus() == ClientStatus.CALL_LANDLORD) { + if (clientSide.getStatus() == ClientStatus.PLAYING) { return; } + + // Toggle ready state clientSide.setStatus(clientSide.getStatus() == ClientStatus.READY ? ClientStatus.NO_READY : ClientStatus.READY); + String result = MapHelper.newInstance() .put("clientNickName", clientSide.getNickname()) .put("status", clientSide.getStatus()) .put("clientId", clientSide.getId()) .json(); - boolean allReady = true; - ConcurrentSkipListMap roomClientMap = (ConcurrentSkipListMap) room.getClientSideMap(); - if (roomClientMap.size() < 3) { - allReady = false; - } else { - for (ClientSide client : room.getClientSideList()) { - if (client.getRole() == ClientRole.PLAYER && client.getStatus() != ClientStatus.READY) { - allReady = false; - break; - } + + // 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.getRole() == ClientRole.PLAYER) { + if (client.getChannel() != null) { ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_GAME_READY, result); } } diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_STARTING.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_STARTING.java index aa146b9..4d1b93d 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_STARTING.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_STARTING.java @@ -1,101 +1,68 @@ package org.nico.ratel.landlords.server.event; -import java.util.ArrayList; import java.util.LinkedList; -import java.util.List; import org.nico.ratel.landlords.channel.ChannelUtils; import org.nico.ratel.landlords.entity.ClientSide; -import org.nico.ratel.landlords.entity.Poker; import org.nico.ratel.landlords.entity.Room; -import org.nico.ratel.landlords.enums.*; +import org.nico.ratel.landlords.enums.ClientEventCode; +import org.nico.ratel.landlords.enums.ClientRole; +import org.nico.ratel.landlords.enums.ClientStatus; +import org.nico.ratel.landlords.enums.PieceType; +import org.nico.ratel.landlords.enums.RoomStatus; import org.nico.ratel.landlords.helper.MapHelper; -import org.nico.ratel.landlords.helper.PokerHelper; import org.nico.ratel.landlords.server.ServerContains; -import org.nico.ratel.landlords.server.robot.RobotEventListener; -import org.nico.ratel.landlords.utils.JsonUtils; -import org.nico.ratel.landlords.utils.LastCardsUtils; public class ServerEventListener_CODE_GAME_STARTING implements ServerEventListener { @Override public void call(ClientSide clientSide, String data) { - Room room = ServerContains.getRoom(clientSide.getRoomId()); + if (room == null) { + return; + } LinkedList roomClientList = room.getClientSideList(); - - // Send the points of poker - List> pokersList = PokerHelper.distributePoker(); - int cursor = 0; - for (ClientSide client : roomClientList) { - client.setPokers(pokersList.get(cursor++)); + if (roomClientList.size() < 2) { + return; } - room.setLandlordPokers(pokersList.get(3)); - // Push information about the robber - int startGrabIndex = (int) (Math.random() * 3); - ClientSide startGrabClient = roomClientList.get(startGrabIndex); - room.setCurrentSellClient(startGrabClient.getId()); + // Assign players: first = BLACK, second = WHITE + ClientSide blackPlayer = roomClientList.get(0); + ClientSide whitePlayer = roomClientList.get(1); - // Push start game messages + blackPlayer.setRole(ClientRole.BLACK_PLAYER); + whitePlayer.setRole(ClientRole.WHITE_PLAYER); + blackPlayer.setStatus(ClientStatus.PLAYING); + whitePlayer.setStatus(ClientStatus.PLAYING); + + room.setBlackPlayerId(blackPlayer.getId()); + room.setWhitePlayerId(whitePlayer.getId()); + room.setCurrentTurn(PieceType.BLACK); room.setStatus(RoomStatus.STARTING); room.setCreateTime(System.currentTimeMillis()); room.setLastFlushTime(System.currentTimeMillis()); + room.resetGame(); - // Record the first speaker - room.setFirstSellClient(startGrabClient.getId()); - List> otherPokers = new ArrayList<>(); - for (ClientSide client : roomClientList) { - client.setType(ClientType.PEASANT); - client.setStatus(ClientStatus.PLAYING); - for(ClientSide otherClient : roomClientList){ - if(otherClient.getId() != client.getId()){ - otherPokers.add(otherClient.getPokers()); - } - } - otherPokers.add(room.getLandlordPokers()); - String lastCards = LastCardsUtils.getLastCards(otherPokers); - otherPokers = new ArrayList<>(); - String result = MapHelper.newInstance() - .put("roomId", room.getId()) - .put("roomOwner", room.getRoomOwner()) - .put("roomClientCount", room.getClientSideList().size()) - .put("nextClientNickname", startGrabClient.getNickname()) - .put("nextClientId", startGrabClient.getId()) - .put("pokers", client.getPokers()) - .put("lastPokers",lastCards) - .put("highestScore", 0) - .json(); - - if (client.getRole() == ClientRole.PLAYER) { - ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_GAME_STARTING, result); - } else { - if (startGrabClient.getId() == client.getId()) { - RobotEventListener.get(ClientEventCode.CODE_GAME_LANDLORD_ELECT).call(client, result); - } - } - - } - - notifyWatcherGameStart(room); - } - - - /** - * 通知房间内的观战人员游戏开始 - * - * @param room 房间 - */ - private void notifyWatcherGameStart(Room room) { String result = MapHelper.newInstance() - .put("player1", room.getClientSideList().getFirst().getNickname()) - .put("player2", room.getClientSideList().getFirst().getNext().getNickname()) - .put("player3", room.getClientSideList().getLast().getNickname()) + .put("roomId", room.getId()) + .put("blackPlayerId", blackPlayer.getId()) + .put("blackPlayerNickname", blackPlayer.getNickname()) + .put("whitePlayerId", whitePlayer.getId()) + .put("whitePlayerNickname", whitePlayer.getNickname()) + .put("boardSize", 15) .json(); - for (ClientSide clientSide : room.getWatcherList()) { - ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_STARTING, result); + + // Notify human players + for (ClientSide client : roomClientList) { + if (client.getChannel() != null) { + ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_GAME_STARTING, result); + } + } + + // Notify spectators + for (ClientSide watcher : room.getWatcherList()) { + ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_GAME_STARTING, result); } } - } diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH.java index 532498f..39b84dd 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH.java @@ -24,7 +24,7 @@ public class ServerEventListener_CODE_GAME_WATCH implements ServerEventListener ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_JOIN_FAIL_BY_INEXIST, result); } else { - // 将用户加入到房间中的观战者列表中 + // Add user to the room's spectator list clientSide.setRoomId(room.getId()); room.getWatcherList().add(clientSide); diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH_EXIT.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH_EXIT.java index 1484f29..a9f6d7f 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH_EXIT.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_GAME_WATCH_EXIT.java @@ -12,7 +12,7 @@ public class ServerEventListener_CODE_GAME_WATCH_EXIT implements ServerEventList Room room = ServerContains.getRoom(clientSide.getRoomId()); if (room != null) { - // 房间如果存在,则将观战者从房间观战列表中移除 + // Remove spectator from room's watcher list if room exists clientSide.setRoomId(room.getId()); boolean successful = room.getWatcherList().remove(clientSide); if (successful) { diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE.java index 3b3ff0a..dde24e7 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE.java @@ -5,6 +5,7 @@ import org.nico.ratel.landlords.channel.ChannelUtils; import org.nico.ratel.landlords.entity.ClientSide; import org.nico.ratel.landlords.entity.Room; import org.nico.ratel.landlords.enums.ClientEventCode; +import org.nico.ratel.landlords.enums.ClientRole; import org.nico.ratel.landlords.enums.ClientStatus; import org.nico.ratel.landlords.enums.RoomStatus; import org.nico.ratel.landlords.enums.RoomType; @@ -14,22 +15,21 @@ public class ServerEventListener_CODE_ROOM_CREATE implements ServerEventListener @Override public void call(ClientSide clientSide, String data) { - 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.setCurrentSellClient(clientSide.getId()); room.setCreateTime(System.currentTimeMillis()); room.setLastFlushTime(System.currentTimeMillis()); clientSide.setRoomId(room.getId()); - ServerContains.addRoom(room); - + clientSide.setRole(ClientRole.BLACK_PLAYER); clientSide.setStatus(ClientStatus.NO_READY); + ServerContains.addRoom(room); + ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_CREATE_SUCCESS, Noson.reversal(room)); } } diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE_PVE.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE_PVE.java index 586c589..7e7618f 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE_PVE.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_CREATE_PVE.java @@ -3,54 +3,59 @@ package org.nico.ratel.landlords.server.event; import org.nico.ratel.landlords.channel.ChannelUtils; import org.nico.ratel.landlords.entity.ClientSide; import org.nico.ratel.landlords.entity.Room; -import org.nico.ratel.landlords.enums.*; -import org.nico.ratel.landlords.robot.RobotDecisionMakers; +import org.nico.ratel.landlords.enums.ClientEventCode; +import org.nico.ratel.landlords.enums.ClientRole; +import org.nico.ratel.landlords.enums.ClientStatus; +import org.nico.ratel.landlords.enums.RoomStatus; +import org.nico.ratel.landlords.enums.RoomType; +import org.nico.ratel.landlords.enums.ServerEventCode; import org.nico.ratel.landlords.server.ServerContains; public class ServerEventListener_CODE_ROOM_CREATE_PVE implements ServerEventListener { @Override public void call(ClientSide clientSide, String data) { - - int difficultyCoefficient = Integer.parseInt(data); - if (!RobotDecisionMakers.contains(difficultyCoefficient)) { + int difficulty = Integer.parseInt(data); + if (difficulty < 1 || difficulty > 3) { ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_PVE_DIFFICULTY_NOT_SUPPORT, null); return; } Room room = new Room(ServerContains.getServerId()); room.setType(RoomType.PVE); - room.setStatus(RoomStatus.BLANK); + 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); - room.setCurrentSellClient(clientSide.getId()); - room.setCreateTime(System.currentTimeMillis()); - room.setDifficultyCoefficient(difficultyCoefficient); - 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); - ClientSide preClient = clientSide; - //Add robots - for (int index = 1; index < 3; index++) { - ClientSide robot = new ClientSide(-ServerContains.getClientId(), ClientStatus.PLAYING, null); - robot.setNickname("robot_" + index); - robot.setRole(ClientRole.ROBOT); - preClient.setNext(robot); - robot.setPre(preClient); - robot.setRoomId(room.getId()); - room.getClientSideMap().put(robot.getId(), robot); - room.getClientSideList().add(robot); - - preClient = robot; - ServerContains.CLIENT_SIDE_MAP.put(robot.getId(), robot); - } - preClient.setNext(clientSide); - clientSide.setPre(preClient); - + // Auto-start game ServerEventListener.get(ServerEventCode.CODE_GAME_STARTING).call(clientSide, String.valueOf(room.getId())); } - + private String getDifficultyName(int difficulty) { + switch (difficulty) { + case 1: return "Easy"; + case 2: return "Medium"; + case 3: return "Hard"; + default: return "Easy"; + } + } } diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_JOIN.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_JOIN.java index c01d7c4..f5b5b74 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_JOIN.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/event/ServerEventListener_CODE_ROOM_JOIN.java @@ -1,7 +1,6 @@ package org.nico.ratel.landlords.server.event; import java.util.LinkedList; -import java.util.concurrent.ConcurrentSkipListMap; import org.nico.ratel.landlords.channel.ChannelUtils; import org.nico.ratel.landlords.entity.ClientSide; @@ -17,7 +16,6 @@ public class ServerEventListener_CODE_ROOM_JOIN implements ServerEventListener { @Override public void call(ClientSide clientSide, String data) { - Room room = ServerContains.getRoom(Integer.parseInt(data)); if (room == null) { @@ -27,7 +25,9 @@ public class ServerEventListener_CODE_ROOM_JOIN implements ServerEventListener { ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_JOIN_FAIL_BY_INEXIST, result); return; } - if (room.getClientSideList().size() == 3) { + + // Gomoku is 2-player + if (room.getClientSideList().size() >= 2) { String result = MapHelper.newInstance() .put("roomId", room.getId()) .put("roomOwner", room.getRoomOwner()) @@ -35,51 +35,36 @@ public class ServerEventListener_CODE_ROOM_JOIN implements ServerEventListener { ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_JOIN_FAIL_BY_FULL, result); return; } - // join default ready + clientSide.setStatus(ClientStatus.READY); clientSide.setRoomId(room.getId()); - ConcurrentSkipListMap roomClientMap = (ConcurrentSkipListMap) room.getClientSideMap(); LinkedList roomClientList = room.getClientSideList(); - - if (roomClientList.size() > 0) { - ClientSide pre = roomClientList.getLast(); - pre.setNext(clientSide); - clientSide.setPre(pre); - } - roomClientList.add(clientSide); - roomClientMap.put(clientSide.getId(), clientSide); + room.getClientSideMap().put(clientSide.getId(), clientSide); room.setStatus(RoomStatus.WAIT); + String result = MapHelper.newInstance() .put("clientId", clientSide.getId()) .put("clientNickname", clientSide.getNickname()) .put("roomId", room.getId()) .put("roomOwner", room.getRoomOwner()) - .put("roomClientCount", room.getClientSideList().size()) + .put("roomClientCount", roomClientList.size()) .json(); - for (ClientSide client : roomClientMap.values()) { - ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_ROOM_JOIN_SUCCESS, result); + + for (ClientSide client : room.getClientSideMap().values()) { + if (client.getChannel() != null) { + ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_ROOM_JOIN_SUCCESS, result); + } } - if (roomClientMap.size() == 3) { - clientSide.setNext(roomClientList.getFirst()); - roomClientList.getFirst().setPre(clientSide); - + // Auto-start when 2 players joined + if (roomClientList.size() == 2) { ServerEventListener.get(ServerEventCode.CODE_GAME_STARTING).call(clientSide, String.valueOf(room.getId())); return; } - notifyWatcherJoinRoom(room, clientSide); - } - - /** - * 通知观战者玩家加入房间 - * - * @param room 房间 - * @param clientSide 玩家 - */ - private void notifyWatcherJoinRoom(Room room, ClientSide clientSide) { + // Notify spectators for (ClientSide watcher : room.getWatcherList()) { ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_ROOM_JOIN_SUCCESS, clientSide.getNickname()); } diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/ProtobufTransferHandler.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/ProtobufTransferHandler.java index 294630e..6cda7d4 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/ProtobufTransferHandler.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/ProtobufTransferHandler.java @@ -34,7 +34,7 @@ public class ProtobufTransferHandler extends ChannelInboundHandlerAdapter { //init client info ClientSide clientSide = new ClientSide(getId(ctx.channel()), ClientStatus.TO_CHOOSE, ch); clientSide.setNickname(String.valueOf(clientSide.getId())); - clientSide.setRole(ClientRole.PLAYER); + clientSide.setRole(ClientRole.BLACK_PLAYER); ServerContains.CLIENT_SIDE_MAP.put(clientSide.getId(), clientSide); SimplePrinter.serverLog("Has client connect to the server: " + clientSide.getId()); diff --git a/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/WebsocketTransferHandler.java b/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/WebsocketTransferHandler.java index 631d714..3be7ea4 100644 --- a/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/WebsocketTransferHandler.java +++ b/landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/WebsocketTransferHandler.java @@ -62,7 +62,7 @@ public class WebsocketTransferHandler extends SimpleChannelInboundHandler liveTime) { - SimplePrinter.serverLog("room " + room.getId() + " live time overflow " + liveTime + ", closed!"); - ServerEventListener.get(ServerEventCode.CODE_CLIENT_EXIT).call(room.getClientSideList().get(0), null); - continue; - } + long alreadyLiveTime = now - room.getCreateTime(); + if (alreadyLiveTime > liveTime) { + SimplePrinter.serverLog("room " + room.getId() + " live time overflow, closed!"); + closeRoom(room); + continue; + } long diff = now - room.getLastFlushTime(); - if (room.getStatus() != RoomStatus.STARTING && diff > waitingStatusInterval) { - SimplePrinter.serverLog("room " + room.getId() + " starting waiting time overflow " + waitingStatusInterval + ", closed!"); - ServerEventListener.get(ServerEventCode.CODE_CLIENT_EXIT).call(room.getClientSideList().get(0), null); - continue; - } - if (room.getType() != RoomType.PVP) { - continue; + if (room.getStatus() != RoomStatus.STARTING && diff > waitingStatusInterval) { + SimplePrinter.serverLog("room " + room.getId() + " waiting time overflow, closed!"); + closeRoom(room); } - - if (diff <= startingStatusInterval) { - continue; - } - - boolean allRobots = true; - for (ClientSide client : room.getClientSideList()) { - if (client.getId() != room.getCurrentSellClient() && client.getRole() == ClientRole.PLAYER) { - allRobots = false; - break; - } - } - - ClientSide currentPlayer = room.getClientSideMap().get(room.getCurrentSellClient()); - - if (allRobots) { - SimplePrinter.serverLog("room " + room.getId() + " all is robots, closed!"); - ServerEventListener.get(ServerEventCode.CODE_CLIENT_EXIT).call(currentPlayer, null); - continue; - } - // Kick this client - ChannelUtils.pushToClient(currentPlayer.getChannel(), ClientEventCode.CODE_CLIENT_KICK, null); - - notifyWatcherClientKick(room, currentPlayer); - - // Remove current player - room.getClientSideMap().remove(currentPlayer.getId()); - room.getClientSideList().remove(currentPlayer); - - ClientSide robot = new ClientSide(-ServerContains.getClientId(), ClientStatus.PLAYING, null); - robot.setNickname(currentPlayer.getNickname()); - robot.setRole(ClientRole.ROBOT); - robot.setRoomId(room.getId()); - robot.setNext(currentPlayer.getNext()); - robot.setPre(currentPlayer.getPre()); - robot.getNext().setPre(robot); - robot.getPre().setNext(robot); - robot.setPokers(currentPlayer.getPokers()); - robot.setType(currentPlayer.getType()); - - room.getClientSideMap().put(robot.getId(), robot); - room.getClientSideList().add(robot); - room.setCurrentSellClient(robot.getId()); - - // If last sell client is current client, replace it to robot id - if (room.getLastSellClient() == currentPlayer.getId()) { - room.setLastSellClient(robot.getId()); - } - - // Set robot difficulty -> simple - room.setDifficultyCoefficient(1); - - ServerContains.CLIENT_SIDE_MAP.put(robot.getId(), robot); - - // Initialize client - currentPlayer.init(); - - SimplePrinter.serverLog("room " + room.getId() + " player " + currentPlayer.getNickname() + " " + startingStatusInterval + "ms not operating, automatic custody!"); - - RobotEventListener.get(room.getLandlordId() == -1 ? ClientEventCode.CODE_GAME_LANDLORD_ELECT : ClientEventCode.CODE_GAME_POKER_PLAY).call(robot, null); } } - /** - * Notify spectators that a player has been kicked from the room - * - * @param room room - * @param player player who was kicked - */ - private void notifyWatcherClientKick(Room room, ClientSide player) { - for (ClientSide watcher : room.getWatcherList()) { - ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_CLIENT_KICK, player.getNickname()); + private void closeRoom(Room room) { + if (!room.getClientSideList().isEmpty()) { + ClientSide first = room.getClientSideList().get(0); + ServerEventListener.get(ServerEventCode.CODE_CLIENT_EXIT).call(first, null); + } else { + ServerContains.removeRoom(room.getId()); } } }