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
This commit is contained in:
2026-04-09 17:25:56 +07:00
parent 640fce79e8
commit 13ee8ea7f3
13 changed files with 288 additions and 281 deletions
@@ -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());
}
}
}
@@ -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());
}
}
@@ -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<String, Object> 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);
}
}
}
@@ -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<Integer, ClientSide> roomClientMap = (ConcurrentSkipListMap<Integer, ClientSide>) 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);
}
}
@@ -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<ClientSide> roomClientList = room.getClientSideList();
// Send the points of poker
List<List<Poker>> 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<List<Poker>> 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);
}
}
}
@@ -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);
@@ -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) {
@@ -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));
}
}
@@ -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";
}
}
}
@@ -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<Integer, ClientSide> roomClientMap = (ConcurrentSkipListMap<Integer, ClientSide>) room.getClientSideMap();
LinkedList<ClientSide> 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());
}
@@ -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());
@@ -62,7 +62,7 @@ public class WebsocketTransferHandler extends SimpleChannelInboundHandler<TextWe
// 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());
@@ -3,34 +3,23 @@ package org.nico.ratel.landlords.server.timer;
import java.util.Map;
import java.util.TimerTask;
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;
import org.nico.ratel.landlords.enums.ServerEventCode;
import org.nico.ratel.landlords.print.SimplePrinter;
import org.nico.ratel.landlords.server.ServerContains;
import org.nico.ratel.landlords.server.event.ServerEventListener;
import org.nico.ratel.landlords.server.robot.RobotEventListener;
/**
*
* @author nico
* Periodically cleans up idle or expired rooms.
*/
public class RoomClearTask extends TimerTask {
//The room wait time of after create is 100s
// Room wait time after creation: 100s
private static final long waitingStatusInterval = 1000 * 100;
//The room starting destroy time is 100s
private static final long startingStatusInterval = 1000 * 300;
//The room live time is 600s
// Room live time: 20 minutes
private static final long liveTime = 1000 * 60 * 20;
@Override
@@ -40,7 +29,6 @@ public class RoomClearTask extends TimerTask {
} catch (Exception e) {
SimplePrinter.serverLog(e.getMessage());
}
}
public void doing() {
@@ -51,95 +39,27 @@ public class RoomClearTask extends TimerTask {
long now = System.currentTimeMillis();
for (Room room : rooms.values()) {
long alreadyLiveTime = System.currentTimeMillis() - room.getCreateTime();
SimplePrinter.serverLog("room " + room.getId() + " already live " + alreadyLiveTime + "ms");
if (alreadyLiveTime > 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());
}
}
}