diff --git a/robots/zhipai/robot_zp_fls/.idea/.gitignore b/robots/zhipai/robot_zp_fls/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/robots/zhipai/robot_zp_fls/.idea/compiler.xml b/robots/zhipai/robot_zp_fls/.idea/compiler.xml
new file mode 100644
index 0000000..c03c746
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/.idea/compiler.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/.idea/encodings.xml b/robots/zhipai/robot_zp_fls/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/.idea/jarRepositories.xml b/robots/zhipai/robot_zp_fls/.idea/jarRepositories.xml
new file mode 100644
index 0000000..af6ee75
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/.idea/misc.xml b/robots/zhipai/robot_zp_fls/.idea/misc.xml
new file mode 100644
index 0000000..d5cd614
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/.idea/pom.xml.iml b/robots/zhipai/robot_zp_fls/.idea/pom.xml.iml
new file mode 100644
index 0000000..dfa92c3
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/.idea/pom.xml.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/.idea/vcs.xml b/robots/zhipai/robot_zp_fls/.idea/vcs.xml
new file mode 100644
index 0000000..77a3cc7
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/config/game-config.xml b/robots/zhipai/robot_zp_fls/config/game-config.xml
new file mode 100644
index 0000000..45e5d06
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/config/game-config.xml
@@ -0,0 +1,10 @@
+
+
+
+ 8.134.76.43
+ 8.134.76.43
+ 8707
+ 8707
+ 107
+ true
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/config/log4j.properties b/robots/zhipai/robot_zp_fls/config/log4j.properties
new file mode 100644
index 0000000..6786dba
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/config/log4j.properties
@@ -0,0 +1,20 @@
+
+log4j.rootLogger = INFO,consoleAppender,fileAppender
+
+# ConsoleAppender
+log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
+log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.consoleAppender.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%t] %c{2} %3x - %m%n
+
+
+# Regular FileAppender
+log4j.appender.fileAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.fileAppender.File=${WORKDIR}/logs/web_main.log
+log4j.appender.fileAppender.layout.ConversionPattern=%d{dd MMM yyyy | HH:mm:ss,SSS} | %-5p | %t | %c{3} | %3x | %m%n
+log4j.appender.fileAppender.Encoding=UTF-8
+log4j.appender.fileAppender.DatePattern='.'yyyy-MM-dd
+log4j.appender.dailyFile.Append=true
+
+# The file is rolled over very day
+log4j.appender.fileAppender.DatePattern ='.'yyyy-MM-dd
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/config/taurus-core.xml b/robots/zhipai/robot_zp_fls/config/taurus-core.xml
new file mode 100644
index 0000000..2dd31d5
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/config/taurus-core.xml
@@ -0,0 +1,62 @@
+
+
+ log4j.properties
+
+
+ redis
+ com.taurus.core.plugin.redis.RedisPlugin
+
+
+
+ 200
+
+ 50
+
+ 20
+
+ 2000
+
+ false
+
+ false
+
+ true
+
+ 100
+
+ 60000
+
+ 30000
+
+ 1800000
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/config/taurus-permanent.xml b/robots/zhipai/robot_zp_fls/config/taurus-permanent.xml
new file mode 100644
index 0000000..1619e2c
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/config/taurus-permanent.xml
@@ -0,0 +1,75 @@
+
+
+ 1
+
+ 512
+
+ Heap
+
+ Heap
+
+ 524288
+
+ 16384
+
+ 32768
+
+ 512
+
+
+ 4
+ 2
+ 2
+
+
+ true
+
+ 300
+
+
+
+
+
+
+
+
+
+ 1.2.3.4
+
+
+ 127.0.0.1
+
+ 10000
+
+
+
+ false
+ 0.0.0.0
+ 80
+
+
+
+
+ robot - test
+ robot.zp.EXMainServer
+
+
+
+
+ Sys
+ 16
+ 32
+ 60000
+ 5000
+
+
+
+
+ Ext
+ 16
+ 32
+ 60000
+ 5000
+
+
+
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/pom.xml b/robots/zhipai/robot_zp_fls/pom.xml
new file mode 100644
index 0000000..1f536b3
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/pom.xml
@@ -0,0 +1,47 @@
+
+ 4.0.0
+
+ com.robot
+ robot_zp_fulushou
+ 1.0.0
+ jar
+
+ robot_zp_fulushou
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ com.robot
+ robot_common
+ 1.0.0
+
+
+
+
+
+
+
+
+ robot
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.6.1
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+
+
+
+
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/Config.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/Config.java
new file mode 100644
index 0000000..b20d162
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/Config.java
@@ -0,0 +1,37 @@
+package robot.zp;
+
+public class Config {
+
+ /**
+ * 发送准备 - robot_zp_fls to game_zp_fls 的协议号
+ */
+ public static final String GAME_READY_FLS = "1003";
+
+ /**
+ * 加入房间 - robot_mgr to game_zp_fls 的内部协议号
+ */
+ public static final String JOIN_ROOM_FLS = "1002";
+
+ /** Web组加入房间协议 */
+ public static final String WEB_GROUP_JOIN_ROOM = "225";
+
+ /** Web组主动重连协议 */
+ public static final String WEB_GROUP_ACTIVE_RECONNECT = "226";
+
+ //==================== 游戏服务器配置 ====================
+ /** 游戏服务器主机地址 */
+ public static final String GAME_SERVER_HOST = "127.0.0.1";
+
+ /** 游戏服务器端口 */
+ public static final String GAME_SERVER_PORT = "8971";
+
+ /** 默认密码 */
+ public static final String DEFAULT_PASSWORD = "123456";
+
+ /** 默认PID */
+ public static final String DEFAULT_PID = "107";
+
+ /** 默认群组ID */
+ public static final String DEFAULT_GROUP_ID = "426149";
+
+}
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXGameController.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXGameController.java
new file mode 100644
index 0000000..2728bd3
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXGameController.java
@@ -0,0 +1,369 @@
+package robot.zp;
+
+import com.robot.GameController;
+import com.robot.GameInterceptor;
+import com.taurus.core.entity.ITObject;
+import com.taurus.core.entity.TObject;
+import com.taurus.core.plugin.redis.Redis;
+import com.taurus.core.routes.ActionKey;
+import com.taurus.permanent.data.Session;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import redis.clients.jedis.Jedis;
+import robot.zp.info.RobotUser;
+import robot.zp.thread.ThreadPoolConfig;
+import taurus.client.TaurusClient;
+import taurus.client.business.GroupRoomBusiness;
+import taurus.util.ROBOTEventType;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import static robot.zp.thread.ThreadPoolConfig.scheduleDelay;
+
+/**
+ * 福禄寿游戏控制器 - 处理游戏协议
+ */
+public class EXGameController extends GameController {
+ private static final Logger log = LoggerFactory.getLogger(EXGameController.class);
+
+ private static final RobotConnectionManager robotConnectionManager = new RobotConnectionManager();
+
+ //机器人房间
+ protected static final Map robotRoomMapping = new ConcurrentHashMap<>();
+
+ //记录最近访问时间
+ private static final Map lastAccessTime = new ConcurrentHashMap<>();
+
+ //设置连接超时时间(5分钟)
+ private static final long CONNECTION_TIMEOUT = 5 * 60 * 1000;
+
+ public EXGameController() {
+ super();
+ log.info("福禄寿游戏控制器已初始化");
+ }
+
+ /**
+ * 接收来自web_group的加入房间协议
+ */
+ @ActionKey(value = Config.WEB_GROUP_JOIN_ROOM, validate = GameInterceptor.NOT_PLAYER)
+ public void webGroup(Session session, ITObject params, int gid) {
+ int robotId = params.getInt("robotid");
+ String roomId = params.getString("roomid");
+ int groupId = params.getInt("groupid");
+
+ //检查Redis中该房间是否真的包含当前机器人
+ if (!checkRobotInRoomRedis(roomId, String.valueOf(robotId))) {
+ //Redis中不存在该机器人 清理本地可能的错误映射
+ List robotUsers = getRobotUsersByRoomId(Integer.parseInt(roomId));
+ if (!robotUsers.isEmpty()) {
+ synchronized (robotUsers) {
+ RobotUser robotUser = robotUsers.get(0);
+ log.warn("房间{}中Redis未找到机器人{},但本地映射存在{},清理本地映射", roomId, robotId, robotUser.getRobotId());
+ robotRoomMapping.remove(robotUser.getConnecId());
+ robotRoomMapping.remove(robotUser.getRobotId());
+ }
+ }
+ } else {
+ //Redis中存在该机器人 检查是否是不同机器人的冲突
+ List robotUsers = getRobotUsersByRoomId(Integer.parseInt(roomId));
+ if (!robotUsers.isEmpty()) {
+ synchronized (robotUsers) {
+ RobotUser robotUser = robotUsers.get(0);
+ int existingRobotId = Integer.parseInt(robotUser.getRobotId());
+
+ if (robotId != existingRobotId) {
+ //不同机器人的冲突
+ log.warn("房间{}中Redis已存在机器人{},当前机器人{}不执行加入逻辑", roomId, existingRobotId, robotId);
+ return;
+ }
+ }
+ }
+ }
+ log.info("225开始进房间: room:{} robot:{}", roomId, robotId);
+ //加入房间
+ joinRoomCommon(robotId, roomId, groupId, params);
+ log.info("225已进入房间准备成功: room:{} robot:{}", roomId, robotId);
+ }
+
+ /**
+ * 接收来自web_group的主动重连协议
+ */
+ @ActionKey(value = Config.WEB_GROUP_ACTIVE_RECONNECT, validate = GameInterceptor.NOT_PLAYER)
+ public void webGroupActive(Session session, ITObject params, int gid) {
+ int robotId = params.getInt("robotid");
+ String roomId = params.getString("roomid");
+ log.info("226开始进房间: room:{} robot:{}", roomId, robotId);
+ //加入房间
+ joinRoomCommon(params.getInt("robotid"), params.getString("roomid"), params.getInt("groupid"), params);
+ log.info("226已进入房间准备成功: room:{} robot:{}", roomId, robotId);
+ }
+
+ /**
+ * 重启服务断线重连
+ * */
+ public void webGroupJoinRoom(RobotUser robotUser) {
+ String connecId = robotUser.getConnecId();
+ Jedis jedis0 = Redis.use("group1_db0").getJedis();
+ Jedis jedis2 = Redis.use("group1_db2").getJedis();
+ //重启检查
+ try {
+ Set robotTokens = jedis0.smembers("{user}:"+robotUser.getRobotId()+"_token");
+ String robotSession = null;
+
+ for (String token : robotTokens) {
+ if (jedis0.exists(token)) {
+ robotSession = token;
+ break;
+ }
+ }
+ String gallrobot = jedis2.hget("gallrobot", robotUser.getRobotId());
+ if (gallrobot.equals("0")) {
+ robotRoomMapping.remove(connecId);
+ return;
+ }
+
+ log.info("重启后开始进房间: room:{} robot:{}", robotUser.getCurrentRoomId(), robotUser.getRobotId());
+ ITObject params = new TObject();
+ params.putString("session", "{user}:" + robotUser.getRobotId() + "," + robotSession);
+ //加入房间
+ joinRoomCommon(Integer.parseInt(robotUser.getRobotId()), String.valueOf(robotUser.getCurrentRoomId()), Integer.parseInt(robotUser.getRobotGroupid()), params);
+ log.info("重启后已进入房间准备成功: room:{} robot:{}", robotUser.getCurrentRoomId(), robotUser.getRobotId());
+
+ } catch (Exception e) {
+ log.error("重启服务断线重连时发生错误", e);
+ } finally {
+ jedis0.close();
+ jedis2.close();
+ }
+ }
+
+ /**
+ * 加入房间逻辑
+ */
+ private void joinRoomCommon(int robotId, String roomId, int groupId, ITObject params) {
+ Jedis jedis0 = Redis.use("group1_db0").getJedis();
+ Jedis jedis2 = Redis.use("group1_db2").getJedis();
+ try {
+ Set robotTokens = jedis0.smembers("{user}:" + robotId + "_token");
+ String robotSession = null;
+
+ for (String token : robotTokens) {
+ if (jedis0.exists(token)) {
+ robotSession = token;
+ break;
+ }
+ }
+
+ log.info("开始进房间: room:{}", roomId);
+ log.info("开始进房间: {user}:{}", robotId);
+
+ TaurusClient client = getFlsGameServerConnection(roomId + "_" + robotId);
+ GroupRoomBusiness.joinRoom(groupId, "room:" + roomId, "{user}:" + robotId, null);
+
+ //机器人房间映射关系
+ RobotUser robotUser = getRobotRoomInfo(String.valueOf(robotId));
+ String connecId = roomId + "_" + robotId;
+ if (robotUser.getCurrentRoomId() == 0) {
+ robotUser.setCurrentRoomId(Integer.parseInt(roomId));
+ robotUser.setClient(client);
+ robotUser.setConnecId(connecId);
+ }
+
+ //先不放入映射 等确认加入成功后再放入
+ //robotRoomMapping.put(robotUser.getConnecId(), robotUser);
+ robotRoomMapping.remove(robotUser.getRobotId());
+ //非阻塞延迟替代Thread.sleep
+ scheduleDelay(() -> {
+
+ }, 2, TimeUnit.SECONDS);
+ params.putString("session", "{user}:" + robotId + "," + robotSession);
+
+ //发送加入房间请求到game_zp_fls
+ client.send(Config.JOIN_ROOM_FLS, params, response -> {
+ //成功响应后才建立映射关系
+ robotRoomMapping.put(robotUser.getConnecId(), robotUser);
+ robotConnectionManager.reconnectToGameServer(response, robotUser, client);
+ });
+
+ log.info("已进入房间成功: {}", robotUser.getConnecId());
+ Thread.sleep(1000);
+ if (client.isConnected()) {
+ client.send(Config.GAME_READY_FLS, params, response -> {
+ log.info("1003:{}", response);
+ });
+ jedis2.hset("gallrobot", String.valueOf(robotUser.getRobotId()), "1");
+
+ robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
+ robotConnectionManager.setSessionAndToken("{user}:" + robotId, robotSession, robotUser.getConnecId());
+ }
+ //添加超时检查机制
+ CompletableFuture.runAsync(() -> {
+ try {
+ //定时任务替代Thread.sleep
+ scheduleDelay(() -> {
+ //15秒后还没有建立映射关系 加入可能失败
+ if (robotRoomMapping.get(robotUser.getConnecId()) == null) {
+ log.warn("机器人{}加入房间{}超时,清理临时状态", robotId, roomId);
+ robotConnectionManager.disconnectFromGameServer(connecId);
+ }
+ }, 15, TimeUnit.SECONDS);
+ //15秒后还没有建立映射关系 加入可能失败
+ if (robotRoomMapping.get(robotUser.getConnecId()) == null) {
+ log.warn("机器人{}加入房间{}超时,清理临时状态", robotId, roomId);
+ robotConnectionManager.disconnectFromGameServer(connecId);
+ }
+ } catch (Exception e) {
+ log.error("机器人加入房间超时", e);
+ }
+ }, ThreadPoolConfig.getBusinessThreadPool());//指定自定义线程池
+ robotUser.setIntoRoomTime(robotConnectionManager.getTime());
+ log.info("已进入房间准备成功: {}", robotUser.getConnecId());
+ } catch (Exception e) {
+ log.error("加入房间时发生错误", e);
+ } finally {
+ jedis0.close();
+ jedis2.close();
+ }
+ }
+
+ /**
+ * 根据机器人ID获取其所在的房间信息
+ */
+ public static RobotUser getRobotRoomInfo(String robotId) {
+ RobotUser robotUser = robotRoomMapping.get(robotId);
+ if (robotUser ==null) {
+ RobotUser robotUserCopy = new RobotUser();
+ robotUserCopy.setRobotId(robotId);
+ robotUserCopy.setPassword(Config.DEFAULT_PASSWORD);
+ robotUserCopy.setGameHost(Config.GAME_SERVER_HOST);
+ robotUserCopy.setGamePort(Config.GAME_SERVER_PORT);
+ robotUserCopy.setRobotGroupid(Config.DEFAULT_GROUP_ID);
+ robotUserCopy.setRobotPid(Config.DEFAULT_PID);
+ return robotUserCopy;
+ }
+ return robotRoomMapping.get(robotId);
+ }
+
+ /**
+ * 根据房间ID获取所有对应的RobotUser
+ */
+ public List getRobotUsersByRoomId(int roomId) {
+ String prefix = roomId + "_";
+ List result = new ArrayList<>();
+
+ for (Map.Entry entry : robotRoomMapping.entrySet()) {
+ if (entry.getKey().startsWith(prefix)) {
+ result.add(entry.getValue());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 根据机器人ID删除其所在的房间信息
+ */
+ public static void removeRobotRoomInfo(String robotId) {
+ RobotUser removedUser = robotRoomMapping.remove(robotId);
+ lastAccessTime.remove(robotId);
+
+ // 如果有连接ID,也一并清理
+ if (removedUser != null && removedUser.getConnecId() != null) {
+ robotRoomMapping.remove(removedUser.getConnecId());
+ lastAccessTime.remove(removedUser.getConnecId());
+ }
+
+ log.info("清理机器人房间信息: {}", robotId);
+ }
+
+ /**
+ * 更新连接的最后访问时间
+ */
+ public static void updateLastAccessTime(String connecId) {
+ lastAccessTime.put(connecId, System.currentTimeMillis());
+ }
+
+ /**
+ * 清理超时的连接
+ */
+ public static void cleanupExpiredConnections() {
+ long currentTime = System.currentTimeMillis();
+ List expiredConnections = new ArrayList<>();
+
+ for (Map.Entry entry : lastAccessTime.entrySet()) {
+ if (currentTime - entry.getValue() > CONNECTION_TIMEOUT) {
+ expiredConnections.add(entry.getKey());
+ }
+ }
+
+ for (String connecId : expiredConnections) {
+ RobotUser robotUser = robotRoomMapping.get(connecId);
+ if (robotUser != null) {
+ log.info("清理超时连接: {}, 机器人ID: {}", connecId, robotUser.getRobotId());
+ robotConnectionManager.disconnectFromGameServer(connecId);
+ }
+ robotRoomMapping.remove(connecId);
+ lastAccessTime.remove(connecId);
+ }
+
+ if (!expiredConnections.isEmpty()) {
+ log.info("本次清理了 {} 个超时连接", expiredConnections.size());
+ }
+ }
+
+ /**
+ * 检查Redis中房间是否包含指定机器人
+ * @param roomId 房间ID
+ * @param robotId 机器人ID
+ * @return 是否包含该机器人
+ */
+ private boolean checkRobotInRoomRedis(String roomId, String robotId) {
+ Jedis jedis = Redis.use().getJedis();
+ try {
+ //查询该房间的玩家信息
+ String playersStr = jedis.hget("room:" + roomId, "players");
+ if (playersStr == null || playersStr.equals("[]")) {
+ return false;
+ }
+
+ String players = playersStr.substring(1, playersStr.length() - 1);
+ String[] playerIds = players.split(",");
+
+ //检查是否包含该机器人
+ int targetRobotId = Integer.parseInt(robotId);
+ for (String playerIdStr : playerIds) {
+ int playerId = Integer.parseInt(playerIdStr.trim());
+ if (playerId == targetRobotId) {
+ return true;
+ }
+ }
+ return false;
+ } catch (Exception e) {
+ log.error("检查Redis房间玩家信息时发生错误,roomId: {}, robotId: {}", roomId, robotId, e);
+ return false;
+ } finally {
+ jedis.close();
+ }
+ }
+
+ /**
+ * 根据机器人ID和连接ID获取福禄寿游戏服务器连接
+ * 基于robotId和connectionId的组合复用连接
+ */
+ public static TaurusClient getFlsGameServerConnection(String connecId) {
+ TaurusClient taurusClient = robotConnectionManager.getGameClient(connecId);
+ log.info("根据机器人ID和连接ID获取福禄寿游戏服务器连接 client: {}", taurusClient);
+ if (taurusClient != null) {
+ log.debug("成功获取游戏服务器连接,connecId: {}", connecId);
+ return taurusClient;
+ }
+ taurusClient = robotConnectionManager.connectToGameServer(connecId);
+ return taurusClient;
+ }
+
+}
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXMainServer.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXMainServer.java
new file mode 100644
index 0000000..f8f1d65
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXMainServer.java
@@ -0,0 +1,169 @@
+package robot.zp;
+
+import java.util.Map;
+
+import com.robot.GameController;
+import com.robot.MainServer;
+import com.robot.data.Player;
+import com.robot.data.Room;
+import com.taurus.core.plugin.redis.Redis;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import redis.clients.jedis.Jedis;
+import robot.zp.info.RobotUser;
+import robot.zp.thread.ResourceCleanupUtil;
+import robot.zp.thread.ThreadPoolConfig;
+import taurus.client.NetManager;
+
+import static robot.zp.EXGameController.robotRoomMapping;
+
+/**
+ * 福禄寿机器人主服务器
+ * TCP服务端接收robot_mgr的协议 同时作为客户端连接game_zp_fls处理AI逻辑
+ */
+public class EXMainServer extends MainServer{
+ private static final Logger log = LoggerFactory.getLogger(EXMainServer.class);
+
+ private static final RobotConnectionManager robotConnectionManager = new RobotConnectionManager();
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ //JVM关闭钩子
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ log.info("收到JVM关闭信号,开始优雅关闭...");
+ try {
+ //关闭所有机器人连接
+ for (Map.Entry entry : robotRoomMapping.entrySet()) {
+ RobotUser robotUser = entry.getValue();
+ if (robotUser.getClient() != null && robotUser.getClient().isConnected()) {
+ robotConnectionManager.disconnectFromGameServer(robotUser.getConnecId());
+ }
+ }
+
+ //关闭线程池
+ robot.zp.thread.ThreadPoolConfig.shutdown();
+
+ log.info("优雅关闭完成");
+ } catch (Exception e) {
+ log.error("关闭过程中发生异常", e);
+ }
+ }));
+
+ // 1. 先启动独立的事件处理线程(只启动一次)
+ startNetEventThread();
+
+ // 2. 启动资源清理定时任务
+ startResourceCleanupScheduler();
+
+ // 3. 启动系统监控
+ //startConnectionCheckScheduler();
+ //测试
+ Jedis jedis2 = Redis.use("group1_db2").getJedis();
+ String robotskey = "g{"+Config.DEFAULT_GROUP_ID+"}:play:"+Config.DEFAULT_PID;
+ Map maprobot = jedis2.hgetAll(robotskey);
+ for(Map.Entry entry : maprobot.entrySet()) {
+ log.info("{}:{}", entry.getKey(), entry.getValue());
+ //是否创建
+ RobotUser robotUser = new RobotUser();
+ robotUser.setRobotId(entry.getKey());
+ robotUser.setPassword(Config.DEFAULT_PASSWORD);
+ robotUser.setGameHost(Config.GAME_SERVER_HOST);
+ robotUser.setGamePort(Config.GAME_SERVER_PORT);
+ robotUser.setRobotGroupid(Config.DEFAULT_GROUP_ID);
+ robotUser.setRobotPid(Config.DEFAULT_PID);
+
+ robotRoomMapping.put(entry.getKey(), robotUser);
+ }
+
+ for(Map.Entry entry : robotRoomMapping.entrySet()) {
+ RobotUser robotUser = entry.getValue();
+ //1、登录
+ //判断是否登录
+ if(!robotUser.isLogin){
+ robotConnectionManager.login(robotUser);
+ }
+ }
+
+ log.info("福禄寿机器人服务器已启动");
+ log.info("服务器将监听端口 {} 用于接收robot_mgr管理协议", gameSetting.port);
+ log.info("当前线程池配置: {}", ThreadPoolConfig.getThreadPoolStatus());
+
+ jedis2.close();
+ }
+
+ /**
+ * 独立的事件处理线程
+ */
+ private void startNetEventThread() {
+ Thread eventThread = new Thread(() -> {
+ while (true) {
+ NetManager.processEvents();
+ try {
+ Thread.sleep(2);
+ } catch (InterruptedException e) {
+ break;
+ } catch (Exception e) {
+ }
+ }
+ }, "Changsha_Thread");
+
+ eventThread.setDaemon(true); //设置为守护线程
+ eventThread.start();
+ }
+
+ /**
+ * 启动资源清理定时任务
+ */
+ private void startResourceCleanupScheduler() {
+ Thread cleanupThread = new Thread(() -> {
+ while (true) {
+ try {
+ //每30秒执行一次资源清理
+ Thread.sleep(30000);
+ ResourceCleanupUtil.performCleanup();
+ log.info("线程池状态: {}", ThreadPoolConfig.getThreadPoolStatus());
+ } catch (InterruptedException e) {
+ break;
+ } catch (Exception e) {
+ log.error("资源清理任务异常: {}", e.getMessage(), e);
+ // 发生异常时尝试清理
+ try {
+ ResourceCleanupUtil.performCleanup();
+ } catch (Exception cleanupEx) {
+ log.error("异常清理也失败: {}", cleanupEx.getMessage(), cleanupEx);
+ }
+ }
+ }
+ }, "ResourceCleanupThread");
+
+ cleanupThread.setDaemon(true);
+ cleanupThread.start();
+ log.info("资源清理定时任务已启动");
+ }
+
+
+
+ @Override
+ public Room newRoom(String roomid, Map redis_room_map) {
+ return new EXRoom(roomid, redis_room_map);
+ }
+
+ @Override
+ public Player newPlayer(int i, Room room, String s) {
+ return new EXPlayer(i, room, s);
+ }
+
+
+ protected GameController newController() {
+ return new EXGameController();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ log.info("福禄寿机器人服务器已停止");
+ }
+}
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXPlayer.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXPlayer.java
new file mode 100644
index 0000000..fe596c7
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXPlayer.java
@@ -0,0 +1,26 @@
+package robot.zp;
+
+import com.robot.data.Player;
+import com.robot.data.Room;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ *
+ */
+public class EXPlayer extends Player {
+
+
+ private static final Logger log = LoggerFactory.getLogger(EXPlayer.class);
+
+ public EXPlayer(int playerid, Room table, String session_id) {
+ super(playerid, table, session_id);
+ log.info("new robot");
+ }
+
+ public EXRoom getRoom() {
+ return (EXRoom) room;
+ }
+
+}
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXRoom.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXRoom.java
new file mode 100644
index 0000000..ee57e52
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/EXRoom.java
@@ -0,0 +1,32 @@
+package robot.zp;
+
+import com.robot.data.Room;
+
+
+import java.util.Map;
+
+public class EXRoom extends Room {
+
+ public EXRoom(String roomid, Map redis_room_map) {
+ super(roomid, redis_room_map);
+ }
+ @Override
+ protected void roomResult() {
+
+ }
+ @Override
+ public void endGame() {
+ super.endGame();
+ }
+
+ @Override
+ public void saveMilitaryTotal(boolean dissmiss) {
+ super.saveMilitaryTotal(dissmiss);
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ }
+
+}
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/RobotConnectionManager.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/RobotConnectionManager.java
new file mode 100644
index 0000000..d75dbe2
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/RobotConnectionManager.java
@@ -0,0 +1,877 @@
+package robot.zp;
+
+import com.taurus.core.entity.ITArray;
+import com.taurus.core.entity.ITObject;
+import com.taurus.core.entity.TArray;
+import com.taurus.core.entity.TObject;
+import com.taurus.core.events.Event;
+import com.taurus.core.events.IEventListener;
+import com.taurus.core.plugin.redis.Redis;
+import com.taurus.core.util.ICallback;
+import com.taurus.core.util.Logger;
+import com.taurus.core.util.StringUtil;
+import robot.zp.business.AccountBusiness;
+import robot.zp.info.RobotUser;
+import robot.zp.thread.ThreadPoolConfig;
+import taurus.client.Message;
+import taurus.client.MessageResponse;
+import taurus.client.TaurusClient;
+import taurus.client.SocketCode;
+import redis.clients.jedis.Jedis;
+import robot.zp.handler.FuLuShouHandler;
+import taurus.util.ROBOTEventType;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import static robot.zp.thread.ThreadPoolConfig.scheduleDelay;
+
+import static robot.zp.EXGameController.robotRoomMapping;
+
+/**
+ * 机器人连接管理器 - 管理与游戏服务器的连接
+ */
+public class RobotConnectionManager {
+
+ private final Logger log = Logger.getLogger(RobotConnectionManager.class);
+ private static final Map fuLuShouHandlerInstances = new ConcurrentHashMap<>();
+
+ //记录活跃连接 用于资源清理判断
+ private static final Set activeConnections = ConcurrentHashMap.newKeySet();
+
+ //记录连接创建时间
+ private static final Map connectionCreationTime = new ConcurrentHashMap<>();
+
+ //连接最大生存时间(5 分钟)
+ private static final long MAX_CONNECTION_LIFETIME = 5 * 60 * 1000;
+ private final EXGameController exGameController;
+
+ private final String host= Config.GAME_SERVER_HOST;
+ private final int port= Integer.parseInt(Config.GAME_SERVER_PORT);
+
+ /*福禄寿游戏算法相关 start*/
+ private final Map>> playerOutcardsMapByConn = new ConcurrentHashMap<>();
+ private final Map>> playerchisMapByConn = new ConcurrentHashMap<>();
+ private final Map>> playerpengsMapByConn = new ConcurrentHashMap<>();
+ private final Map>> playermingsMapByConn = new ConcurrentHashMap<>();
+ private final Map>> playerzisMapByConn = new ConcurrentHashMap<>();
+
+ private Map> getPlayerOutcardsMap(String connecId) {
+ return playerOutcardsMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
+ }
+ private Map> getPlayerchisMap(String connecId) {
+ return playerchisMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
+ }
+ private Map> getPlayerpengsMap(String connecId) {
+ return playerpengsMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
+ }
+ private Map> getPlayermingsMap(String connecId) {
+ return playermingsMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
+ }
+ private Map> getPlayerzisMap(String connecId) {
+ return playerzisMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
+ }
+ private int pid = 0;
+ private Map count = new HashMap();
+ /*福禄寿游戏算法相关 end*/
+
+
+
+
+ public RobotConnectionManager() {
+ exGameController = new EXGameController();
+ }
+
+ /**
+ * 获取福禄寿处理器实例
+ */
+ private FuLuShouHandler getFuLuShouHandlerInstance(String connecId) {
+ //标记连接为活跃状态
+ activeConnections.add(connecId);
+ connectionCreationTime.put(connecId, System.currentTimeMillis());
+
+ //定期清理过期连接
+ cleanupExpiredInstances();
+
+ FuLuShouHandler existingInstance = fuLuShouHandlerInstances.get(connecId);
+ if (existingInstance != null) {
+ return existingInstance;
+ }
+
+ FuLuShouHandler newInstance = new FuLuShouHandler();
+ log.info("创建新的 FuLuShouHandler 实例:{}", connecId);
+
+ fuLuShouHandlerInstances.put(connecId, newInstance);
+ log.info("当前 FuLuShouHandler 实例总数:{}", fuLuShouHandlerInstances.size());
+ return newInstance;
+ }
+
+ /**
+ * 设置会话和令牌
+ */
+ public void setSessionAndToken(String session, String token, String connecId) {
+ FuLuShouHandler handler = getFuLuShouHandlerInstance(connecId);
+ handler.session = session;
+ handler.token = token;
+ }
+
+ /**
+ * 连接到福禄寿游戏服务器
+ */
+ public TaurusClient connectToGameServer(String connecId) {
+ try {
+ //创建Taurus客户端
+ TaurusClient client = new TaurusClient(host + ":" + port, "game", TaurusClient.ConnectionProtocol.Tcp);
+
+ //设置事件监听器
+ setupEventListeners(client, connecId);
+
+ client.connect();
+
+ return client;
+ } catch (Exception e) {
+ log.error("连接到游戏服务器时发生异常", e);
+ return null;
+ }
+ }
+
+ /**
+ * 断开与游戏服务器的连接(主动断开)
+ */
+ public void disconnectFromGameServer(String connecId) {
+ log.info("开始主动断开连接:{}", connecId);
+ RobotUser robotUser = robotRoomMapping.remove(connecId);
+
+ //标记连接为非活跃
+ activeConnections.remove(connecId);
+ connectionCreationTime.remove(connecId);
+
+ //清理连接数据
+ if (connecId != null) {
+ FuLuShouHandler handler = fuLuShouHandlerInstances.get(connecId);
+ if (handler != null) {
+ //清理所有集合数据以释放内存
+ handler.clearAllData();
+ log.info("清空 FuLuShouHandler 集合数据:{}", connecId);
+ }
+
+ //移除实例和相关数据
+ fuLuShouHandlerInstances.remove(connecId);
+ playerOutcardsMapByConn.remove(connecId);
+ playerchisMapByConn.remove(connecId);
+ playerpengsMapByConn.remove(connecId);
+ playermingsMapByConn.remove(connecId);
+ playerzisMapByConn.remove(connecId);
+
+ log.info("清理完成,当前活跃连接数:{}, 实例数:{}", activeConnections.size(), fuLuShouHandlerInstances.size());
+ }
+
+ if (robotUser != null) {
+ TaurusClient client = robotUser.getClient();
+ if (client != null) {
+ try {
+ if (client.isConnected()) {
+ client.killConnection();
+ }
+ log.info("客户端主动断开连接完成:{}", connecId);
+ } catch (Exception e) {
+ log.error("断开客户端连接时发生异常:{}, 错误:{}", connecId, e.getMessage(), e);
+ }
+ } else {
+ log.warn("客户端连接不存在:{}", connecId);
+ }
+
+ //同时清理机器人房间映射
+ EXGameController.removeRobotRoomInfo(robotUser.getRobotId());
+ }
+ }
+
+ /**
+ * 清理过期的实例
+ */
+ private void cleanupExpiredInstances() {
+ long currentTime = System.currentTimeMillis();
+ List expiredConnections = new ArrayList<>();
+
+ //检查连接生存时间
+ for (Map.Entry entry : connectionCreationTime.entrySet()) {
+ if (currentTime - entry.getValue() > MAX_CONNECTION_LIFETIME) {
+ expiredConnections.add(entry.getKey());
+ }
+ }
+
+ //清理过期连接
+ for (String connecId : expiredConnections) {
+ log.info("清理过期连接实例:{}", connecId);
+ disconnectFromGameServer(connecId);
+ }
+
+ if (!expiredConnections.isEmpty()) {
+ log.info("本次清理了 {} 个过期连接实例", expiredConnections.size());
+ }
+ }
+
+ /**
+ * 设置事件监听器
+ */
+ public void setupEventListeners(TaurusClient client, String connecId) {
+ //添加消息事件监听器
+ IEventListener messageListener = new IEventListener() {
+ @Override
+ public void handleEvent(Event event) {
+ //获取 msg
+ Message message = (Message) event.getParameter("msg");
+
+ ITObject param = message.param;
+ //回调协议号
+ String command = message.command;
+ log.debug("fls OnEvent msg: {}", command);
+
+ //根据玩法ID处理不同的回调
+ if (StringUtil.isNotEmpty(command)) {
+ //直接处理协议
+ handleProtocol(command, message, client, connecId);
+ }
+ }
+ };
+
+ //添加连接状态监听器
+ IEventListener connectListener = new IEventListener() {
+ @Override
+ public void handleEvent(Event event) {
+ Message message = (Message) event.getParameter("msg");
+ SocketCode code = (SocketCode) event.getParameter("code");
+
+ }
+ };
+
+ //注册事件监听器
+ client.addEventListener(TaurusClient.NetClientEvent.OnEvent, messageListener);
+ client.addEventListener(TaurusClient.NetClientEvent.Connect, connectListener);
+ }
+
+
+ /**
+ * 机器人断线重连
+ */
+ public void reconnectToGameServer(MessageResponse response, RobotUser robotUser, TaurusClient client) {
+ String connecId = robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId();
+ if(client.isConnected()){
+ try {
+ ITObject obj = response.messageData.param.getTObject("tableInfo");
+ ITObject reloadInfo = response.messageData.param.getTObject("reloadInfo");
+ if (obj != null) {
+ //处理 seat
+ //获取机器人的seat
+ ITArray playerData = obj.getTArray("playerData");
+ for (int i = 0; i < playerData.size(); i++) {
+ ITObject tms = playerData.getTObject(i);
+ Integer tmuserid = tms.getInt("aid");
+ if (tmuserid == Integer.parseInt(robotUser.getRobotId())) {
+ Integer seat = tms.getInt("seat");
+ robotUser.setSeat(seat);
+ }
+ }
+ log.info("playerData: {}", playerData);
+
+ log.info("obj: {}", obj);
+ log.info("reloadInfo: {}", reloadInfo);
+ if (reloadInfo != null) {
+ //重连回来的
+ int curren_outcard_seat = reloadInfo.getInt("curren_outcard_seat");
+ if (curren_outcard_seat == robotUser.getSeat()) {
+ //同步手牌
+ ITArray hand_card = reloadInfo.getTArray("hand_card");
+ ITArray info_list = reloadInfo.getTArray("info_list");
+
+ List hcard = new ArrayList<>();
+ if (hand_card != null) {
+ for (int i = 0; i < hand_card.size(); i++) {
+ hcard.add(hand_card.getInt(i));
+ }
+ }
+ ITArray outcard_list = new TArray();
+ if (info_list != null) {
+ for (int i = 0; i < info_list.size(); i++) {
+ ITObject tms = info_list.getTObject(i);
+ Integer playerid = tms.getInt("playerid");
+ if (playerid == Integer.parseInt(robotUser.getRobotId())) {
+ outcard_list = tms.getTArray("outcard_list");
+ }
+ }
+ }
+
+ log.info("hcard>0{}", hcard);
+ if (hcard.size() > 0) {
+ //同步手牌
+ FuLuShouHandler currentInstance = getFuLuShouHandlerInstance(connecId);
+
+ //同步逻辑比较手牌数量
+ List currentHand = currentInstance.getChangShaCardInhand();
+ if (currentHand.isEmpty() || hcard.size() > currentHand.size()) {
+ //手牌集合为空 或者 玩家出牌了
+ currentInstance.updateHandCard(hcard);
+ log.info("断线重连:同步手牌数据,服务器手牌:{}", hcard);
+ } else {
+ log.info("断线重连:使用Redis恢复的手牌数据,数量:{}", currentHand.size());
+ }
+
+ if (outcard_list.size() > 0) {
+ List outcards = new ArrayList<>();
+ for (int i = 0; i < outcard_list.size(); i++) {
+ outcards.add(outcard_list.getInt(i));
+ }
+
+ //检查出牌记录是否需要同步
+ List currentOutCards = currentInstance.getChuGuoCardInhand();
+ if (currentOutCards.isEmpty() || outcards.size() > currentOutCards.size()) {
+ currentInstance.updateOutCard(outcards);
+ log.info("断线重连:同步出牌数据,服务器出牌:{}", outcards);
+ } else {
+ log.info("断线重连:使用Redis恢复的出牌数据,数量:{}", currentOutCards.size());
+ }
+ }
+
+ //非阻塞的延迟执行,增加更完善的异常处理
+ scheduleDelay(() -> {
+ try {
+ //重新获取当前实例,确保数据一致性
+ FuLuShouHandler reconnectedInstance = getFuLuShouHandlerInstance(connecId);
+ Map> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId);
+ Map> currentPlayerchisMap = getPlayerchisMap(connecId);
+ Map> currentPlayerpengsMap = getPlayerpengsMap(connecId);
+ Map> currentPlayermingsMap = getPlayermingsMap(connecId);
+ Map> currentPlayerzisMap = getPlayerzisMap(connecId);
+
+ reconnectedInstance.outCard(client, currentPlayerOutcardsMap, currentPlayerchisMap, currentPlayerpengsMap, currentPlayermingsMap, currentPlayerzisMap);
+ log.info("断线重连后成功执行出牌操作");
+ } catch (Exception e) {
+ log.error("断线重连后执行出牌操作时发生异常", e);
+ //即使出牌失败,也要确保连接状态正确
+ try {
+ if (robotUser != null) {
+ robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_READY);
+ }
+ } catch (Exception statusEx) {
+ log.error("更新机器人状态时发生异常", statusEx);
+ }
+ }
+ }, 2, TimeUnit.SECONDS);
+ } else {
+ log.warn("警告:重连时未获取到手牌数据");
+ }
+ }
+ }
+ }
+ }catch (Exception e) {
+ log.error("机器人断线重连异常");
+ }
+ }else {
+ renconnect(robotUser);
+ }
+ }
+
+ /**
+ * 处理接收到的游戏协议
+ * 福禄寿支持的协议:
+ * 核心流程:811(发牌), 819(摸牌), 812(出牌广播), 813(出牌提示), 814(放招提示), 612(动作), 611(出牌), 815(动作通知), 816(胡牌), 817(结算), 820(换玩家)
+ * 飘鸟系统:1015(飘操作), 833(飘鸟提示), 2031(飘鸟提示 reload), 2032(飘鸟事件)
+ * 房间相关:2001, 2002, 2005, 2008, 2009
+ */
+ private void handleProtocol(String command, Message message, TaurusClient client, String connecId) {
+ RobotUser robotUser = robotRoomMapping.get(connecId);
+
+ //更新连接的最后访问时间
+ EXGameController.updateLastAccessTime(connecId);
+
+ if (robotUser == null) {
+ log.error("未找到机器人用户信息,连接ID: {}", connecId);
+ return;
+ }
+
+ int robotId = Integer.parseInt(robotUser.getRobotId());
+ ITObject param = message.param;
+ FuLuShouHandler handler = getFuLuShouHandlerInstance(connecId);
+ Jedis jedis0 = Redis.use().getJedis();
+ Jedis jedis2 = Redis.use("group1_db2").getJedis();
+ try {
+ //福禄寿 机器人处理事件
+ //初始化手牌
+ if ("811".equalsIgnoreCase(command)) {
+ robotUser.setStatus(ROBOTEventType.ROBOT_INTOROOM_WORKING);
+ //初始化手牌
+ String key = robotId+"";
+ if (jedis2.hget("{robortInfo}:" + key, "circleId") != null && jedis2.hget("{robortInfo}:" + key, "pid") != null) {
+ String circleId = jedis2.hget("{robortInfo}:" + key, "circleId");
+ String pid = jedis2.hget("{robortInfo}:" + key, "pid");
+ String getStart = "g{" + circleId + "}:play:" + pid;
+ if (!pid.equals("0")){
+ jedis2.hset(getStart, key, "2");
+ }
+ }
+ handler.initHandCards(message);
+ }
+ //出牌广播
+ else if ("812".equalsIgnoreCase(command)) {
+ ITArray outcard_map = param.getTArray("outcard_map");
+ ITArray opchicards = param.getTArray("opchicards");
+ ITArray oppengcards = param.getTArray("oppengcards");
+ ITArray opmingcards = param.getTArray("opmingcards");
+ ITArray opzicards = param.getTArray("opzicards");
+
+ //获取当前连接专用的Maps
+ Map> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId);
+ Map> currentPlayerchisMap = getPlayerchisMap(connecId);
+ Map> currentPlayerpengsMap = getPlayerpengsMap(connecId);
+ Map> currentPlayermingsMap = getPlayermingsMap(connecId);
+ Map> currentPlayerzisMap = getPlayerzisMap(connecId);
+
+ //清空旧数据 用新数据完全覆盖
+ currentPlayerOutcardsMap.clear();
+ currentPlayerchisMap.clear();
+ currentPlayerpengsMap.clear();
+ currentPlayermingsMap.clear();
+ currentPlayerzisMap.clear();
+ //出过的牌
+ if (outcard_map != null) {
+ for (int i = 0; i < outcard_map.size(); i++) {
+ ITObject playerData = outcard_map.getTObject(i);
+ int playerId = playerData.getInt("playerId");
+ ITArray outcardsArray = playerData.getTArray("outcards");
+
+ List outcardsList = new ArrayList<>();
+ for (int j = 0; j < outcardsArray.size(); j++) {
+ outcardsList.add(outcardsArray.getInt(j));
+ }
+
+ //存储到当前连接的Map中(覆盖旧数据)
+ currentPlayerOutcardsMap.put(playerId, outcardsList);
+ }
+ }
+
+ //吃的牌
+ if (opchicards != null) {
+ for (int i = 0; i < opchicards.size(); i++) {
+ ITObject playerData = opchicards.getTObject(i);
+ int playerId = playerData.getInt("playerId");
+ ITArray outchiArray = playerData.getTArray("opchicards");
+
+ List outchiList = new ArrayList<>();
+ for (int j = 0; j < outchiArray.size(); j++) {
+ outchiList.add(outchiArray.getInt(j));
+ }
+ currentPlayerchisMap.put(playerId, outchiList);
+ }
+ }
+
+ //碰的牌
+ if (oppengcards != null) {
+ for (int i = 0; i < oppengcards.size(); i++) {
+ ITObject playerData = oppengcards.getTObject(i);
+ int playerId = playerData.getInt("playerId");
+ ITArray outpengArray = playerData.getTArray("oppengcards");
+
+ List outpengList = new ArrayList<>();
+ for (int j = 0; j < outpengArray.size(); j++) {
+ outpengList.add(outpengArray.getInt(j));
+ }
+ currentPlayerpengsMap.put(playerId, outpengList);
+ }
+ }
+
+ //明杠的牌
+ if (opmingcards != null) {
+ for (int i = 0; i < opmingcards.size(); i++) {
+ ITObject playerData = opmingcards.getTObject(i);
+ int playerId = playerData.getInt("playerId");
+ ITArray outmingArray = playerData.getTArray("opmingcards");
+
+ List outmingList = new ArrayList<>();
+ for (int j = 0; j < outmingArray.size(); j++) {
+ outmingList.add(outmingArray.getInt(j));
+ }
+ currentPlayermingsMap.put(playerId, outmingList);
+ }
+ }
+
+ //暗杠的牌
+ if (opzicards != null) {
+ for (int i = 0; i < opzicards.size(); i++) {
+ ITObject playerData = opzicards.getTObject(i);
+ int playerId = playerData.getInt("playerId");
+ ITArray outziArray = playerData.getTArray("opzicards");
+
+ List outziList = new ArrayList<>();
+ for (int j = 0; j < outziArray.size(); j++) {
+ outziList.add(outziArray.getInt(j));
+ }
+ currentPlayerzisMap.put(playerId, outziList);
+ }
+ }
+
+ handler.onDiscardBroadcast(message);
+ }
+ //摸牌
+ else if ("819".equalsIgnoreCase(command)) {
+ handler.drawCard(message);
+ }
+ //出牌提示
+ else if ("813".equalsIgnoreCase(command)) {
+ //获取当前连接的 Maps
+ Map> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId);
+ Map> currentPlayerchisMap = getPlayerchisMap(connecId);
+ Map> currentPlayerpengsMap = getPlayerpengsMap(connecId);
+ Map> currentPlayermingsMap = getPlayermingsMap(connecId);
+ Map> currentPlayerzisMap = getPlayerzisMap(connecId);
+
+ handler.makeDiscardDecision(client, currentPlayerOutcardsMap, currentPlayerchisMap, currentPlayerpengsMap, currentPlayermingsMap, currentPlayerzisMap);
+ }
+ //放招提示
+ else if ("814".equalsIgnoreCase(command)) {
+ handler.actionTip(param, client);
+ }
+ //飘操作
+ else if ("1015".equalsIgnoreCase(command)) {
+ log.info("收到飘操作协议:{}", param);
+ }
+ //2026.02.03修改 玩家加入房间
+ else if ("2001".equalsIgnoreCase(command)) {
+ //直接使用定时任务替代Thread.sleep,避免嵌套异步调用
+ scheduleDelay(() -> {
+ Jedis jedis = Redis.use().getJedis();
+ try {
+ String roomKey = String.valueOf(robotUser.getCurrentRoomId());
+
+ //查询该房间的玩家信息
+ String playersStr = jedis.hget("room:"+roomKey, "players");
+ if (playersStr != null && !playersStr.equals("[]")) {
+ String players = playersStr.substring(1, playersStr.length() - 1);
+ String[] playerIds = players.split(",");
+
+ //判断只有当前机器人一个玩家
+ if (playerIds.length == 1) {
+ int playerId = Integer.parseInt(playerIds[0].trim());
+ if (playerId == robotId) {
+ //发送退出房间协议
+ ITObject params = TObject.newInstance();
+ client.send("1005", params, response -> {
+ EXGameController.removeRobotRoomInfo(String.valueOf(robotId));
+ //更新机器人剩余数量
+ updateLeftoverRobot(robotId);
+ disconnectFromGameServer(connecId);
+ log.info("2001发送退出房间协议1005,robotId: {}", robotId);
+ });
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.error("处理玩家加入房间检查时发生异常", e);
+ } finally {
+ // 确保Jedis连接关闭
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+ }, 6, TimeUnit.SECONDS);
+ log.info("玩家{}加入房间:{}", robotUser.getCurrentRoomId(), param);
+ }
+ //2026.02.03修改 玩家退出房间也要检查
+ else if ("2002".equalsIgnoreCase(command)) {
+ //直接使用定时任务替代Thread.sleep,避免嵌套异步调用
+ scheduleDelay(() -> {
+ Jedis jedis = Redis.use().getJedis();
+ try {
+ String roomKey = String.valueOf(robotUser.getCurrentRoomId());
+
+ //查询该房间的玩家信息
+ String playersStr = jedis.hget("room:"+roomKey, "players");
+ if (playersStr != null && !playersStr.equals("[]")) {
+ String players = playersStr.substring(1, playersStr.length() - 1);
+ String[] playerIds = players.split(",");
+
+ //判断只有当前机器人一个玩家
+ if (playerIds.length == 1) {
+ int playerId = Integer.parseInt(playerIds[0].trim());
+ if (playerId == robotId) {
+ //发送退出房间协议
+ ITObject params = TObject.newInstance();
+ client.send("1005", params, response -> {
+ EXGameController.removeRobotRoomInfo(String.valueOf(robotId));
+ //更新机器人剩余数量
+ updateLeftoverRobot(robotId);
+ disconnectFromGameServer(connecId);
+ log.info("2002发送退出房间协议1005,robotId: {}", robotId);
+ });
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.error("处理玩家退出房间检查时发生异常", e);
+ } finally {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+ }, 6, TimeUnit.SECONDS);
+ }
+ //2026.02.05修改 玩家解散房间
+ else if ("2005".equalsIgnoreCase(command)) {
+ EXGameController.removeRobotRoomInfo(String.valueOf(robotId));
+ //更新机器人剩余数量
+ updateLeftoverRobot(robotId);
+ disconnectFromGameServer(connecId);
+ log.info("2005玩家发送解散房间协议,robotId: {}", robotId);
+ }
+ //2026.02.03修改 解散房间时候恢复机器人账号可以使用
+ else if ("2008".equalsIgnoreCase(command)) {
+ updateLeftoverRobot(Integer.parseInt(robotUser.getRobotId()));
+ disconnectFromGameServer(connecId);
+ }
+ //2026.02.03修改 通过机器人房间映射直接获取房间信息
+ else if ("2009".equalsIgnoreCase(command)) {
+ //直接使用定时任务替代Thread.sleep,避免嵌套异步调用
+ scheduleDelay(() -> {
+ Jedis jedis = null;
+ try {
+ jedis = Redis.use().getJedis();
+ Integer paramRobotId = param.getInt("aid");
+ if (robotUser != null && paramRobotId != null) {
+ String roomKey = String.valueOf(robotUser.getCurrentRoomId());
+
+ //查询该房间的玩家信息
+ String playersStr = jedis.hget(roomKey, "players");
+ if (playersStr != null && !playersStr.equals("[]")) {
+ String players = playersStr.substring(1, playersStr.length() - 1);
+ String[] playerIds = players.split(",");
+
+ //判断只有当前机器人一个玩家
+ if (playerIds.length == 1) {
+ int playerId = Integer.parseInt(playerIds[0].trim());
+ if (playerId == paramRobotId) {
+ String gpid = jedis.hget(roomKey, "gpid");
+
+ //更新机器人剩余数量
+ if (gpid != null && count != null && count.containsKey(Integer.parseInt(gpid))) {
+ Integer currentValue = count.get(Integer.parseInt(gpid));
+ if (currentValue != null && currentValue > 0) {
+ count.put(Integer.parseInt(gpid), currentValue - 1);
+ }
+ }
+
+ //发送退出房间协议
+ ITObject params = TObject.newInstance();
+ client.send("1005", params, response -> {
+ EXGameController.removeRobotRoomInfo(String.valueOf(paramRobotId));
+ //断开连接
+ disconnectFromGameServer(connecId);
+ //更新机器人剩余数量
+ updateLeftoverRobot(paramRobotId);
+ log.info("2009发送退出房间协议1005,robotId: {}", paramRobotId);
+ });
+ }
+ }
+ }
+ }
+ } catch (NumberFormatException e) {
+ log.error("2009协议数字格式异常,robotId: {}, connecId: {}", param.get("aid"), connecId);
+ } catch (NullPointerException e) {
+ log.error("2009协议空指针异常,connecId: {}", connecId);
+ } catch (Exception e) {
+ log.error("2009协议处理异常: {}, connecId: {}", e.getMessage(), connecId, e);
+ } finally {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+ }, 6, TimeUnit.SECONDS);
+ }
+ //结算
+ else if ("817".equalsIgnoreCase(command)) {
+ //清空所有 FuLuShouHandler 相关的集合数据
+ handler.clearAllData();
+
+ Integer type = param.getInt("type");
+ if (type == 1 || type == 2) { //为 1 为大结算 为 2 为解散
+ if (count != null && count.containsKey(pid)) {
+ Integer currentValue = count.get(pid);
+ if (currentValue > 0) {
+ count.put(pid, currentValue - 1);
+ }
+ }
+ //更新机器人剩余数量
+ updateLeftoverRobot(Integer.parseInt(robotUser.getRobotId()));
+
+ //游戏结束后主动断开连接
+ disconnectFromGameServer(connecId);
+ }
+ ITObject params = TObject.newInstance();
+ params.putString("session", client.getSession());
+ client.send("1003", params, new ICallback() {
+ @Override
+ public void action(MessageResponse messageResponse) {
+
+ }
+ });
+ }
+ //服务器通知客户端有玩家执行了操作
+ else if ("815".equalsIgnoreCase(command)) {
+ handler.onPlayerAction(param);
+ }
+ //飘鸟提示
+ else if ("833".equalsIgnoreCase(command)) {
+ handler.piaoNiaoTip();
+ }
+ //飘鸟提示 reload
+ else if ("2031".equalsIgnoreCase(command)) {
+ log.info("收到飘鸟提示 reload: {}", param);
+ }
+ //飘鸟事件
+ else if ("2032".equalsIgnoreCase(command)) {
+ log.info("收到飘鸟事件:{}", param);
+ }
+ //2001-2009 房间相关协议保持原有逻辑
+ } catch (Exception e) {
+ log.error("处理接收到的游戏协议异常:{}, command: {}", e.getMessage(), command);
+ } finally {
+ if (jedis0 != null) {
+ jedis0.close();
+ }
+ if (jedis2 != null) {
+ jedis2.close();
+ }
+ }
+ }
+
+ /**
+ * 增加 leftover_robot 数量 机器人退出房间
+ */
+ private void updateLeftoverRobot(int robotId) {
+ Jedis jedis2 = Redis.use("group1_db2").getJedis();
+ try {
+
+ jedis2.hset("gallrobot", String.valueOf(robotId), "0");
+
+ jedis2.hset("{grobot}:" + robotId, "start", "0");
+
+ log.info("机器人 {} 退出房间,修改gallrobot为0", robotId);
+ } finally {
+ jedis2.close();
+ }
+ }
+
+ /**
+ * 机器人登录
+ */
+ public void login(RobotUser robotUser){
+ log.info("login:{}", robotUser.getRobotId());
+ ITObject object = null;
+ AccountBusiness accountBusiness = null;
+ accountBusiness = new AccountBusiness();
+ try {
+ //先快速登录
+ object = accountBusiness.fastLogin(Integer.parseInt(robotUser.getRobotId()));
+ log.info("object:{}", object);
+ if(object==null){
+ object = accountBusiness.idPasswordLogin(Integer.parseInt(robotUser.getRobotId()), robotUser.getPassword());
+ }
+ ITObject finalObject = object;
+ CompletableFuture.runAsync(() -> {
+ if (finalObject != null) {
+ //判断是否有房间
+ if(finalObject.getTObject("account")!=null){
+ ITObject validate = TObject.newInstance();
+ validate.putString("token", finalObject.getString("token"));
+ robotUser.setToken(finalObject.getString("token"));;
+ robotUser.setLoginsession("{user}:"+robotUser.getRobotId());
+ if (robotUser.getLoginsession() != null) {
+ robotUser.setIsLogin(true);
+ }
+ if(finalObject.getTObject("account").get("roomid")!=null){
+ String roomid = finalObject.getTObject("account").get("roomid").toString();
+ robotUser.setCurrentRoomId(Integer.parseInt(roomid));
+ connectGame(robotUser);
+
+ robotUser.setConnecId(robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId());
+ log.info("重启获取的机器人还有当前房间,准备加入: {}", robotUser.getConnecId());
+ exGameController.webGroupJoinRoom(robotUser);
+ }
+ }
+ }
+ }, ThreadPoolConfig.getBusinessThreadPool()); //指定自定义线程池
+ } catch (Exception e) {
+ log.error("机器人登录异常");
+ }
+ }
+
+ public void connectGame(RobotUser robotUser){
+ if(robotUser.isLogin){
+ if(robotUser.getClient()==null){
+ TaurusClient client = new TaurusClient(robotUser.getGameHost()+":"+robotUser.getGamePort(), "cm"+robotUser.getRobotId(), TaurusClient.ConnectionProtocol.Tcp);
+ client.setSession(robotUser.getLoginsession());
+ client.connect();
+ setupEventListeners(client, robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId());
+ robotUser.setIsconnect(client.isConnected());
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ log.error("连接游戏服务器时发生异常", e);
+ }
+ robotUser.setClient(client);
+ EXGameController.robotRoomMapping.put(robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId(), robotUser);
+ }else{
+ log.info("reconnect");
+ log.info("client.isConnected(){}", robotUser.getClient().isConnected());
+ if(robotUser.getClient().isConnected()){
+ robotUser.setIsconnect(true);
+ }else{
+ log.info("reconnect{}", robotUser.getClient().getGameID());
+ TaurusClient client = new TaurusClient(robotUser.getGameHost()+":"+robotUser.getGamePort(), "cm"+robotUser.getRobotId(), TaurusClient.ConnectionProtocol.Tcp);
+ client.setSession(robotUser.getLoginsession());
+ client.connect();
+ robotUser.setIsconnect(client.isConnected());
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ log.error("重新连接游戏服务器时发生异常", e);
+ }
+ robotUser.setClient(client);
+ EXGameController.robotRoomMapping.put(robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId(), robotUser);
+ }
+ }
+ }
+ }
+
+ /**
+ * 重连
+ */
+ public void renconnect(RobotUser robotUser){
+ TaurusClient client = robotUser.getClient();
+ if(client!=null){
+ if(client.isConnected()){
+ client.connect();
+ robotUser.setIsconnect(client.isConnected());
+ }
+ }
+ }
+
+ /**
+ * 根据connecId获取游戏服务器连接
+ */
+ public TaurusClient getGameClient(String connecId) {
+ return robotRoomMapping.get(connecId) != null ? robotRoomMapping.get(connecId).getClient() : null;
+ }
+
+
+ public int getTime(){
+ return Integer.parseInt((System.currentTimeMillis() + "").substring(0, 10));
+ }
+
+ public static void sleepTime(int time) {
+ try {
+ //添加延迟
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/business/AccountBusiness.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/business/AccountBusiness.java
new file mode 100644
index 0000000..eab7670
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/business/AccountBusiness.java
@@ -0,0 +1,291 @@
+package robot.zp.business;
+
+import com.data.bean.AccountBean;
+import com.data.bean.GameBean;
+import com.data.cache.AccountCache;
+import com.data.cache.BaseCache;
+import com.data.cache.GameCache;
+import com.data.util.Utility;
+import com.taurus.core.entity.ITArray;
+import com.taurus.core.entity.ITObject;
+import com.taurus.core.entity.TArray;
+import com.taurus.core.entity.TObject;
+import com.taurus.core.plugin.database.DataBase;
+import com.taurus.core.plugin.redis.Redis;
+import com.taurus.core.plugin.redis.RedisLock;
+import com.taurus.core.util.Logger;
+import com.taurus.core.util.StringUtil;
+import com.taurus.core.util.Utils;
+import com.taurus.web.Controller;
+import redis.clients.jedis.Jedis;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class AccountBusiness extends Controller {
+ private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(AccountBusiness.class);
+ private static Logger logger = Logger.getLogger(AccountBusiness.class);
+
+ private ITObject fillLoginData(String session, int accountid) {
+ ITObject resData = TObject.newInstance();
+ ITObject userData = TObject.newInstance();
+ resData.putTObject("account", userData);
+ resData.putUtfString("session_id", session);
+ resData.putTArray("games", getOnlineGames());
+ Jedis jedis0 = Redis.use("group1_db0").getJedis();
+ try {
+ Map map = jedis0.hgetAll(session);
+ userData.putInt("id", accountid);
+ userData.putInt("diamo", Integer.parseInt(map.get("diamo")));
+ userData.putUtfString("nick", map.get("nick"));
+ userData.putUtfString("portrait", map.get("portrait"));
+ userData.putInt("sex", Integer.parseInt(map.get("sex")));
+ userData.putInt("type", Integer.parseInt(map.get("type")));
+ int mng = Integer.parseInt(map.get("mng"));
+ userData.putInt("mng", mng);
+
+ String phone = map.get("phone");
+ if (StringUtil.isNotEmpty(phone)) {
+ userData.putUtfString("phone", phone);
+ }
+
+ String address = map.get("address");
+ if (StringUtil.isNotEmpty(address)) {
+ userData.putUtfString("address", address);
+ }
+
+ String real_info = map.get("real_info");
+ if (StringUtil.isNotEmpty(real_info)) {
+ userData.putTObject("real_info", TObject.newFromJsonData(real_info));
+ }
+ String oldRoom = Utility.getOldRoomV2(jedis0, 0, session, accountid);
+ if (StringUtil.isNotEmpty(oldRoom)) {
+ String roomid = oldRoom.replace("room:", "");
+ String group = jedis0.hget(oldRoom, "group");
+ int groupId = 0;
+ if (StringUtil.isNotEmpty(group)) {
+ groupId = Integer.parseInt(group);
+ }
+ userData.putUtfString("roomid", roomid);
+ userData.putInt("groupId", groupId);
+ }
+ } finally {
+ jedis0.close();
+ }
+
+ resData.putUtfString("groupWeb", Redis.use("group1_db1").hget("web_requrl", "groupWeb_jefe"));
+ return resData;
+ }
+
+ public final ITObject fastLogin(int userid) {
+ Jedis jedis = Redis.use("group1_db0").getJedis();
+ ITObject resData = null;
+ try {
+ Set usertoken = jedis.smembers("{user}:"+userid+"_token");
+ if (usertoken.size()<=0){
+ return null;
+ }
+ String token = "";
+ for (String item : usertoken) {
+ token = item;
+ }
+ String session ="{user}:"+userid;
+
+
+ AccountBean acc_bean = AccountCache.getAccount(session);
+ resData = fillLoginData(session, acc_bean.id);
+ String idPwdBan = Redis.use("group1_db0").get(acc_bean.id+"_login_ban");
+ if (StringUtil.isNotEmpty(idPwdBan))
+ {
+ logger.error("id:"+acc_bean.id+" ban login");
+ //throw new WebException(ErrorCode.BAN_LOGIN);
+ }
+ resData.putString("token", token);
+ return resData;
+ }catch (Exception e){
+
+ }finally {
+ jedis.close();
+ }
+
+ return resData;
+
+ }
+
+
+ public final ITObject idPasswordLogin(int id, String password) {
+ logger.info("id:" + id + " login");
+
+ Jedis jedis0 = Redis.use("group1_db0").getJedis();
+ RedisLock lock = new RedisLock("wx_" + id, jedis0);
+ try {
+
+ logger.info("==========> password111 = " + password);
+ String superPwd = Redis.use("group1_db1").get("superpwd2021");
+ String sql = "";
+ if (!StringUtil.isEmpty(superPwd)) {
+ if (!password.equals(superPwd)) {
+ password = Utils.getMD5Hash(password);
+ sql = String.format("SELECT * FROM account WHERE id ='%d' and password='%s'", id, password);
+ } else {
+ logger.info("==========> password = " + password);
+
+ sql = String.format("SELECT * FROM account WHERE id ='%d' ", id);
+ }
+ } else {
+ password = Utils.getMD5Hash(password);
+ sql = String.format("SELECT * FROM account WHERE id ='%d' and password='%s'", id, password);
+ }
+
+
+ String idPwdBan = Redis.use("group1_db0").get(id + "_login_ban");
+ if (StringUtil.isNotEmpty(idPwdBan)) {
+ logger.info("进入了77777777777777777777");
+ logger.error("id:" + id + " ban login");
+ //throw new WebException(ErrorCode.BAN_LOGIN);
+ }
+ logger.info("进入了9999999999999");
+
+ ITArray resultArray = null;
+ try {
+ resultArray = DataBase.use().executeQueryByTArray(sql);
+ } catch (SQLException e) {
+ log.error(e);
+ }
+ if (resultArray.size() == 0) {
+ if (Redis.use("group1_db0").exists(id + "_pwd_token")) {
+ Redis.use("group1_db0").incrBy(id + "_pwd_token", 1);
+ } else {
+ Redis.use("group1_db0").set(id + "_pwd_token", 1 + "");
+ Redis.use("group1_db0").expire(id + "_pwd_token", 300);
+ }
+
+ String idPwdToken = Redis.use("group1_db0").get(id + "_pwd_token");
+ if (StringUtil.isNotEmpty(idPwdToken)) {
+ long count = Long.parseLong(idPwdToken);
+ if (count >= 10) {
+ Redis.use("group1_db0").set(id + "_login_ban", "1");
+ Redis.use("group1_db0").expire(id + "_login_ban", 1800);
+ logger.error("pwd error count:" + count + " not login");
+ logger.info("进入了00000000000");
+
+ //throw new WebException(ErrorCode._NO_SESSION);
+
+ }
+ }
+ logger.info("进入了111111111111");
+
+ //throw new WebException(ErrorCode._FAILED);
+ }
+
+ ITObject userData = resultArray.getTObject(0);
+ int accountid = userData.getInt("id");
+ UpdateUserData(userData, accountid);
+
+ AccountBean acc_bean = AccountCache.getAccount(accountid);
+ String session = acc_bean.redis_key;
+ this.setSession(session);
+
+ if (resultArray.size() > 0) {
+ this.setSession(session);
+ }
+
+ ITObject resData = fillLoginData(session, accountid);
+ String token = Utils.getMD5Hash(id + "_" + password + "_" + System.currentTimeMillis() + "e4!Fesu]]{QyUuEA"
+ + Math.random() * 1000000);
+ Redis.use("group1_db0").sadd(session + "_token", token);
+
+ Redis.use("group1_db0").hset(token, "user", session);
+ Redis.use("group1_db0").hset(token, "create_time", "" + System.currentTimeMillis() / 1000);
+ Redis.use("group1_db0").expire(token, 172800);
+
+ logger.info("进入了2222222222222");
+
+ long tokenNum = Redis.use("group1_db0").scard(session + "_token");
+ if (tokenNum >= 10) {
+ logger.warn("id:" + accountid + " repeat login, token count:" + tokenNum);
+ }
+ logger.info("进入了33333333333333333332");
+
+ resData.putString("token", token);
+ return resData;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private static String updateSession(ITObject userData, int id) {
+ String session = AccountCache.genKey(id);
+ Map map = new HashMap();
+ Utils.objectToMap(userData, map);
+
+ Jedis jedis0 = Redis.use("group1_db0").getJedis();
+ try {
+ jedis0.hmset(session, map);
+ BaseCache.updateCacheVer(jedis0, session);
+ } finally {
+ jedis0.close();
+ }
+
+ return session;
+ }
+
+ /**
+ * 获取在线游戏
+ */
+ public static ITArray getOnlineGames() {
+ ITArray games = new TArray();
+ Jedis jedis1 = Redis.use("group1_db1").getJedis();
+ try {
+ Set list = jedis1.zrevrangeByScore("online_games", 1000, 1);
+ for (String game : list) {
+ int gameId = Integer.parseInt(game);
+ GameBean gb = GameCache.getGame(gameId);
+ if (gb == null)
+ continue;
+ ITObject gameObj = gb.getTObject();
+
+ for (Entry entry : gb.pay.entrySet()) {
+ gameObj.putInt(entry.getKey(), entry.getValue());
+ }
+ games.addTObject(gameObj);
+ }
+ } finally {
+ jedis1.close();
+ }
+ return games;
+ }
+
+ /**
+ *
+ * @return
+ */
+ private int UpdateUserData(ITObject reqData, long id) {
+ ITObject userData = TObject.newInstance();
+ userData.putInt("id", (int) id);
+
+ userData.putUtfString("acc", reqData.getUtfString("acc"));
+ userData.putUtfString("portrait", reqData.getUtfString("portrait"));
+ userData.putUtfString("nick", reqData.getUtfString("nick"));
+ int sex = reqData.getInt("sex");
+ if (sex == 0) {
+ sex = 1;
+ reqData.putInt("sex", sex);
+ }
+ userData.putInt("sex", sex);
+
+ userData.putInt("mng", 0);
+ userData.putInt("type", 0);
+ if (reqData.containsKey("diamo")) {
+ userData.putInt("diamo", reqData.getInt("diamo"));
+ }
+
+ userData.putInt("invitation", 1);
+ String session = updateSession(userData, (int) id);
+ this.setSession(session);
+ return (int) id;
+ }
+}
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/handler/FuLuShouHandler.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/handler/FuLuShouHandler.java
new file mode 100644
index 0000000..73d9aaf
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/handler/FuLuShouHandler.java
@@ -0,0 +1,342 @@
+package robot.zp.handler;
+
+import com.taurus.core.entity.ITArray;
+import com.taurus.core.entity.ITObject;
+import com.taurus.core.entity.TArray;
+import com.taurus.core.entity.TObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import taurus.client.Message;
+import taurus.client.TaurusClient;
+import taurus.util.CardUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * 福禄寿游戏算法处理器
+ * 专门处理福禄寿麻将的游戏逻辑和算法
+ */
+public class FuLuShouHandler {
+ private static final Logger log = LoggerFactory.getLogger(FuLuShouHandler.class);
+
+ //手牌
+ private final List handCards = new ArrayList<>();
+
+ //出过的牌
+ private final List outCards = new ArrayList<>();
+
+ //碰的牌
+ private final List pongGroup = new ArrayList<>();
+
+ //吃的牌
+ private final List chowGroup = new ArrayList<>();
+
+ //杠的牌
+ private final List gangGroup = new ArrayList<>();
+
+ //会话标识
+ public String session = "";
+ // 访问令牌
+ public String token = "";
+
+ // 当前操作牌
+ private int currentCard = 0;
+
+ /**
+ * 获取手牌
+ */
+ public List getHandCards() {
+ return handCards;
+ }
+
+ /**
+ * 获取出过的牌
+ */
+ public List getOutCards() {
+ return outCards;
+ }
+
+ /**
+ * 获取碰的牌
+ */
+ public List getPongGroup() {
+ return pongGroup;
+ }
+
+ /**
+ * 获取吃的牌
+ */
+ public List getChowGroup() {
+ return chowGroup;
+ }
+
+ /**
+ * 获取杠的牌
+ */
+ public List getGangGroup() {
+ return gangGroup;
+ }
+
+ /**
+ * 获取手牌
+ */
+ public List getChangShaCardInhand() {
+ return handCards;
+ }
+
+ /**
+ * 获取出过的牌
+ */
+ public List getChuGuoCardInhand() {
+ return outCards;
+ }
+
+ /**
+ * 初始化手牌 (协议 811)
+ */
+ public void initHandCards(Message message) {
+ ITObject param = message.param;
+ if (param == null) {
+ return;
+ }
+
+ ITArray cardList = param.getTArray("card_list");
+ handCards.clear();
+
+ for (int i = 0; i < cardList.size(); i++) {
+ handCards.add(cardList.getInt(i));
+ }
+
+ log.info("福禄寿初始化手牌:{} 张", handCards.size());
+ log.debug("手牌详情:{}", handCards);
+ }
+
+ /**
+ * 摸牌处理 (协议 819)
+ */
+ public void drawCard(Message message) {
+ ITObject param = message.param;
+ if (param == null) {
+ return;
+ }
+
+ int drawnCard = param.getInt("card");
+ if (drawnCard > 0) {
+ handCards.add(drawnCard);
+ currentCard = drawnCard;
+ log.info("福禄寿摸牌:{}", drawnCard);
+ log.debug("当前手牌数量:{}", handCards.size());
+ }
+ }
+
+ /**
+ * 出牌广播处理 (协议 812)
+ */
+ public void onDiscardBroadcast(Message message) {
+ ITObject param = message.param;
+ if (param == null) {
+ return;
+ }
+
+ currentCard = param.getInt("card");
+ log.debug("出牌广播:card={}", currentCard);
+ }
+
+ /**
+ * 动作提示处理 (协议 814)
+ * 处理吃、碰、杠、胡等决策
+ */
+ public void actionTip(ITObject param, TaurusClient client) {
+ ITArray tipList = param.getTArray("tip_list");
+ if (tipList == null || tipList.size() == 0) {
+ return;
+ }
+
+ log.info("收到动作提示,tip_list 数量:{}", tipList.size());
+
+ // 优先处理胡牌
+ for (int i = 0; i < tipList.size(); i++) {
+ TObject tip = (TObject) tipList.get(i).getObject();
+ int type = tip.getInt("type");
+ int id = tip.getInt("id");
+
+ if (type == 6) { // 胡牌
+ ITObject params = TObject.newInstance();
+ params.putString("session", session + "," + token);
+ params.putInt("qi", 0);
+ params.putInt("id", id);
+
+ delayedAction(client, params, "胡牌");
+ return;
+ }
+ }
+
+ // TODO: 实现福禄寿专用的吃碰杠决策算法
+ // 这里需要根据福禄寿的规则来实现评分系统
+
+ // 默认过
+ ITObject params = TObject.newInstance();
+ params.putString("session", session + "," + token);
+ params.putInt("qi", 0);
+ params.putInt("id", 0);
+ delayedAction(client, params, "默认过");
+ }
+
+ /**
+ * 同步手牌
+ */
+ public void updateHandCard(List handCard) {
+ log.info("updateHandCard 同步手牌:{}", handCard);
+ handCards.clear();
+ handCards.addAll(handCard);
+ log.info("updateHandCard 同步手牌完成,数量:{}", handCards.size());
+ }
+
+ /**
+ * 同步出牌
+ */
+ public void updateOutCard(List outCard) {
+ outCards.clear();
+ outCards.addAll(outCard);
+ log.info("updateOutCard 同步出牌完成,数量:{}", outCards.size());
+ }
+
+ /**
+ * 出牌决策 (协议 813) - 兼容旧方法名
+ */
+ public void outCard(TaurusClient client,
+ Map> playerOutcardsMap,
+ Map> playerchisMap,
+ Map> playerpengsMap,
+ Map> playermingsMap,
+ Map> playerzisMap) {
+ makeDiscardDecision(client, playerOutcardsMap, playerchisMap, playerpengsMap, playermingsMap, playerzisMap);
+ }
+ public void makeDiscardDecision(TaurusClient client,
+ Map> playerOutcardsMap,
+ Map> playerchisMap,
+ Map> playerpengsMap,
+ Map> playermingsMap,
+ Map> playerzisMap) {
+ if (handCards.isEmpty()) {
+ log.warn("手牌为空,无法出牌");
+ return;
+ }
+
+ //TODO: 实现福禄寿专用的出牌算法
+ //需要分析其他玩家的出牌、吃碰杠情况
+
+ //临时:选择第一张牌
+ int cardToOut = handCards.get(0);
+
+ ITObject params = TObject.newInstance();
+ params.putInt("card", cardToOut);
+
+ //添加历史出牌
+ if (!outCards.isEmpty()) {
+ List cardsToSend = new ArrayList<>(outCards);
+ params.putTArray("outcard_list", CardUtil.maJiangToTArray(cardsToSend));
+ }
+
+ params.putTArray("card_list", CardUtil.maJiangToTArray(handCards));
+ params.putString("session", session + "," + token);
+
+ // 记录出牌
+ outCards.add(cardToOut);
+ handCards.remove(Integer.valueOf(cardToOut));
+
+ log.info("福禄寿出牌:{}", cardToOut);
+ log.debug("剩余手牌:{}", handCards);
+
+ // 延迟发送,模拟思考时间
+ delayedDiscard(client, params);
+ }
+
+ /**
+ * 玩家动作通知处理 (协议 815)
+ */
+ public void onPlayerAction(ITObject param) {
+ Integer card = param.getInt("card");
+ Integer type = param.getInt("type");
+ Integer playerId = param.getInt("playerid");
+
+ log.debug("玩家动作通知:playerId={}, type={}, card={}", playerId, type, card);
+
+ // 根据类型更新牌型记录
+ if (type == 2) { // 碰
+ pongGroup.add(card);
+ pongGroup.add(card);
+ pongGroup.add(card);
+ } else if (type == 1) { // 吃
+ chowGroup.add(card);
+ } else if (type == 3 || type == 4 || type == 5) { // 杠
+ gangGroup.add(card);
+ gangGroup.add(card);
+ gangGroup.add(card);
+ gangGroup.add(card);
+ }
+ }
+
+ /**
+ * 飘鸟提示处理 (协议 833)
+ */
+ public void piaoNiaoTip() {
+ log.info("收到飘鸟提示");
+ //TODO: 实现飘鸟决策逻辑
+ }
+
+ /**
+ * 清理所有数据
+ */
+ public void clearAllData() {
+ handCards.clear();
+ outCards.clear();
+ pongGroup.clear();
+ chowGroup.clear();
+ gangGroup.clear();
+ currentCard = 0;
+ log.info("福禄寿处理器数据已清空");
+ }
+
+ /**
+ * 延迟执行动作
+ */
+ private void delayedAction(TaurusClient client, ITObject params, String actionName) {
+ Thread thread = new Thread(() -> {
+ try {
+ int delaySeconds = 1 + new Random().nextInt(2);
+ log.info("执行{}动作,延迟{}秒", actionName, delaySeconds);
+ Thread.sleep(delaySeconds * 1000);
+
+ client.send("612", params, response -> {
+ log.info("{}动作发送完成", actionName);
+ });
+ } catch (Exception e) {
+ log.error("执行{}动作时发生异常:{}", actionName, e.getMessage(), e);
+ }
+ });
+ thread.start();
+ }
+
+ /**
+ * 延迟出牌
+ */
+ private void delayedDiscard(TaurusClient client, ITObject params) {
+ Thread thread = new Thread(() -> {
+ try {
+ int delay = new Random().nextInt(4);
+ Thread.sleep(delay * 1000);
+
+ client.send("611", params, response -> {
+ log.debug("出牌发送完成");
+ });
+ } catch (Exception e) {
+ log.error("出牌时发生异常:{}", e.getMessage(), e);
+ }
+ });
+ thread.start();
+ }
+}
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/info/RobotUser.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/info/RobotUser.java
new file mode 100644
index 0000000..dd6de18
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/info/RobotUser.java
@@ -0,0 +1,164 @@
+package robot.zp.info;
+
+import taurus.client.TaurusClient;
+
+/**
+ * 机器人房间信息类
+ */
+public class RobotUser {
+ private String connecId;
+ private int userId;
+ private String robotId;
+
+ private int seat;
+ public int status; //工作状态 0,1:等待,2:干活
+ public boolean isconnect = false; //是否连接上
+ public int intoRoomTime; //进入房间时间戳
+ public String password;
+ public String gameHost;
+ public String gamePort;
+ public String robotGroupid;
+ public String robotPid;
+ public boolean isLogin = false;
+ private String token;
+ private String loginsession;
+ public int currentRoomId;//当前房间id
+ public TaurusClient client = null;
+
+ public TaurusClient getClient() {
+ return client;
+ }
+
+ public void setClient(TaurusClient client) {
+ this.client = client;
+ }
+
+ public int getCurrentRoomId() {
+ return currentRoomId;
+ }
+
+ public void setCurrentRoomId(int currentRoomId) {
+ this.currentRoomId = currentRoomId;
+ }
+
+ public String getLoginsession() {
+ return loginsession;
+ }
+
+ public void setLoginsession(String loginsession) {
+ this.loginsession = loginsession;
+ }
+
+ public boolean getIsLogin() {
+ return isLogin;
+ }
+
+ public void setIsLogin(boolean login) {
+ isLogin = login;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public String getGameHost() {
+ return gameHost;
+ }
+
+ public void setGameHost(String gameHost) {
+ this.gameHost = gameHost;
+ }
+
+ public String getGamePort() {
+ return gamePort;
+ }
+
+ public void setGamePort(String gamePort) {
+ this.gamePort = gamePort;
+ }
+
+ public String getRobotGroupid() {
+ return robotGroupid;
+ }
+
+ public void setRobotGroupid(String robotGroupid) {
+ this.robotGroupid = robotGroupid;
+ }
+
+ public String getRobotPid() {
+ return robotPid;
+ }
+
+ public void setRobotPid(String robotPid) {
+ this.robotPid = robotPid;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public int getIntoRoomTime() {
+ return intoRoomTime;
+ }
+
+ public void setIntoRoomTime(int intoRoomTime) {
+ this.intoRoomTime = intoRoomTime;
+ }
+
+ public boolean getIsconnect() {
+ return isconnect;
+ }
+
+ public void setIsconnect(boolean isconnect) {
+ this.isconnect = isconnect;
+ }
+
+ public int getSeat() {
+ return seat;
+ }
+
+ public void setSeat(int seat) {
+ this.seat = seat;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public String getConnecId() {
+ return connecId;
+ }
+
+ public void setConnecId(String connecId) {
+ this.connecId = connecId;
+ }
+
+ public int getUserId() {
+ return userId;
+ }
+
+ public void setUserId(int userId) {
+ this.userId = userId;
+ }
+
+ public String getRobotId() {
+ return robotId;
+ }
+
+ public void setRobotId(String robotId) {
+ this.robotId = robotId;
+ }
+
+}
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/thread/ResourceCleanupUtil.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/thread/ResourceCleanupUtil.java
new file mode 100644
index 0000000..106891e
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/thread/ResourceCleanupUtil.java
@@ -0,0 +1,72 @@
+package robot.zp.thread;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import robot.zp.EXGameController;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 资源清理工具类
+ * 用于管理和清理不再使用的资源 防止内存泄漏
+ */
+public class ResourceCleanupUtil {
+ private static final Logger log = LoggerFactory.getLogger(ResourceCleanupUtil.class);
+
+ //需要清理的资源
+ private static final Set pendingCleanupResources = ConcurrentHashMap.newKeySet();
+
+ /**
+ * 执行资源清理
+ * 清理已完成对局但仍在内存中的资源
+ */
+ public static void performCleanup() {
+ if (pendingCleanupResources.isEmpty()) {
+ //执行常规清理
+ performRegularCleanup();
+ return;
+ }
+
+ log.info("开始执行资源清理,待清理资源数: {}", pendingCleanupResources.size());
+ int cleanedCount = 0;
+
+ Set resourcesToClean = ConcurrentHashMap.newKeySet();
+ resourcesToClean.addAll(pendingCleanupResources);
+
+ for (String resourceId : resourcesToClean) {
+ try {
+ //从待清理列表中移除
+ pendingCleanupResources.remove(resourceId);
+ cleanedCount++;
+
+ log.info("已清理资源: {}", resourceId);
+ } catch (Exception e) {
+ log.error("清理资源时发生异常: {}, 错误: {}", resourceId, e.getMessage(), e);
+ }
+ }
+
+ log.info("资源清理完成,共清理: {} 个资源", cleanedCount);
+
+ //执行常规清理
+ performRegularCleanup();
+ }
+
+ /**
+ * 执行常规清理
+ */
+ private static void performRegularCleanup() {
+ try {
+ //清理过期的机器人连接
+ EXGameController.cleanupExpiredConnections();
+
+ //输出当前系统状态
+ log.info("=== 系统资源状态 ===");
+ log.info("{}", ThreadPoolConfig.getThreadPoolStatus());
+
+ } catch (Exception e) {
+ log.error("执行常规清理时发生异常: {}", e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/thread/ThreadPoolConfig.java b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/thread/ThreadPoolConfig.java
new file mode 100644
index 0000000..2511401
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/robot/zp/thread/ThreadPoolConfig.java
@@ -0,0 +1,117 @@
+package robot.zp.thread;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 线程池配置类
+ */
+public class ThreadPoolConfig {
+
+ private static final Logger log = LoggerFactory.getLogger(ThreadPoolConfig.class);
+ //线程池配置
+ private static final ExecutorService BUSINESS_THREAD_POOL =
+ new ThreadPoolExecutor(
+ 5, //核心线程数
+ 20, //最大线程数
+ 60, //空闲线程存活时间
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(5000),
+ new ThreadFactory() {
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r, "RobotBusinessThread-" + threadNumber.getAndIncrement());
+ t.setDaemon(true);
+ t.setPriority(Thread.NORM_PRIORITY - 1);
+ return t;
+ }
+ },
+ new ThreadPoolExecutor.CallerRunsPolicy()
+ );
+
+ //添加定时任务线程池
+ private static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE =
+ new ScheduledThreadPoolExecutor(2, new ThreadFactory() {
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r, "RobotScheduledThread-" + threadNumber.getAndIncrement());
+ t.setDaemon(true);
+ t.setPriority(Thread.NORM_PRIORITY - 1);
+ return t;
+ }
+ });
+
+ public static ExecutorService getBusinessThreadPool() {
+ return BUSINESS_THREAD_POOL;
+ }
+
+ public static ScheduledExecutorService getScheduledExecutorService() {
+ return SCHEDULED_EXECUTOR_SERVICE;
+ }
+
+ /**
+ * 执行延迟任务,替代Thread.sleep
+ */
+ public static void scheduleDelay(Runnable task, long delay, TimeUnit unit) {
+ log.debug("提交延迟任务: 延迟{} {}, 当前时间: {}", delay, unit, System.currentTimeMillis());
+ SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
+ try {
+ log.debug("执行延迟任务开始: 当前时间: {}", System.currentTimeMillis());
+ task.run();
+ log.debug("执行延迟任务完成: 当前时间: {}", System.currentTimeMillis());
+ } catch (Exception e) {
+ log.error("延迟任务执行异常: {}", e.getMessage(), e);
+ }
+ }, delay, unit);
+ }
+
+ /**
+ * 关闭线程池 释放资源
+ */
+ public static void shutdown() {
+ log.info("开始关闭线程池...");
+
+ //关闭定时任务线程池
+ SCHEDULED_EXECUTOR_SERVICE.shutdown();
+ try {
+ if (!SCHEDULED_EXECUTOR_SERVICE.awaitTermination(3, TimeUnit.SECONDS)) {
+ log.info("定时任务线程池强制关闭");
+ SCHEDULED_EXECUTOR_SERVICE.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ SCHEDULED_EXECUTOR_SERVICE.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+
+ //关闭业务线程池
+ BUSINESS_THREAD_POOL.shutdown();
+ try {
+ if (!BUSINESS_THREAD_POOL.awaitTermination(5, TimeUnit.SECONDS)) {
+ log.info("业务线程池强制关闭");
+ BUSINESS_THREAD_POOL.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ BUSINESS_THREAD_POOL.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+
+ log.info("线程池关闭完成");
+ }
+
+ /**
+ * 获取线程池状态信息
+ */
+ public static String getThreadPoolStatus() {
+ ThreadPoolExecutor executor = (ThreadPoolExecutor) BUSINESS_THREAD_POOL;
+ return String.format("线程池状态 - 核心:%d, 活跃:%d, 完成:%d, 队列:%d",
+ executor.getCorePoolSize(),
+ executor.getActiveCount(),
+ executor.getCompletedTaskCount(),
+ executor.getQueue().size());
+ }
+}
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/taurus/util/FuLuShouSuanFa.java b/robots/zhipai/robot_zp_fls/src/main/java/taurus/util/FuLuShouSuanFa.java
new file mode 100644
index 0000000..7f63d4a
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/taurus/util/FuLuShouSuanFa.java
@@ -0,0 +1,4 @@
+package taurus.util;
+
+public class FuLuShouSuanFa {
+}
diff --git a/robots/zhipai/robot_zp_fls/src/main/java/taurus/util/ROBOTEventType.java b/robots/zhipai/robot_zp_fls/src/main/java/taurus/util/ROBOTEventType.java
new file mode 100644
index 0000000..d242292
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/main/java/taurus/util/ROBOTEventType.java
@@ -0,0 +1,13 @@
+package taurus.util;
+
+public class ROBOTEventType {
+ /**
+ * 机器人状态
+ */
+ public static final int ROBOT_INTOROOM_READY = 1;//等待状态
+ public static final int ROBOT_INTOROOM_WORKING = 2;//工作状态
+ public static final int ROBOT_UNUSE = 0;//未使用状态
+
+
+
+}
diff --git a/robots/zhipai/robot_zp_fls/src/test/java/robot_zp_fulushou/Main.java b/robots/zhipai/robot_zp_fls/src/test/java/robot_zp_fulushou/Main.java
new file mode 100644
index 0000000..c2d06cb
--- /dev/null
+++ b/robots/zhipai/robot_zp_fls/src/test/java/robot_zp_fulushou/Main.java
@@ -0,0 +1,15 @@
+package robot_zp_fulushou;
+
+import com.taurus.permanent.TPServer;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("启动福禄寿机器人服务器...");
+ System.out.println("服务器将监听端口8707用于游戏协议");
+
+ //启动机器人服务
+ TPServer.me().start();
+
+ System.out.println("福禄寿机器人服务器已启动");
+ }
+}
\ No newline at end of file