init福禄寿

master
zhouwei 2026-03-04 18:58:13 +08:00
parent d7f8ae74ce
commit 8e888698e6
26 changed files with 2824 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="robot_zp_fulushou" />
<module name="robot_mj_changsha" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/nexus/content/groups/public" />
</remote-repository>
</component>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/pom.xml" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../../.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
</component>
</project>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<gameSetting>
<host>8.134.76.43</host>
<intranet>8.134.76.43</intranet>
<port>8707</port>
<serverId>8707</serverId>
<gameId>107</gameId>
<loggerDebug>true</loggerDebug>
</gameSetting>

View File

@ -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

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<serivce-core>
<log4jPath>log4j.properties</log4jPath>
<plugin>
<id>redis</id>
<class>com.taurus.core.plugin.redis.RedisPlugin</class>
<poolConfig>
<!-- 最大连接数, 默认8个 -->
<maxTotal>200</maxTotal>
<!-- 最大空闲连接数, 默认8个 -->
<maxIdle>50</maxIdle>
<!-- 最小空闲连接数, 默认0个 -->
<minIdle>20</minIdle>
<!-- 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1 -->
<maxWaitMillis>2000</maxWaitMillis>
<!-- 在borrow一个jedis实例时是否提前进行alidate操作, 默认false -->
<testOnBorrow>false</testOnBorrow>
<!-- 在return给pool时是否提前进行validate操作, 默认false -->
<testOnReturn>false</testOnReturn>
<!-- 表示有一个idle object evitor线程对idle object进行扫描如果validate失败
此object会被从pool中drop掉这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义, 默认true -->
<testWhileIdle>true</testWhileIdle>
<!-- 表示idle object evitor每次扫描的最多的对象数, 默认-1 -->
<numTestsPerEvictionRun>100</numTestsPerEvictionRun>
<!-- 表示一个对象至少停留在idle状态的最短时间然后才能被idle object evitor扫描并驱逐
这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义 , 默认60000-->
<minEvictableIdleTimeMillis>60000</minEvictableIdleTimeMillis>
<!-- 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认30000 -->
<timeBetweenEvictionRunsMillis>30000</timeBetweenEvictionRunsMillis>
<!-- 在minEvictableIdleTimeMillis基础上加入了至少minIdle个对象已经在pool里面了。
如果为-1evicted不会根据idle time驱逐任何对象。如果minEvictableIdleTimeMillisd大于0
则此项设置无意义且只有在timeBetweenEvictionRunsMillis大于0时才有意义默认1800000 -->
<softMinEvictableIdleTimeMillis>1800000</softMinEvictableIdleTimeMillis>
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<blockWhenExhausted>true</blockWhenExhausted>
</poolConfig>
<infos>
<info name="group1_db0" host="8.134.76.43" password="cssq@2020" port="6379" database="0" timeout="5000"/>
<info name="group1_db1" host="8.134.76.43" password="cssq@2020" port="6379" database="1" timeout="5000"/>
<info name="group1_db2" host="8.134.76.43" password="cssq@2020" port="6379" database="2" timeout="5000"/>
<info name="group1_db5" host="8.134.76.43" password="cssq@2020" port="6379" database="5" timeout="5000"/>
<info name="group1_db8" host="8.134.76.43" password="cssq@2020" port="6379" database="8" timeout="5000"/>
<info name="group1_db9" host="8.134.76.43" password="cssq@2020" port="6379" database="9" timeout="5000"/>
<info name="group1_db10" host="8.134.76.43" password="cssq@2020" port="6379" database="10" timeout="5000"/>
<info name="group1_db11" host="8.134.76.43" password="cssq@2020" port="6379" database="11" timeout="5000"/>
<info name="tmp_group1_db9" host="8.134.76.43" password="654sads" port="6479" database="9" timeout="5000"/>
</infos>
<!--<infos>
<info name="group1_db0" host="127.0.0.1" port="6379" database="0" timeout="5000"/>
<info name="group1_db1" host="127.0.0.1" port="6379" database="1" timeout="5000"/>
<info name="group1_db2" host="127.0.0.1" port="6379" database="2" timeout="5000"/>
<info name="group1_db5" host="127.0.0.1" port="6379" database="5" timeout="5000"/>
<info name="group1_db8" host="127.0.0.1" port="6379" database="8" timeout="5000"/>
<info name="group1_db9" host="127.0.0.1" port="6379" database="9" timeout="5000"/>
<info name="group1_db10" host="127.0.0.1" port="6379" database="10" timeout="5000"/>
<info name="group1_db11" host="127.0.0.1" port="6379" database="11" timeout="5000"/>
</infos>-->
</plugin>
</serivce-core>

View File

@ -0,0 +1,75 @@
<server>
<!-- 计时器线程池大小-->
<timerThreadPoolSize>1</timerThreadPoolSize>
<!-- 协议包压缩门槛 (单位字节),当协议包大于设定的值则会进行压缩 -->
<protocolCompression>512</protocolCompression>
<!-- 读缓冲区类型 nio Direct Buffer 或者 Heap Buffer-->
<readBufferType>Heap</readBufferType>
<!-- 写入冲区类型 nio Direct Buffer 或者 Heap Buffer-->
<writeBufferType>Heap</writeBufferType>
<!-- 最大的数据包大小 -->
<maxPacketSize>524288</maxPacketSize>
<!-- 最大读取缓存大小 -->
<maxReadBufferSize>16384</maxReadBufferSize>
<!-- 最大写入缓存大小 -->
<maxWriteBufferSize>32768</maxWriteBufferSize>
<!-- 会话队列的大小-->
<sessionPacketQueueSize>512</sessionPacketQueueSize>
<!-- Thread Pool Size of the 3 main stages of the Bitswarm Engine -->
<socketAcceptorThreadPoolSize>4</socketAcceptorThreadPoolSize>
<socketReaderThreadPoolSize>2</socketReaderThreadPoolSize>
<socketWriterThreadPoolSize>2</socketWriterThreadPoolSize>
<!-- Enable disable Nagle algorithm on sockets, true == disable -->
<tcpNoDelay>true</tcpNoDelay>
<!-- 会话超时时间(单位秒)-->
<sessionTimeout>300</sessionTimeout>
<!-- Bind socket addresses -->
<socketAddresses>
<socket address="0.0.0.0" port="8707" type="TCP" />
</socketAddresses>
<!-- Ip addresses filter-->
<ipFilter>
<addressBlackList>
<string>1.2.3.4</string>
</addressBlackList>
<addressWhiteList>
<string>127.0.0.1</string>
</addressWhiteList>
<maxConnectionsPerAddress>10000</maxConnectionsPerAddress>
</ipFilter>
<webSocket>
<isActive>false</isActive>
<address>0.0.0.0</address>
<port>80</port>
</webSocket>
<!-- Main extension class -->
<extensionConfig>
<name>robot - test</name>
<className>robot.zp.EXMainServer</className>
</extensionConfig>
<!-- The system thread pool config -->
<systemThreadPoolConfig>
<name>Sys</name>
<corePoolSize>16</corePoolSize>
<maxPoolSize>32</maxPoolSize>
<keepAliveTime>60000</keepAliveTime>
<maxQueueSize>5000</maxQueueSize>
</systemThreadPoolConfig>
<!-- The extension thread pool config -->
<extensionThreadPoolConfig>
<name>Ext</name>
<corePoolSize>16</corePoolSize>
<maxPoolSize>32</maxPoolSize>
<keepAliveTime>60000</keepAliveTime>
<maxQueueSize>5000</maxQueueSize>
</extensionThreadPoolConfig>
</server>

View File

