parent
8e888698e6
commit
79637a3c82
|
|
@ -34,6 +34,9 @@ public class Config {
|
||||||
|
|
||||||
//==================== 游戏服务器配置 ====================
|
//==================== 游戏服务器配置 ====================
|
||||||
/** 游戏服务器主机地址 */
|
/** 游戏服务器主机地址 */
|
||||||
|
/*public static final String GAME_SERVER_HOST = "8.134.76.43";
|
||||||
|
public static final String DEFAULT_GROUP_ID = "762479";*/
|
||||||
|
public static final String DEFAULT_GROUP_ID = "426149";
|
||||||
public static final String GAME_SERVER_HOST = "127.0.0.1";
|
public static final String GAME_SERVER_HOST = "127.0.0.1";
|
||||||
|
|
||||||
/** 游戏服务器端口 */
|
/** 游戏服务器端口 */
|
||||||
|
|
@ -45,7 +48,5 @@ public class Config {
|
||||||
/** 默认PID */
|
/** 默认PID */
|
||||||
public static final String DEFAULT_PID = "22";
|
public static final String DEFAULT_PID = "22";
|
||||||
|
|
||||||
/** 默认群组ID */
|
|
||||||
public static final String DEFAULT_GROUP_ID = "330800";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,9 +7,8 @@ import com.taurus.core.entity.ITObject;
|
||||||
import com.taurus.core.entity.TObject;
|
import com.taurus.core.entity.TObject;
|
||||||
import com.taurus.core.plugin.redis.Redis;
|
import com.taurus.core.plugin.redis.Redis;
|
||||||
import com.taurus.core.routes.ActionKey;
|
import com.taurus.core.routes.ActionKey;
|
||||||
|
import com.taurus.core.util.Logger;
|
||||||
import com.taurus.permanent.data.Session;
|
import com.taurus.permanent.data.Session;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import redis.clients.jedis.Jedis;
|
import redis.clients.jedis.Jedis;
|
||||||
import robot.mj.info.RobotUser;
|
import robot.mj.info.RobotUser;
|
||||||
import robot.mj.thread.ThreadPoolConfig;
|
import robot.mj.thread.ThreadPoolConfig;
|
||||||
|
|
@ -31,7 +30,7 @@ import static robot.mj.thread.ThreadPoolConfig.scheduleDelay;
|
||||||
* 红中麻将游戏控制器 - 处理游戏协议
|
* 红中麻将游戏控制器 - 处理游戏协议
|
||||||
*/
|
*/
|
||||||
public class EXGameController extends GameController {
|
public class EXGameController extends GameController {
|
||||||
private static final Logger log = LoggerFactory.getLogger(EXGameController.class);
|
private static final Logger log = Logger.getLogger(EXGameController.class);
|
||||||
|
|
||||||
private static final RobotConnectionManager robotConnectionManager = new RobotConnectionManager();
|
private static final RobotConnectionManager robotConnectionManager = new RobotConnectionManager();
|
||||||
|
|
||||||
|
|
@ -239,27 +238,25 @@ public class EXGameController extends GameController {
|
||||||
String roomId = params.getString("roomid");
|
String roomId = params.getString("roomid");
|
||||||
int groupId = params.getInt("groupid");
|
int groupId = params.getInt("groupid");
|
||||||
|
|
||||||
//检查机器人是否已经在房间中
|
String lockKey = "room_lock:" + roomId;
|
||||||
RobotUser existingUser = getRobotRoomInfo(String.valueOf(robotId));
|
synchronized (lockKey.intern()) {
|
||||||
if (existingUser != null && existingUser.getCurrentRoomId() > 0) {
|
if (checkRobotInRoomRedis(roomId, String.valueOf(robotId))) {
|
||||||
String existingConnecId = existingUser.getCurrentRoomId() + "_" + robotId;
|
log.info("机器人{"+robotId+"}已在房间{"+roomId+"}中(Redis 中存在),直接允许加入");
|
||||||
log.warn("机器人{}已在房间{}中,connecId: {}", robotId, existingUser.getCurrentRoomId(), existingConnecId);
|
|
||||||
|
|
||||||
//检查现有连接状态
|
|
||||||
TaurusClient existingClient = existingUser.getClient();
|
|
||||||
if (existingClient == null || !existingClient.isConnected()) {
|
|
||||||
log.warn("现有连接不健康,准备清理并重新加入");
|
|
||||||
robotConnectionManager.disconnectFromGameServer(existingConnecId);
|
|
||||||
} else {
|
} else {
|
||||||
log.info("现有连接健康,跳过重复加入");
|
RobotUser existingRobotUser = getRobotRoomInfo(String.valueOf(robotId));
|
||||||
//返回成功响应
|
if (existingRobotUser != null && existingRobotUser.getCurrentRoomId() == Integer.parseInt(roomId)) {
|
||||||
ITObject response = TObject.newInstance();
|
log.info("机器人{"+robotId+"}已在房间{"+roomId+"}中(本地映射存在),直接允许加入");
|
||||||
response.putString("status", "success");
|
} else {
|
||||||
response.putString("message", "机器人已在房间中");
|
if (isPlayerIdConflictInRoom(roomId, robotId)) {
|
||||||
MainServer.instance.sendResponse(gid, 0, response, session);
|
log.warn("检测到机器人{"+robotId+"}与房间{"+roomId+"}中的真人玩家 ID 冲突,拒绝加入");
|
||||||
|
ITObject errorResponse = TObject.newInstance();
|
||||||
|
errorResponse.putString("status", "failed");
|
||||||
|
errorResponse.putString("message", "机器人 ID 与房间内玩家冲突");
|
||||||
|
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//检查Redis中该房间是否真的包含当前机器人
|
//检查Redis中该房间是否真的包含当前机器人
|
||||||
if (!checkRobotInRoomRedis(roomId, String.valueOf(robotId))) {
|
if (!checkRobotInRoomRedis(roomId, String.valueOf(robotId))) {
|
||||||
|
|
@ -268,7 +265,7 @@ public class EXGameController extends GameController {
|
||||||
if (!robotUsers.isEmpty()) {
|
if (!robotUsers.isEmpty()) {
|
||||||
synchronized (robotUsers) {
|
synchronized (robotUsers) {
|
||||||
RobotUser robotUser = robotUsers.get(0);
|
RobotUser robotUser = robotUsers.get(0);
|
||||||
log.error("房间{}中Redis未找到机器人{},但本地映射存在{}", roomId, robotId, robotId);
|
log.warn("房间{"+roomId+"}中Redis未找到机器人{"+robotId+"},但本地映射存在{"+robotUser.getRobotId()+"},清理本地映射");
|
||||||
robotRoomMapping.remove(robotUser.getConnecId());
|
robotRoomMapping.remove(robotUser.getConnecId());
|
||||||
robotRoomMapping.remove(robotUser.getRobotId());
|
robotRoomMapping.remove(robotUser.getRobotId());
|
||||||
}
|
}
|
||||||
|
|
@ -283,16 +280,17 @@ public class EXGameController extends GameController {
|
||||||
|
|
||||||
if (robotId != existingRobotId) {
|
if (robotId != existingRobotId) {
|
||||||
//不同机器人的冲突
|
//不同机器人的冲突
|
||||||
log.error("房间{}中Redis已存在机器人{},当前机器人{}不执行加入逻辑", roomId, existingRobotId, robotId);
|
log.warn("房间{"+roomId+"}中Redis已存在机器人{"+existingRobotId+"},当前机器人{"+robotId+"}不执行加入逻辑");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info("225开始进房间: room:{} robot:{}", roomId, robotId);
|
log.info("225 开始进房间:room:{"+roomId+"} robot:{"+robotId+"}");
|
||||||
//加入房间
|
//加入房间
|
||||||
joinRoomCommon(robotId, roomId, groupId, params);
|
joinRoomCommon(robotId, roomId, groupId, params);
|
||||||
log.info("225已进入房间准备成功: room:{} robot:{}", roomId, robotId);
|
log.info("225 已进入房间准备成功:room:{"+roomId+"} robot:{"+robotId+"}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -302,10 +300,10 @@ public class EXGameController extends GameController {
|
||||||
public void webGroupActive(Session session, ITObject params, int gid) {
|
public void webGroupActive(Session session, ITObject params, int gid) {
|
||||||
int robotId = params.getInt("robotid");
|
int robotId = params.getInt("robotid");
|
||||||
String roomId = params.getString("roomid");
|
String roomId = params.getString("roomid");
|
||||||
log.info("226开始进房间 room: {} robot: {}", roomId, robotId);
|
log.info("226开始进房间 room: {"+roomId+"} robot:{"+robotId+"}");
|
||||||
//加入房间
|
//加入房间
|
||||||
joinRoomCommon(params.getInt("robotid"), params.getString("roomid"), params.getInt("groupid"), params);
|
joinRoomCommon(params.getInt("robotid"), params.getString("roomid"), params.getInt("groupid"), params);
|
||||||
log.info("226已进入房间准备成功: room:{} robot:{}", roomId, robotId);
|
log.info("226已进入房间准备成功 room: {"+roomId+"} robot:{"+robotId+"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -332,12 +330,12 @@ public class EXGameController extends GameController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("重启后开始进房间: room:{}robot:{}", robotUser.getCurrentRoomId(), robotUser.getRobotId());
|
log.info("重启后开始进房间: room:{"+robotUser.getCurrentRoomId()+"}robot:{"+robotUser.getRobotId()+"}");
|
||||||
ITObject params = new TObject();
|
ITObject params = new TObject();
|
||||||
params.putString("session", "{user}:" + robotUser.getRobotId() + "," + robotSession);
|
params.putString("session", "{user}:" + robotUser.getRobotId() + "," + robotSession);
|
||||||
//加入房间
|
//加入房间
|
||||||
joinRoomCommon(Integer.parseInt(robotUser.getRobotId()), String.valueOf(robotUser.getCurrentRoomId()), Integer.parseInt(robotUser.getRobotGroupid()), params);
|
joinRoomCommon(Integer.parseInt(robotUser.getRobotId()), String.valueOf(robotUser.getCurrentRoomId()), Integer.parseInt(robotUser.getRobotGroupid()), params);
|
||||||
log.info("重启后已进入房间准备成功: room:{} robot:{}", robotUser.getCurrentRoomId(), robotUser.getRobotId());
|
log.info("重启后已进入房间准备成功: room:{"+robotUser.getCurrentRoomId()+"}robot:{"+robotUser.getRobotId()+"}");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("重启后进房间时发生错误", e);
|
log.error("重启后进房间时发生错误", e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -354,22 +352,22 @@ public class EXGameController extends GameController {
|
||||||
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
try {
|
try {
|
||||||
Set<String> robotTokens = jedis0.smembers("{user}:" + robotId + "_token");
|
Set<String> robotTokens = jedis0.smembers("{user}:" + robotId + "_token");
|
||||||
String robotSession = null;
|
String robotSession = robotTokens.stream().filter(jedis0::exists).findFirst().orElse(null);
|
||||||
|
|
||||||
for (String token : robotTokens) {
|
log.info("开始进房间:room:{"+roomId+"}");
|
||||||
if (jedis0.exists(token)) {
|
log.info("开始进房间:{user}:{"+robotId+"}");
|
||||||
robotSession = token;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("开始进房间: room:{}robot:{}", roomId, robotId);
|
|
||||||
log.info("开始进房间: {user}:{}", robotId);
|
|
||||||
|
|
||||||
|
//建立 TCP 连接
|
||||||
TaurusClient client = getCsMjGameServerConnection(roomId + "_" + robotId);
|
TaurusClient client = getCsMjGameServerConnection(roomId + "_" + robotId);
|
||||||
GroupRoomBusiness.joinRoom(groupId, "room:" + roomId, "{user}:" + robotId, null);
|
if (client == null) {
|
||||||
|
log.error("机器人{"+robotId+"}连接游戏服务器失败,connecId:{"+roomId + "_" + robotId+"}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//机器人房间映射关系
|
ITObject joinResult = GroupRoomBusiness.joinRoom(groupId, "room:" + roomId, "{user}:" + robotId, null);
|
||||||
|
log.info("GroupRoomBusiness.joinRoom 结果:robotId:{"+robotId+"}, roomId:{"+roomId+"}, result:{"+joinResult+"}");
|
||||||
|
|
||||||
|
log.info("机器人{"+robotId+"}准备发送 JOIN_ROOM_CS(1002) 协议");
|
||||||
RobotUser robotUser = getRobotRoomInfo(String.valueOf(robotId));
|
RobotUser robotUser = getRobotRoomInfo(String.valueOf(robotId));
|
||||||
String connecId = roomId + "_" + robotId;
|
String connecId = roomId + "_" + robotId;
|
||||||
if (robotUser.getCurrentRoomId() == 0) {
|
if (robotUser.getCurrentRoomId() == 0) {
|
||||||
|
|
@ -377,57 +375,159 @@ public class EXGameController extends GameController {
|
||||||
robotUser.setClient(client);
|
robotUser.setClient(client);
|
||||||
robotUser.setConnecId(connecId);
|
robotUser.setConnecId(connecId);
|
||||||
}
|
}
|
||||||
//先不放入映射 等确认加入成功后再放入
|
|
||||||
//robotRoomMapping.put(robotUser.getConnecId(), robotUser);
|
|
||||||
robotRoomMapping.remove(robotUser.getRobotId());
|
|
||||||
//非阻塞延迟替代Thread.sleep
|
|
||||||
scheduleDelay(() -> {
|
|
||||||
|
|
||||||
}, 2, TimeUnit.SECONDS);
|
//先不放入映射 等确认加入成功后再放入
|
||||||
|
robotRoomMapping.remove(robotUser.getRobotId());
|
||||||
|
|
||||||
params.putString("session", "{user}:" + robotId + "," + robotSession);
|
params.putString("session", "{user}:" + robotId + "," + robotSession);
|
||||||
|
|
||||||
//发送加入房间请求到game_mj_cs
|
//发送 JOIN_ROOM_CS(1002) 到 game_mj_cs
|
||||||
client.send(Config.JOIN_ROOM_CS, params, response -> {
|
client.send(Config.JOIN_ROOM_CS, params, response -> {
|
||||||
//成功响应后才建立映射关系
|
try {
|
||||||
robotRoomMapping.put(robotUser.getConnecId(), robotUser);
|
log.info("JOIN_ROOM_CS(1002) 响应:{"+response+"}");
|
||||||
robotConnectionManager.reconnectToGameServer(response, robotUser, client);
|
|
||||||
});
|
|
||||||
log.info("已进入房间成功: {}", connecId);
|
|
||||||
Thread.sleep(1000);
|
|
||||||
if (client.isConnected()) {
|
|
||||||
client.send(Config.GAME_READY_CS, params, response -> {
|
|
||||||
log.info("1003: {}", response);
|
|
||||||
});
|
|
||||||
jedis2.hset("gallrobot", String.valueOf(robotUser.getRobotId()), "1");
|
|
||||||
|
|
||||||
|
// 检查响应是否成功
|
||||||
|
if (response == null || response.messageData == null || response.messageData.param == null) {
|
||||||
|
log.error("机器人{"+robotId+"}加入房间{"+roomId+"}失败:game_mj_cs 返回 null");
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ITObject responseParam = response.messageData.param;
|
||||||
|
int responseCode = responseParam.containsKey("code") ? responseParam.getInt("code") : 0;
|
||||||
|
if (responseCode != 0) {
|
||||||
|
log.error("机器人{"+robotId+"}加入房间{"+roomId+"}失败:game_mj_cs 返回错误码{"+responseCode+"}, response:{"+response+"}");
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//1002 响应成功后,添加短暂延迟等待服务器将机器人加入 Redis
|
||||||
|
scheduleDelay(() -> {
|
||||||
|
try {
|
||||||
|
//验证机器人是否真的进入了房间(检查 Redis)
|
||||||
|
if (!checkRobotInRoomRedis(roomId, String.valueOf(robotId))) {
|
||||||
|
log.error("机器人{"+robotId+"}加入房间{"+roomId+"}失败:1002 响应成功但 Redis 中未找到机器人,清理资源");
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("机器人{"+robotId+"}Redis 验证成功,建立映射关系");
|
||||||
|
|
||||||
|
//成功响应后才建立映射关系
|
||||||
|
synchronized (robotRoomMapping) {
|
||||||
|
robotRoomMapping.put(robotUser.getConnecId(), robotUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("机器人{"+robotId+"}已成功加入房间{"+roomId+"},建立映射关系");
|
||||||
|
|
||||||
|
//发送准备协议
|
||||||
|
scheduleDelay(() -> {
|
||||||
|
try {
|
||||||
|
if (client != null && client.isConnected()) {
|
||||||
|
client.send(Config.GAME_READY_CS, params, readyResponse -> {
|
||||||
|
try {
|
||||||
|
log.info("GAME_READY 响应:{"+readyResponse+"}");
|
||||||
|
|
||||||
|
//设置准备状态
|
||||||
robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
|
robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
|
||||||
robotConnectionManager.setSessionAndToken("{user}:" + robotId, robotSession, robotUser.getConnecId());
|
robotConnectionManager.setSessionAndToken("{user}:" + robotId, robotSession, robotUser.getConnecId());
|
||||||
|
|
||||||
|
//标记机器人为可用状态
|
||||||
|
jedis2.hset("gallrobot", String.valueOf(robotUser.getRobotId()), "1");
|
||||||
|
robotUser.setIntoRoomTime(robotConnectionManager.getTime());
|
||||||
|
|
||||||
|
log.info("机器人{"+robotId+"}准备成功,房间:{"+roomId+"}");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("机器人{"+robotId+"}设置准备状态失败"+ e);
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
}
|
}
|
||||||
//添加超时检查机制
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("机器人{"+robotId+"}发送准备协议失败"+ e);
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
|
}
|
||||||
|
}, 1000, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("机器人{"+robotId+"}Redis 验证失败"+ e);
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
|
}
|
||||||
|
}, 500, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理 JOIN_ROOM_CS 响应时发生异常", e);
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("已进入房间成功:{"+robotUser.getConnecId()+"}");
|
||||||
|
|
||||||
|
/*//添加超时检查机制(15 秒)
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
//定时任务替代Thread.sleep
|
//定时任务替代 Thread.sleep
|
||||||
scheduleDelay(() -> {
|
scheduleDelay(() -> {
|
||||||
//15秒后还没有建立映射关系 加入可能失败
|
//15 秒后还没有建立映射关系或状态不是准备状态,说明加入失败
|
||||||
if (robotRoomMapping.get(robotUser.getConnecId()) == null) {
|
RobotUser currentUser = robotRoomMapping.get(connecId);
|
||||||
log.info("机器人{{}}加入房间{{}}超时,清理临时状态", robotId, roomId);
|
if (currentUser == null || currentUser.getStatus() != ROBOTEventType.ROBOT_INTOROOM_READY) {
|
||||||
robotConnectionManager.disconnectFromGameServer(connecId);
|
log.warn("机器人{}加入房间{}超时(15 秒),清理临时状态", robotId, roomId);
|
||||||
|
cleanupFailedRobot(robotUser, connecId, roomId);
|
||||||
}
|
}
|
||||||
}, 15, TimeUnit.SECONDS);
|
}, 15, TimeUnit.SECONDS);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("机器人加入房间超时", e);
|
log.error("机器人{}加入房间超时检查异常", robotId, e);
|
||||||
}
|
}
|
||||||
}, ThreadPoolConfig.getBusinessThreadPool());//指定自定义线程池
|
}, ThreadPoolConfig.getBusinessThreadPool());*/
|
||||||
robotUser.setIntoRoomTime(robotConnectionManager.getTime());
|
log.info("已进入房间准备成功:{"+robotUser.getConnecId()+"}");
|
||||||
log.info("已进入房间成功: {}", robotUser.getConnecId());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("机器人加入房间失败", e);
|
log.error("加入房间时发生错误", e);
|
||||||
|
//发生异常时清理资源
|
||||||
|
String failedConnecId = roomId + "_" + robotId;
|
||||||
|
cleanupFailedRobot(null, failedConnecId, roomId);
|
||||||
} finally {
|
} finally {
|
||||||
jedis0.close();
|
jedis0.close();
|
||||||
jedis2.close();
|
jedis2.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理加入失败的机器人资源
|
||||||
|
*/
|
||||||
|
private void cleanupFailedRobot(RobotUser robotUser, String connecId, String roomId) {
|
||||||
|
try {
|
||||||
|
String robotId = robotUser != null ? robotUser.getRobotId() : "unknown";
|
||||||
|
log.info("开始清理失败机器人资源:robotId:{"+robotId+"}, connecId:{"+connecId+"}, roomId:{"+roomId+"}");
|
||||||
|
|
||||||
|
// 清理映射关系
|
||||||
|
if (robotUser != null) {
|
||||||
|
robotRoomMapping.remove(robotUser.getConnecId());
|
||||||
|
robotRoomMapping.remove(robotUser.getRobotId());
|
||||||
|
} else {
|
||||||
|
robotRoomMapping.remove(connecId);
|
||||||
|
if (connecId != null && connecId.contains("_")) {
|
||||||
|
String[] parts = connecId.split("_");
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
robotRoomMapping.remove(parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 断开 TCP 连接
|
||||||
|
TaurusClient client = robotUser != null ? robotUser.getClient() : null;
|
||||||
|
if (client != null && client.isConnected()) {
|
||||||
|
client.killConnection();
|
||||||
|
log.info("已清理失败机器人{"+robotId+"}的 TCP 连接");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知连接管理器清理资源
|
||||||
|
robotConnectionManager.disconnectFromGameServer(connecId);
|
||||||
|
|
||||||
|
log.info("完成失败机器人资源的清理:connecId:{"+connecId+"}, roomId:{"+roomId+"}");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("清理失败机器人资源时发生异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据机器人ID获取其所在的房间信息
|
* 根据机器人ID获取其所在的房间信息
|
||||||
*/
|
*/
|
||||||
|
|
@ -474,7 +574,7 @@ public class EXGameController extends GameController {
|
||||||
lastAccessTime.remove(removedUser.getConnecId());
|
lastAccessTime.remove(removedUser.getConnecId());
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("已删除机器人房间信息: {}", robotId);
|
log.info("已删除机器人房间信息: {"+robotId+"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -500,7 +600,7 @@ public class EXGameController extends GameController {
|
||||||
for (String connecId : expiredConnections) {
|
for (String connecId : expiredConnections) {
|
||||||
RobotUser robotUser = robotRoomMapping.get(connecId);
|
RobotUser robotUser = robotRoomMapping.get(connecId);
|
||||||
if (robotUser != null) {
|
if (robotUser != null) {
|
||||||
log.info("清理超时连接{}已超时,, 机器人ID: {}", connecId, robotUser.getRobotId());
|
log.info("清理超时连接{"+connecId+"}已超时,, 机器人ID: {"+robotUser.getRobotId()+"}");
|
||||||
robotConnectionManager.disconnectFromGameServer(connecId);
|
robotConnectionManager.disconnectFromGameServer(connecId);
|
||||||
}
|
}
|
||||||
robotRoomMapping.remove(connecId);
|
robotRoomMapping.remove(connecId);
|
||||||
|
|
@ -508,7 +608,7 @@ public class EXGameController extends GameController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!expiredConnections.isEmpty()) {
|
if (!expiredConnections.isEmpty()) {
|
||||||
log.info("本次清理了 {} 个超时连接", expiredConnections.size());
|
log.info("本次清理了 {"+expiredConnections.size()+"} 个超时连接");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -518,9 +618,9 @@ public class EXGameController extends GameController {
|
||||||
*/
|
*/
|
||||||
public static TaurusClient getCsMjGameServerConnection(String connecId) {
|
public static TaurusClient getCsMjGameServerConnection(String connecId) {
|
||||||
TaurusClient taurusClient = robotConnectionManager.getGameClient(connecId);
|
TaurusClient taurusClient = robotConnectionManager.getGameClient(connecId);
|
||||||
log.info("根据机器人ID和连接ID获取红中麻将游戏服务器连接 connecId: {}", connecId);
|
log.info("根据机器人ID和连接ID获取红中麻将游戏服务器连接 connecId: {"+connecId+"}");
|
||||||
if (taurusClient != null) {
|
if (taurusClient != null) {
|
||||||
log.info("成功获取游戏服务器连接,connecId: {}", connecId);
|
log.info("成功获取游戏服务器连接,connecId: {"+connecId+"}");
|
||||||
return taurusClient;
|
return taurusClient;
|
||||||
}
|
}
|
||||||
taurusClient = robotConnectionManager.connectToGameServer(connecId);
|
taurusClient = robotConnectionManager.connectToGameServer(connecId);
|
||||||
|
|
@ -555,11 +655,88 @@ public class EXGameController extends GameController {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("检查Redis房间玩家信息时发生错误,roomId: {}, robotId: {}", roomId, robotId, e);
|
log.error("检查Redis房间玩家信息时发生错误,roomId: {"+roomId+"}, robotId: {"+robotId+"}"+ e);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
jedis.close();
|
jedis.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查机器人 ID 是否与房间内已有玩家冲突
|
||||||
|
*/
|
||||||
|
private boolean isPlayerIdConflictInRoom(String roomId, int robotId) {
|
||||||
|
Jedis jedis = Redis.use().getJedis();
|
||||||
|
Jedis jedis2 = Redis.use("group1_db2").getJedis();
|
||||||
|
try {
|
||||||
|
//查询该房间的玩家信息
|
||||||
|
String playersStr = jedis.hget("room:" + roomId, "players");
|
||||||
|
if (playersStr == null || playersStr.equals("[]")) {
|
||||||
|
log.info("房间{"+roomId+"}为空,机器人{"+robotId+"}可以加入");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String players = playersStr.substring(1, playersStr.length() - 1);
|
||||||
|
String[] playerIds = players.split(",");
|
||||||
|
|
||||||
|
//统计房间中的真人数量和机器人数量
|
||||||
|
int realPlayerCount = 0;
|
||||||
|
int robotCount = 0;
|
||||||
|
boolean hasSameRobot = false;
|
||||||
|
|
||||||
|
for (String playerIdStr : playerIds) {
|
||||||
|
try {
|
||||||
|
int playerId = Integer.parseInt(playerIdStr.trim());
|
||||||
|
|
||||||
|
String robotData = jedis2.hget("{robot}:" + playerId, "password");
|
||||||
|
boolean isRobot = (robotData != null);
|
||||||
|
|
||||||
|
if (isRobot) {
|
||||||
|
robotCount++;
|
||||||
|
//检查是否是相同的机器人 ID
|
||||||
|
if (playerId == robotId) {
|
||||||
|
hasSameRobot = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
realPlayerCount++;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("解析玩家 ID 失败:"+playerIdStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSameRobot) {
|
||||||
|
log.info("房间{"+roomId+"}中已有相同机器人{"+robotId+"},允许重复加入");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realPlayerCount >= 2) {
|
||||||
|
log.warn("房间{"+roomId+"}中已有{"+realPlayerCount+"}个真人玩家,拒绝机器人{"+robotId+"}加入");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realPlayerCount == 1) {
|
||||||
|
if (robotCount == 0) {
|
||||||
|
log.info("房间{"+roomId+"}中有 1 个真人玩家,允许第一个机器人{"+robotId+"}加入陪打");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log.info("房间{"+roomId+"}中已有 1 个真人 +1 个机器人,拒绝额外机器人{"+robotId+"}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (robotCount >= 2) {
|
||||||
|
log.warn("房间{"+roomId+"}中已有{"+robotCount+"}个机器人,不再添加新机器人{"+robotId+"}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("房间{"+roomId+"}当前状态:真人{"+realPlayerCount+"}人,机器人{"+robotCount+"}个,允许机器人{"+robotId+"}加入");
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("检查房间人数时发生异常,roomId:{"+roomId+"}, robotId:{"+robotId+"}" + e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
jedis.close();
|
||||||
|
jedis2.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -32,8 +32,8 @@ import static robot.mj.thread.ThreadPoolConfig.scheduleDelay;
|
||||||
*/
|
*/
|
||||||
public class RobotConnectionManager {
|
public class RobotConnectionManager {
|
||||||
|
|
||||||
private final Map<String, HuNanHongZhong> huNanHongZhongInstances = new ConcurrentHashMap<>();
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(RobotConnectionManager.class);
|
private static final Logger log = LoggerFactory.getLogger(RobotConnectionManager.class);
|
||||||
|
private static final Map<String, HuNanHongZhong> huNanHongZhongInstances = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
//记录活跃连接 用于资源清理判断
|
//记录活跃连接 用于资源清理判断
|
||||||
private static final Set<String> activeConnections = ConcurrentHashMap.newKeySet();
|
private static final Set<String> activeConnections = ConcurrentHashMap.newKeySet();
|
||||||
|
|
@ -41,7 +41,7 @@ public class RobotConnectionManager {
|
||||||
//记录连接创建时间
|
//记录连接创建时间
|
||||||
private static final Map<String, Long> connectionCreationTime = new ConcurrentHashMap<>();
|
private static final Map<String, Long> connectionCreationTime = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
//连接最大生存时间(5分钟)
|
//连接最大生存时间(5 分钟)
|
||||||
private static final long MAX_CONNECTION_LIFETIME = 5 * 60 * 1000;
|
private static final long MAX_CONNECTION_LIFETIME = 5 * 60 * 1000;
|
||||||
private final EXGameController exGameController;
|
private final EXGameController exGameController;
|
||||||
|
|
||||||
|
|
@ -97,8 +97,12 @@ public class RobotConnectionManager {
|
||||||
*/
|
*/
|
||||||
public TaurusClient connectToGameServer(String connecId) {
|
public TaurusClient connectToGameServer(String connecId) {
|
||||||
try {
|
try {
|
||||||
|
int index = connecId.lastIndexOf("_");
|
||||||
|
String robotId = connecId.substring(index + 1);
|
||||||
|
String clientId = "robot_" + connecId;
|
||||||
|
|
||||||
//创建Taurus客户端
|
//创建Taurus客户端
|
||||||
TaurusClient client = new TaurusClient(host + ":" + port, "game", TaurusClient.ConnectionProtocol.Tcp);
|
TaurusClient client = new TaurusClient(host + ":" + port, clientId, TaurusClient.ConnectionProtocol.Tcp);
|
||||||
|
|
||||||
//设置事件监听器
|
//设置事件监听器
|
||||||
setupEventListeners(client, connecId);
|
setupEventListeners(client, connecId);
|
||||||
|
|
@ -240,8 +244,16 @@ public class RobotConnectionManager {
|
||||||
*/
|
*/
|
||||||
public void reconnectToGameServer(MessageResponse response, RobotUser robotUser, TaurusClient client) {
|
public void reconnectToGameServer(MessageResponse response, RobotUser robotUser, TaurusClient client) {
|
||||||
String connecId = robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId();
|
String connecId = robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId();
|
||||||
|
//先设置机器人状态为准备状态
|
||||||
|
robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
|
||||||
|
|
||||||
if(client.isConnected()){
|
if(client.isConnected()){
|
||||||
try {
|
try {
|
||||||
|
log.info(String.valueOf(response.messageData.param));
|
||||||
|
if (response.messageData.param==null) {
|
||||||
|
log.info("警告:reconnectToGameServer 重连时未获取到参数");
|
||||||
|
return;
|
||||||
|
}
|
||||||
ITObject obj = response.messageData.param.getTObject("tableInfo");
|
ITObject obj = response.messageData.param.getTObject("tableInfo");
|
||||||
ITObject reloadInfo = response.messageData.param.getTObject("reloadInfo");
|
ITObject reloadInfo = response.messageData.param.getTObject("reloadInfo");
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
|
|
@ -323,13 +335,23 @@ public class RobotConnectionManager {
|
||||||
try {
|
try {
|
||||||
currentInstance.outCard(client);
|
currentInstance.outCard(client);
|
||||||
log.info("断线重连后成功执行出牌操作");
|
log.info("断线重连后成功执行出牌操作");
|
||||||
|
//出牌成功后,根据是否有手牌判断状态
|
||||||
|
if (hcard.size() > 0) {
|
||||||
|
robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_WORKING);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("断线重连后执行出牌操作时发生异常", e);
|
log.error("断线重连后执行出牌操作时发生异常", e);
|
||||||
//即使出牌失败,也要确保连接状态正确
|
//出牌失败,保持在准备状态或设置为工作状态
|
||||||
try {
|
try {
|
||||||
if (robotUser != null) {
|
if (robotUser != null) {
|
||||||
|
//如果有手牌,说明还在游戏中,设置为工作状态
|
||||||
|
if (hcard.size() > 0) {
|
||||||
|
robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_WORKING);
|
||||||
|
} else {
|
||||||
|
//无手牌,保持在准备状态
|
||||||
robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
|
robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Exception statusEx) {
|
} catch (Exception statusEx) {
|
||||||
log.error("更新机器人状态时发生异常", statusEx);
|
log.error("更新机器人状态时发生异常", statusEx);
|
||||||
}
|
}
|
||||||
|
|
@ -443,11 +465,12 @@ public class RobotConnectionManager {
|
||||||
//玩家加入房间
|
//玩家加入房间
|
||||||
else if ("2001".equalsIgnoreCase(command)) {
|
else if ("2001".equalsIgnoreCase(command)) {
|
||||||
scheduleDelay(() -> {
|
scheduleDelay(() -> {
|
||||||
|
Jedis jedis = Redis.use().getJedis();
|
||||||
try {
|
try {
|
||||||
String roomKey = String.valueOf(robotUser.getCurrentRoomId());
|
String roomKey = String.valueOf(robotUser.getCurrentRoomId());
|
||||||
|
|
||||||
//查询该房间的玩家信息
|
//查询该房间的玩家信息
|
||||||
String playersStr = jedis0.hget("room:"+roomKey, "players");
|
String playersStr = jedis.hget("room:"+roomKey, "players");
|
||||||
if (!playersStr.equals("[]")) {
|
if (!playersStr.equals("[]")) {
|
||||||
String players = playersStr.substring(1, playersStr.length() - 1);
|
String players = playersStr.substring(1, playersStr.length() - 1);
|
||||||
String[] playerIds = players.split(",");
|
String[] playerIds = players.split(",");
|
||||||
|
|
@ -471,6 +494,11 @@ public class RobotConnectionManager {
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("处理玩家加入房间检查时发生异常", e);
|
log.error("处理玩家加入房间检查时发生异常", e);
|
||||||
|
} finally {
|
||||||
|
//确保Jedis连接关闭
|
||||||
|
if (jedis != null) {
|
||||||
|
jedis.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 6, TimeUnit.SECONDS);
|
}, 6, TimeUnit.SECONDS);
|
||||||
log.info("玩家{"+ robotUser.getCurrentRoomId()+"}加入房间:"+ param);
|
log.info("玩家{"+ robotUser.getCurrentRoomId()+"}加入房间:"+ param);
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,6 @@ import static robot.mj.thread.ThreadPoolConfig.getBusinessThreadPool;
|
||||||
public class HuNanHongZhong {
|
public class HuNanHongZhong {
|
||||||
private static final Logger log = LoggerFactory.getLogger(HuNanHongZhong.class);
|
private static final Logger log = LoggerFactory.getLogger(HuNanHongZhong.class);
|
||||||
|
|
||||||
public int hongZhongCard = 0;
|
|
||||||
|
|
||||||
//红中麻将手牌
|
//红中麻将手牌
|
||||||
private final List<Integer> hongZhongCardInhand = new ArrayList<>();
|
private final List<Integer> hongZhongCardInhand = new ArrayList<>();
|
||||||
|
|
||||||
|
|
@ -32,13 +30,16 @@ public class HuNanHongZhong {
|
||||||
private final List<Integer> hongZhongchuguopai = new ArrayList<>();
|
private final List<Integer> hongZhongchuguopai = new ArrayList<>();
|
||||||
|
|
||||||
// 玩家座位号
|
// 玩家座位号
|
||||||
public int seat = 0;
|
private int seat = 0;
|
||||||
|
|
||||||
public int playerId = 0;
|
private int playerId = 0;
|
||||||
|
|
||||||
// 会话标识
|
// 当前出的牌
|
||||||
|
private int hongZhongCard = 0;
|
||||||
|
|
||||||
|
// 会话标识 - 改为实例变量
|
||||||
public String session = "";
|
public String session = "";
|
||||||
// 访问令牌
|
// 访问令牌 - 改为实例变量
|
||||||
public String token = "";
|
public String token = "";
|
||||||
|
|
||||||
private static HongZhongSuanFaTest hongZhongSuanFaTest = new HongZhongSuanFaTest();
|
private static HongZhongSuanFaTest hongZhongSuanFaTest = new HongZhongSuanFaTest();
|
||||||
|
|
@ -70,12 +71,24 @@ public class HuNanHongZhong {
|
||||||
this.playerId = playerId;
|
this.playerId = playerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHongZhongCard() {
|
public String getSession() {
|
||||||
return hongZhongCard;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHongZhongCard(int hongZhongCard) {
|
public void setSession(String session) {
|
||||||
this.hongZhongCard = hongZhongCard;
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HongZhongSuanFaTest getHongZhongSuanFaTest() {
|
||||||
|
return hongZhongSuanFaTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -199,9 +212,8 @@ public class HuNanHongZhong {
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//使用实例变量
|
hongZhongCard = param.getInt("card");
|
||||||
setHongZhongCard(param.getInt("card"));
|
log.info("出牌广播: {}", hongZhongCard);
|
||||||
log.info("出牌广播:{}", getHongZhongCard());
|
|
||||||
log.info("座位号:{}的用户出牌:{}", param.getInt("seat"), param.getInt("card"));
|
log.info("座位号:{}的用户出牌:{}", param.getInt("seat"), param.getInt("card"));
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -291,16 +303,11 @@ public class HuNanHongZhong {
|
||||||
for (int i = 0; i < cardList.size(); i++) {
|
for (int i = 0; i < cardList.size(); i++) {
|
||||||
hongZhongCardInhand.add(cardList.getInt(i));
|
hongZhongCardInhand.add(cardList.getInt(i));
|
||||||
}
|
}
|
||||||
//设置座位号
|
|
||||||
setSeat(param.getInt("seat"));
|
|
||||||
setPlayerId(param.getInt("player"));
|
|
||||||
|
|
||||||
if (hongZhongCardInhand.size() > 13) {
|
if (hongZhongCardInhand.size() > 13) {
|
||||||
outCard(client);
|
outCard(client);
|
||||||
log.info("机器人:{}为庄家,需要出牌, 牌为:{}", param.getInt("seat"), hongZhongCardInhand.get(0));
|
log.info("机器人:{}为庄家,需要出牌,牌为:{}", param.getInt("seat"), hongZhongCardInhand.get(0));
|
||||||
}
|
}
|
||||||
log.info("机器人:{}初始化手牌, 牌为:{}", param.getInt("seat"), hongZhongCardInhand.toString());
|
log.info("机器人:{}初始化手牌,牌为:{}", param.getInt("seat"), hongZhongCardInhand.toString());
|
||||||
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -419,7 +426,7 @@ public class HuNanHongZhong {
|
||||||
hongZhongSuanFaTest.separateAndAnalyzeHand(hongZhongCardInhand);
|
hongZhongSuanFaTest.separateAndAnalyzeHand(hongZhongCardInhand);
|
||||||
|
|
||||||
// 红中麻将出牌
|
// 红中麻将出牌
|
||||||
String hongzhongOutCard = hongZhongSuanFaTest.outCardSuanFa(hongZhongCardInhand, getHongZhongCard());
|
String hongzhongOutCard = hongZhongSuanFaTest.outCardSuanFa(hongZhongCardInhand, hongZhongCard);
|
||||||
// String hongzhongOutCard = hongZhongSuanFaTest.outCardSuanFa(list, hongZhongCard);
|
// String hongzhongOutCard = hongZhongSuanFaTest.outCardSuanFa(list, hongZhongCard);
|
||||||
ITObject params = TObject.newInstance();
|
ITObject params = TObject.newInstance();
|
||||||
int cardToOut;
|
int cardToOut;
|
||||||
|
|
@ -484,7 +491,6 @@ public class HuNanHongZhong {
|
||||||
log.debug("删除出过的牌组 card: {}", card);
|
log.debug("删除出过的牌组 card: {}", card);
|
||||||
log.debug("删除出过的牌组 type: {}", type);
|
log.debug("删除出过的牌组 type: {}", type);
|
||||||
log.debug("删除出过的牌组 from_seat: {}", from_seat);
|
log.debug("删除出过的牌组 from_seat: {}", from_seat);
|
||||||
log.debug("机器人 seat: {}", getSeat());
|
|
||||||
|
|
||||||
if (type == 2 || type == 3 || type == 5) { // 碰,杠
|
if (type == 2 || type == 3 || type == 5) { // 碰,杠
|
||||||
getChuGuoCardInhand().remove(Integer.valueOf(card));
|
getChuGuoCardInhand().remove(Integer.valueOf(card));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue