diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java index 9cec950..6cbf720 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXGameController.java @@ -22,6 +22,9 @@ 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.mj.thread.ThreadPoolConfig.scheduleDelay; /** * 长沙麻将游戏控制器 - 处理游戏协议 @@ -150,7 +153,10 @@ public class EXGameController extends GameController { GroupRoomBusiness.joinRoom(groupId, roomId, robotSession, null); - Thread.sleep(5000); + //非阻塞延迟替代Thread.sleep + scheduleDelay(() -> { + + }, 5, TimeUnit.SECONDS); params.del("groupId"); params.del("roomId"); @@ -347,7 +353,10 @@ public class EXGameController extends GameController { //先不放入映射 等确认加入成功后再放入 //robotRoomMapping.put(robotUser.getConnecId(), robotUser); robotRoomMapping.remove(robotUser.getRobotId()); - Thread.sleep(2000); + //非阻塞延迟替代Thread.sleep + scheduleDelay(() -> { + + }, 2, TimeUnit.SECONDS); params.putString("session", "{user}:" + robotId + "," + robotSession); //发送加入房间请求到game_mj_cs @@ -371,14 +380,21 @@ public class EXGameController extends GameController { //添加超时检查机制 CompletableFuture.runAsync(() -> { try { - Thread.sleep(15000); - //10秒后还没有建立映射关系 加入可能失败 + //定时任务替代Thread.sleep + scheduleDelay(() -> { + //15秒后还没有建立映射关系 加入可能失败 + if (robotRoomMapping.get(robotUser.getConnecId()) == null) { + System.err.println("机器人{"+robotId+"}加入房间{"+roomId+"}超时,清理临时状态"); + robotConnectionManager.disconnectFromGameServer(connecId); + } + }, 15, TimeUnit.SECONDS); + //15秒后还没有建立映射关系 加入可能失败 if (robotRoomMapping.get(robotUser.getConnecId()) == null) { System.err.println("机器人{"+robotId+"}加入房间{"+roomId+"}超时,清理临时状态"); robotConnectionManager.disconnectFromGameServer(connecId); } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + } catch (Exception e) { + log.error("机器人加入房间超时", e); } }); robotUser.setIntoRoomTime(robotConnectionManager.getTime()); diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java index 23c8955..59177a6 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/EXMainServer.java @@ -28,6 +28,27 @@ public class EXMainServer extends MainServer{ 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.mj.thread.ThreadPoolConfig.shutdown(); + + log.info("优雅关闭完成"); + } catch (Exception e) { + log.error("关闭过程中发生异常", e); + } + })); + // 1. 先启动独立的事件处理线程(只启动一次) startNetEventThread(); diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java index 282c816..0d25cca 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/RobotConnectionManager.java @@ -12,6 +12,7 @@ import com.taurus.core.util.Logger; import com.taurus.core.util.StringUtil; import robot.mj.business.AccountBusiness; import robot.mj.info.RobotUser; +import robot.mj.thread.ThreadPoolConfig; import taurus.client.Message; import taurus.client.MessageResponse; import taurus.client.TaurusClient; @@ -22,6 +23,10 @@ import taurus.util.ROBOTEventType; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static robot.mj.thread.ThreadPoolConfig.getBusinessThreadPool; +import static robot.mj.thread.ThreadPoolConfig.scheduleDelay; import static robot.mj.EXGameController.robotRoomMapping; @@ -292,14 +297,31 @@ public class RobotConnectionManager { } } - sleepTime(2000); - Map> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId); - Map> currentPlayerchisMap = getPlayerchisMap(connecId); - Map> currentPlayerpengsMap = getPlayerpengsMap(connecId); - Map> currentPlayermingsMap = getPlayermingsMap(connecId); - Map> currentPlayerzisMap = getPlayerzisMap(connecId); + //非阻塞的延迟执行,增加更完善的异常处理 + scheduleDelay(() -> { + try { + //重新获取当前实例,确保数据一致性 + HuNanChangSha reconnectedInstance = getHuNanChangShaInstance(connecId); + Map> currentPlayerOutcardsMap = getPlayerOutcardsMap(connecId); + Map> currentPlayerchisMap = getPlayerchisMap(connecId); + Map> currentPlayerpengsMap = getPlayerpengsMap(connecId); + Map> currentPlayermingsMap = getPlayermingsMap(connecId); + Map> currentPlayerzisMap = getPlayerzisMap(connecId); - currentInstance.outCard(client, currentPlayerOutcardsMap, currentPlayerchisMap, currentPlayerpengsMap, currentPlayermingsMap, currentPlayerzisMap); + reconnectedInstance.outCard(client, currentPlayerOutcardsMap, currentPlayerchisMap, currentPlayerpengsMap, currentPlayermingsMap, currentPlayerzisMap); + System.out.println("断线重连后成功执行出牌操作"); + } 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 { System.err.println("警告:重连时未获取到手牌数据"); } @@ -307,7 +329,7 @@ public class RobotConnectionManager { } } }catch (Exception e) { - e.printStackTrace(); + log.error("机器人断线重连异常"); } }else { renconnect(robotUser); @@ -481,68 +503,83 @@ public class RobotConnectionManager { } //2026.02.03修改 玩家加入房间 else if ("2001".equalsIgnoreCase(command)) { - CompletableFuture.runAsync(() -> { - sleepTime(6000); + //直接使用定时任务替代Thread.sleep,避免嵌套异步调用 + scheduleDelay(() -> { + Jedis jedis = Redis.use().getJedis(); + try { + String roomKey = String.valueOf(robotUser.getCurrentRoomId()); - 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(","); - //查询该房间的玩家信息 - String playersStr = jedis0.hget("room:"+roomKey, "players"); - if (!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); - System.out.println("2002发送退出房间协议1005,robotId: {"+robotId+"}"); - }); + //判断只有当前机器人一个玩家 + 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); + System.out.println("2001发送退出房间协议1005,robotId: {"+robotId+"}"); + }); + } } } + } catch (Exception e) { + log.error("处理玩家加入房间检查时发生异常", e); + } finally { + // 确保Jedis连接关闭 + if (jedis != null) { + jedis.close(); + } } - }); + }, 6, TimeUnit.SECONDS); System.out.println("玩家{"+ robotUser.getCurrentRoomId()+"}加入房间:"+ param); } //2026.02.03修改 玩家退出房间也要检查 else if ("2002".equalsIgnoreCase(command)) { - CompletableFuture.runAsync(() -> { - sleepTime(6000); + //直接使用定时任务替代Thread.sleep,避免嵌套异步调用 + scheduleDelay(() -> { + Jedis jedis = Redis.use().getJedis(); + try { + String roomKey = String.valueOf(robotUser.getCurrentRoomId()); - 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(","); - //查询该房间的玩家信息 - String playersStr = jedis0.hget("room:"+roomKey, "players"); - if (!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); - System.out.println("2002发送退出房间协议1005,robotId: {"+robotId+"}"); - }); + //判断只有当前机器人一个玩家 + 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); + System.out.println("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)) { @@ -559,49 +596,56 @@ public class RobotConnectionManager { } //2026.02.03修改 通过机器人房间映射直接获取房间信息 else if ("2009".equalsIgnoreCase(command)) { - CompletableFuture.runAsync(() -> { - Integer paramRobotId = param.getInt("aid"); - sleepTime(6000); + //直接使用定时任务替代Thread.sleep,避免嵌套异步调用 + scheduleDelay(() -> { + Jedis localJedis = Redis.use().getJedis(); + try { + Integer paramRobotId = param.getInt("aid"); + if (robotUser != null) { + String roomKey = String.valueOf(robotUser.getCurrentRoomId()); - if (robotUser != null) { - String roomKey = String.valueOf(robotUser.getCurrentRoomId()); + //查询该房间的玩家信息 + String playersStr = localJedis.hget(roomKey, "players"); + if (playersStr != null && !playersStr.equals("[]")) { + String players = playersStr.substring(1, playersStr.length() - 1); + String[] playerIds = players.split(","); - //查询该房间的玩家信息 - String playersStr = jedis0.hget(roomKey, "players"); - if (!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 = localJedis.hget(roomKey, "gpid"); - //判断只有当前机器人一个玩家 - if (playerIds.length == 1) { - int playerId = Integer.parseInt(playerIds[0].trim()); - if (playerId == paramRobotId) { - - String gpid = jedis0.hget(roomKey, "gpid"); - - //更新机器人剩余数量 - if (count != null && count.containsKey(Integer.parseInt(gpid))) { - Integer currentValue = count.get(Integer.parseInt(gpid)); - if (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); - System.out.println("2009发送退出房间协议1005,robotId: {"+paramRobotId+"}"); - }); + if (count != null && count.containsKey(Integer.parseInt(gpid))) { + Integer currentValue = count.get(Integer.parseInt(gpid)); + if (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); + System.out.println("2009发送退出房间协议1005,robotId: {"+paramRobotId+"}"); + }); + } } } } + } catch (Exception e) { + log.error("处理机器人房间映射检查时发生异常", e); + } finally { + if (localJedis != null) { + localJedis.close(); + } } - }); + }, 6, TimeUnit.SECONDS); } //结算 else if ("817".equalsIgnoreCase(command)) { @@ -624,9 +668,6 @@ public class RobotConnectionManager { //更新机器人剩余数量 updateLeftoverRobot(Integer.parseInt(robotUser.getRobotId())); } - - sleepTime(1000); - ITObject params = TObject.newInstance(); params.putString("session", client.getSession()); client.send("1003", params, new ICallback() { @@ -675,7 +716,7 @@ public class RobotConnectionManager { }); } } catch (Exception e) { - throw new RuntimeException(e); + log.error("处理接收到的游戏协议"); } finally { jedis0.close(); jedis2.close(); @@ -739,7 +780,7 @@ public class RobotConnectionManager { } }); } catch (Exception e) { - throw new RuntimeException(e); + log.error("机器人登录异常"); } } @@ -753,8 +794,8 @@ public class RobotConnectionManager { robotUser.setIsconnect(client.isConnected()); try { Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); + } catch (Exception e) { + log.error("连接游戏服务器时发生异常", e); } robotUser.setClient(client); EXGameController.robotRoomMapping.put(robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId(), robotUser); @@ -771,8 +812,8 @@ public class RobotConnectionManager { robotUser.setIsconnect(client.isConnected()); try { Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); + } catch (Exception e) { + log.error("重新连接游戏服务器时发生异常", e); } robotUser.setClient(client); EXGameController.robotRoomMapping.put(robotUser.getCurrentRoomId()+"_"+robotUser.getRobotId(), robotUser); @@ -811,7 +852,7 @@ public class RobotConnectionManager { //添加延迟 Thread.sleep(time); } catch (InterruptedException e) { - e.printStackTrace(); + System.out.println(e); } } diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/business/AccountBusiness.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/business/AccountBusiness.java index e83bc35..a647e84 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/business/AccountBusiness.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/business/AccountBusiness.java @@ -21,12 +21,14 @@ import com.taurus.web.Controller; import com.taurus.web.WebException; 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) { @@ -101,7 +103,7 @@ public class AccountBusiness extends Controller { if (StringUtil.isNotEmpty(idPwdBan)) { logger.error("id:"+acc_bean.id+" ban login"); - throw new WebException(ErrorCode.BAN_LOGIN); + //throw new WebException(ErrorCode.BAN_LOGIN); } resData.putString("token", token); return resData; @@ -116,7 +118,7 @@ public class AccountBusiness extends Controller { } - public final ITObject idPasswordLogin(int id, String password) throws Exception { + public final ITObject idPasswordLogin(int id, String password) { logger.info("id:" + id + " login"); Jedis jedis0 = Redis.use("group1_db0").getJedis(); @@ -145,12 +147,17 @@ public class AccountBusiness extends Controller { if (StringUtil.isNotEmpty(idPwdBan)) { System.out.println("进入了77777777777777777777"); logger.error("id:" + id + " ban login"); - throw new WebException(ErrorCode.BAN_LOGIN); + //throw new WebException(ErrorCode.BAN_LOGIN); } System.out.println("进入了9999999999999"); - ITArray resultArray = DataBase.use().executeQueryByTArray(sql); - if (resultArray.size() == 0) { + 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 { @@ -167,13 +174,13 @@ public class AccountBusiness extends Controller { logger.error("pwd error count:" + count + " not login"); System.out.println("进入了00000000000"); - throw new WebException(ErrorCode._NO_SESSION); + //throw new WebException(ErrorCode._NO_SESSION); } } System.out.println("进入了111111111111"); - throw new WebException(ErrorCode._FAILED); + //throw new WebException(ErrorCode._FAILED); } ITObject userData = resultArray.getTObject(0); diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java index 667c6d3..8dff8f7 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/handler/HuNanChangSha.java @@ -13,11 +13,11 @@ import taurus.client.TaurusClient; import taurus.util.*; import java.util.*; -import java.util.concurrent.CompletableFuture; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import static robot.mj.thread.ThreadPoolConfig.getBusinessThreadPool; public class HuNanChangSha { @@ -312,6 +312,28 @@ public class HuNanChangSha { changShachuguopai.addAll(outCard); } + /** + * 延迟执行吃碰胡动作,增加自然的停顿效果 + * @param client 客户端连接 + * @param params 动作参数 + */ + private void delayedActionCard(TaurusClient client, ITObject params, String actionName) { + //使用线程池执行延迟动作 + getBusinessThreadPool().execute(() -> { + try { + int delaySeconds = 1 + new Random().nextInt(2); + System.out.println("执行" + actionName + "动作,延迟" + delaySeconds + "秒"); + Thread.sleep(delaySeconds * 1000); + + client.send("612", params, response -> { + System.out.println(actionName + "动作发送完成"); + }); + } catch (Exception e) { + System.err.println("执行" + actionName + "动作时发生异常: " + e.getMessage()); + } + }); + } + /** * 处理 吃,碰,杠,补,胡 * @@ -393,8 +415,8 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", id); - client.send("612", params, response -> { - }); + //使用延迟方法 + delayedActionCard(client, params, "胡牌"); return "胡牌"; } } @@ -429,8 +451,7 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "不开杠"); return "不开杠"; } @@ -438,15 +459,13 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", id); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "开杠"); return "开杠"; } else { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "不开杠"); return "不开杠"; } } @@ -468,8 +487,7 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "不开杠"); return "不开杠"; } @@ -477,15 +495,13 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", id); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "开杠"); return "开杠"; } else { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "不开杠"); return "不开杠"; } } @@ -505,24 +521,22 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "不开杠"); return "不开杠"; + } Util.removeCard(changShaCardInhand, card, 1); params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", id); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "开杠"); return "开杠"; } else { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "不开杠"); return "不开杠"; } } @@ -560,8 +574,8 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", id); - client.send("612", params, response -> { - }); + //使用延迟动作方法 + delayedActionCard(client, params, "胡牌"); return "胡牌"; } //对应的数据 @@ -599,14 +613,14 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, ""); return null; } else { //获取 + ITObject tmp = null; for (Map.Entry entry : idObject.entrySet()) { if (entry.getKey() == changeid) { - ITObject tmp = entry.getValue(); + tmp = entry.getValue(); System.out.println("tmp ++++++++++= " + tmp); if (tmp.getInt("type") == 2) { //碰 @@ -635,9 +649,11 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", changeid); - client.send("612", params, response -> { - }); + //使用延迟动作方法 + String actionName = (tmp.getInt("type") == 2) ? "碰牌" : "吃牌"; + delayedActionCard(client, params, actionName); return null; + } } @@ -678,8 +694,7 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, ""); return null; } //去一张下听? @@ -691,8 +706,7 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "补牌"); return null; } } @@ -706,8 +720,7 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, ""); return null; } else { @@ -718,9 +731,8 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); - return null; + delayedActionCard(client, params, "不开杠"); + return "不开杠"; } System.out.println("补"); @@ -750,8 +762,7 @@ public class HuNanChangSha { gangdepai.add(card); gangdepai.add(card); } - client.send("612", params, response -> { - }); + delayedActionCard(client, params, ""); return null; } @@ -781,8 +792,7 @@ public class HuNanChangSha { gangdepai.add(card); gangdepai.add(card); } - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "补牌"); return null; } } @@ -791,8 +801,7 @@ public class HuNanChangSha { params.putString("session", session + "," + token); params.putInt("qi", 0); params.putInt("id", 0); - client.send("612", params, response -> { - }); + delayedActionCard(client, params, "默认动作"); return null; } @@ -873,7 +882,8 @@ public class HuNanChangSha { System.out.println("打过后的手牌 +++ " + changShaCardInhand); params.putString("session", session + "," + token); - CompletableFuture.runAsync(() -> { + //使用线程池替代CompletableFuture.runAsync + Thread.sleep + getBusinessThreadPool().execute(() -> { try { int ot = new Random().nextInt(4); Thread.sleep(ot*1000); @@ -882,7 +892,6 @@ public class HuNanChangSha { }); } catch (Exception e) { System.out.println("Thread error"); - //Thread.currentThread().interrupt(); } }); diff --git a/robots/majiang/robot_mj_cs/src/main/java/robot/mj/thread/ThreadPoolConfig.java b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/thread/ThreadPoolConfig.java new file mode 100644 index 0000000..62125e5 --- /dev/null +++ b/robots/majiang/robot_mj_cs/src/main/java/robot/mj/thread/ThreadPoolConfig.java @@ -0,0 +1,106 @@ +package robot.mj.thread; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 线程池配置类 + */ +public class ThreadPoolConfig { + + // 优化后的线程池配置:针对机器人场景优化 + private static final ExecutorService BUSINESS_THREAD_POOL = + new ThreadPoolExecutor( + 10, // 核心线程数 - 减少核心线程数 + 50, // 最大线程数 - 降低最大线程数 + 30, // 空闲线程存活时间 - 缩短存活时间 + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(10000), // 增大队列容量,减少拒绝任务 + 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 - 2); // 进一步降低线程优先级 + return t; + } + }, + new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:由调用线程执行 + ); + + // 添加定时任务线程池,专门处理延迟操作 + private static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = + Executors.newScheduledThreadPool(4, 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 - 2); + 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) { + System.out.println("提交延迟任务: 延迟" + delay + " " + unit + ", 当前时间: " + System.currentTimeMillis()); + SCHEDULED_EXECUTOR_SERVICE.schedule(() -> { + try { + System.out.println("执行延迟任务开始: 当前时间: " + System.currentTimeMillis()); + task.run(); + System.out.println("执行延迟任务完成: 当前时间: " + System.currentTimeMillis()); + } catch (Exception e) { + System.err.println("延迟任务执行异常: " + e.getMessage()); + } + }, delay, unit); + } + + /** + * 执行周期性任务 + */ + public static void scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) { + SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(task, initialDelay, period, unit); + } + + /** + * 优雅关闭线程池,释放资源 + */ + public static void shutdown() { + System.out.println("开始关闭线程池..."); + + // 关闭定时任务线程池 + SCHEDULED_EXECUTOR_SERVICE.shutdown(); + try { + if (!SCHEDULED_EXECUTOR_SERVICE.awaitTermination(5, TimeUnit.SECONDS)) { + SCHEDULED_EXECUTOR_SERVICE.shutdownNow(); + } + } catch (InterruptedException e) { + SCHEDULED_EXECUTOR_SERVICE.shutdownNow(); + Thread.currentThread().interrupt(); + } + + // 关闭业务线程池 + BUSINESS_THREAD_POOL.shutdown(); + try { + if (!BUSINESS_THREAD_POOL.awaitTermination(10, TimeUnit.SECONDS)) { + BUSINESS_THREAD_POOL.shutdownNow(); + } + } catch (InterruptedException e) { + BUSINESS_THREAD_POOL.shutdownNow(); + Thread.currentThread().interrupt(); + } + + System.out.println("线程池关闭完成"); + } +} diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java index 5b1ad45..7ce65c4 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ChangshaWinSplitCard.java @@ -23,7 +23,7 @@ public class ChangshaWinSplitCard { // 检测不带精湖 public static int checkNormalHu(List cardInHand, Map map) { if (cardInHand == null || cardInHand.isEmpty()) { - throw new IllegalArgumentException("手牌不能为空"); + //throw new IllegalArgumentException("手牌不能为空"); } String info = ""; diff --git a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java index 91f5304..6c7b7fa 100644 --- a/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java +++ b/robots/majiang/robot_mj_cs/src/main/java/taurus/util/ai.java @@ -31,7 +31,7 @@ public class ai { public int findBestDiscard(PlayerState state) { //参数验证 if (state == null || state.handCards == null) { - throw new IllegalArgumentException("玩家状态和手牌不能为空"); + //throw new IllegalArgumentException("玩家状态和手牌不能为空"); } System.out.println("\n=== 长沙麻将AI出牌分析 ==="); System.out.println("手牌(" + state.handCards.size() + "张): " + this.formatCards(state.handCards));