@ -0,0 +1,47 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.robot</groupId>
<artifactId>robot_zp_fulushou</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>robot_zp_fulushou</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.robot</groupId>
<artifactId>robot_common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<finalName>robot</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<!-- <compilerArgument>-parameters</compilerArgument> -->
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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";
}

View File

@ -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<String, RobotUser> robotRoomMapping = new ConcurrentHashMap<>();
//记录最近访问时间
private static final Map<String, Long> 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<RobotUser> 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<RobotUser> 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<String> 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<String> 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);
}
/**
* IDRobotUser
*/
public List<RobotUser> getRobotUsersByRoomId(int roomId) {
String prefix = roomId + "_";
List<RobotUser> result = new ArrayList<>();
for (Map.Entry<String, RobotUser> 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<String> expiredConnections = new ArrayList<>();
for (Map.Entry<String, Long> 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();
}
}
/**
* IDID寿
* robotIdconnectionId
*/
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;
}
}

View File

@ -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;
/**
* 寿
* TCProbot_mgr game_zp_flsAI
*/
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<String, RobotUser> 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<String, String> maprobot = jedis2.hgetAll(robotskey);
for(Map.Entry<String, String> 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<String, RobotUser> 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<String, String> 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("福禄寿机器人服务器已停止");
}
}

View File

@ -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;
}
}

View File

@ -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<String, String> 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();
}
}

View File

@ -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<String, FuLuShouHandler> fuLuShouHandlerInstances = new ConcurrentHashMap<>();
//记录活跃连接 用于资源清理判断
private static final Set<String> activeConnections = ConcurrentHashMap.newKeySet();
//记录连接创建时间
private static final Map<String, Long> 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<String, Map<Integer, List<Integer>>> playerOutcardsMapByConn = new ConcurrentHashMap<>();
private final Map<String, Map<Integer, List<Integer>>> playerchisMapByConn = new ConcurrentHashMap<>();
private final Map<String, Map<Integer, List<Integer>>> playerpengsMapByConn = new ConcurrentHashMap<>();
private final Map<String, Map<Integer, List<Integer>>> playermingsMapByConn = new ConcurrentHashMap<>();
private final Map<String, Map<Integer, List<Integer>>> playerzisMapByConn = new ConcurrentHashMap<>();
private Map<Integer, List<Integer>> getPlayerOutcardsMap(String connecId) {
return playerOutcardsMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
}
private Map<Integer, List<Integer>> getPlayerchisMap(String connecId) {
return playerchisMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
}
private Map<Integer, List<Integer>> getPlayerpengsMap(String connecId) {
return playerpengsMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
}
private Map<Integer, List<Integer>> getPlayermingsMap(String connecId) {
return playermingsMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
}
private Map<Integer, List<Integer>> getPlayerzisMap(String connecId) {
return playerzisMapByConn.computeIfAbsent(connecId, k -> new ConcurrentHashMap<>());
}
private int pid = 0;
private Map<Integer, Integer> count = new HashMap<Integer, Integer>();
/*福禄寿游戏算法相关 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<String> expiredConnections = new ArrayList<>();
//检查连接生存时间
for (Map.Entry<String, Long> 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<Integer> 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<Integer> 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<Integer> outcards = new ArrayList<>();
for (int i = 0; i < outcard_list.size(); i++) {
outcards.add(outcard_list.getInt(i));
}
//检查出牌记录是否需要同步
List<Integer> 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<Integer, List<Integer>> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId);
Map<Integer, List<Integer>> currentPlayerchisMap = getPlayerchisMap(connecId);
Map<Integer, List<Integer>> currentPlayerpengsMap = getPlayerpengsMap(connecId);
Map<Integer, List<Integer>> currentPlayermingsMap = getPlayermingsMap(connecId);
Map<Integer, List<Integer>> 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<Integer, List<Integer>> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId);
Map<Integer, List<Integer>> currentPlayerchisMap = getPlayerchisMap(connecId);
Map<Integer, List<Integer>> currentPlayerpengsMap = getPlayerpengsMap(connecId);
Map<Integer, List<Integer>> currentPlayermingsMap = getPlayermingsMap(connecId);
Map<Integer, List<Integer>> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer, List<Integer>> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId);
Map<Integer, List<Integer>> currentPlayerchisMap = getPlayerchisMap(connecId);
Map<Integer, List<Integer>> currentPlayerpengsMap = getPlayerpengsMap(connecId);
Map<Integer, List<Integer>> currentPlayermingsMap = getPlayermingsMap(connecId);
Map<Integer, List<Integer>> 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发送退出房间协议1005robotId: {}", 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发送退出房间协议1005robotId: {}", 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发送退出房间协议1005robotId: {}", 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<MessageResponse>() {
@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) {
}
}
}

View File

@ -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<String, String> 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<String> 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<String, String> map = new HashMap<String, String>();
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<String> 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<String, Integer> 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;
}
}

View File

@ -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<Integer> handCards = new ArrayList<>();
//出过的牌
private final List<Integer> outCards = new ArrayList<>();
//碰的牌
private final List<Integer> pongGroup = new ArrayList<>();
//吃的牌
private final List<Integer> chowGroup = new ArrayList<>();
//杠的牌
private final List<Integer> gangGroup = new ArrayList<>();
//会话标识
public String session = "";
// 访问令牌
public String token = "";
// 当前操作牌
private int currentCard = 0;
/**
*
*/
public List<Integer> getHandCards() {
return handCards;
}
/**
*
*/
public List<Integer> getOutCards() {
return outCards;
}
/**
*
*/
public List<Integer> getPongGroup() {
return pongGroup;
}
/**
*
*/
public List<Integer> getChowGroup() {
return chowGroup;
}
/**
*
*/
public List<Integer> getGangGroup() {
return gangGroup;
}
/**
*
*/
public List<Integer> getChangShaCardInhand() {
return handCards;
}
/**
*
*/
public List<Integer> 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<Integer> handCard) {
log.info("updateHandCard 同步手牌:{}", handCard);
handCards.clear();
handCards.addAll(handCard);
log.info("updateHandCard 同步手牌完成,数量:{}", handCards.size());
}
/**
*
*/
public void updateOutCard(List<Integer> outCard) {
outCards.clear();
outCards.addAll(outCard);
log.info("updateOutCard 同步出牌完成,数量:{}", outCards.size());
}
/**
* ( 813) -
*/
public void outCard(TaurusClient client,
Map<Integer, List<Integer>> playerOutcardsMap,
Map<Integer, List<Integer>> playerchisMap,
Map<Integer, List<Integer>> playerpengsMap,
Map<Integer, List<Integer>> playermingsMap,
Map<Integer, List<Integer>> playerzisMap) {
makeDiscardDecision(client, playerOutcardsMap, playerchisMap, playerpengsMap, playermingsMap, playerzisMap);
}
public void makeDiscardDecision(TaurusClient client,
Map<Integer, List<Integer>> playerOutcardsMap,
Map<Integer, List<Integer>> playerchisMap,
Map<Integer, List<Integer>> playerpengsMap,
Map<Integer, List<Integer>> playermingsMap,
Map<Integer, List<Integer>> 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<Integer> 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();
}
}

View File

@ -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;
}
}

View File

@ -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<String> pendingCleanupResources = ConcurrentHashMap.newKeySet();
/**
*
*
*/
public static void performCleanup() {
if (pendingCleanupResources.isEmpty()) {
//执行常规清理
performRegularCleanup();
return;
}
log.info("开始执行资源清理,待清理资源数: {}", pendingCleanupResources.size());
int cleanedCount = 0;
Set<String> 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);
}
}
}

View File

@ -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());
}
}

View File

@ -0,0 +1,4 @@
package taurus.util;
public class FuLuShouSuanFa {
}

View File

@ -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;//未使用状态
}

View File

@ -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("福禄寿机器人服务器已启动");
}
}