From b96271e61cc570e6b0f85439b162327b19395e1f Mon Sep 17 00:00:00 2001 From: miaoqingshuai Date: Wed, 17 Jun 2026 21:02:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=BE=E7=82=AE=E7=BD=9A=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=E6=B7=BB=E5=8A=A0http=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/robot/zp/Config.java | 9 + .../main/java/robot/zp/EXGameController.java | 46 ++- .../src/main/java/robot/zp/EXMainServer.java | 262 ++++++++++-------- .../main/java/robot/zp/RobotHttpServer.java | 175 ++++++++++++ 4 files changed, 363 insertions(+), 129 deletions(-) create mode 100644 robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/RobotHttpServer.java diff --git a/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/Config.java b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/Config.java index 3991501..9737f39 100644 --- a/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/Config.java +++ b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/Config.java @@ -34,4 +34,13 @@ public class Config { /** 默认群组ID */ public static final String DEFAULT_GROUP_ID = "426149"; + /** + * 机器人HTTP服务端口(接收 web_group 通过 HTTP 发送的 225 协议) + * 规则:TCP端口 + 1000(TCP=8917,HTTP=9917) + */ + public static final int HTTP_SERVER_PORT = 9917; + + /** 机器人HTTP服务路径 */ + public static final String HTTP_PATH_JOIN_ROOM = "/robot/joinRoom"; + } \ No newline at end of file diff --git a/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXGameController.java b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXGameController.java index 08e662d..19a1eec 100644 --- a/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXGameController.java +++ b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXGameController.java @@ -2,6 +2,7 @@ package robot.zp; import com.robot.GameController; import com.robot.GameInterceptor; +import com.robot.MainServer; import com.taurus.core.entity.ITObject; import com.taurus.core.entity.TObject; import com.taurus.core.plugin.redis.Redis; @@ -36,10 +37,10 @@ public class EXGameController extends GameController { //机器人房间 protected static final Map robotRoomMapping = new ConcurrentHashMap<>(); - + //记录最近访问时间 private static final Map lastAccessTime = new ConcurrentHashMap<>(); - + //设置连接超时时间(5分钟) private static final long CONNECTION_TIMEOUT = 5 * 60 * 1000; @@ -56,7 +57,35 @@ public class EXGameController extends GameController { int robotId = params.getInt("robotid"); String roomId = params.getString("roomid"); int groupId = params.getInt("groupid"); - + + ITObject result = processWebGroupJoin(robotId, roomId, groupId, params); + + // 仅在"ID冲突"场景下回错误响应,其他场景保持原有静默行为不变 + int code = result.containsKey("code") ? result.getInt("code") : 0; + if (code == 1) { + ITObject errorResponse = TObject.newInstance(); + errorResponse.putString("status", "failed"); + errorResponse.putString("message", result.containsKey("message") ? result.getString("message") : "处理失败"); + MainServer.instance.sendResponse(gid, 1, errorResponse, session); + } + + } + + + /** + * 处理 web_group 加入房间请求 - 核心逻辑(与传输方式无关) + * 供 TCP 入口(webGroup)和 HTTP 入口(RobotHttpServer)共用 + * + * @param robotId 机器人ID + * @param roomId 房间ID + * @param groupId 群组ID + * @param params 原始参数 + * @return ITObject 处理结果,包含: + * code (0=成功, 1=ID冲突需告知调用方, 2=不同机器人冲突静默忽略) + * message (描述信息) + */ + public ITObject processWebGroupJoin(int robotId, String roomId, int groupId, ITObject params) { + ITObject result = TObject.newInstance(); //检查Redis中该房间是否真的包含当前机器人 if (!checkRobotInRoomRedis(roomId, String.valueOf(robotId))) { //Redis中不存在该机器人 清理本地可能的错误映射 @@ -76,11 +105,13 @@ public class EXGameController extends GameController { synchronized (robotUsers) { RobotUser robotUser = robotUsers.get(0); int existingRobotId = Integer.parseInt(robotUser.getRobotId()); - + if (robotId != existingRobotId) { //不同机器人的冲突 log.warn("房间{}中Redis已存在机器人{},当前机器人{}不执行加入逻辑", roomId, existingRobotId, robotId); - return; + result.putInt("code", 2); + result.putString("message", "Redis已存在其他机器人,不执行加入逻辑"); + return result; } } } @@ -89,6 +120,9 @@ public class EXGameController extends GameController { //加入房间 joinRoomCommon(robotId, roomId, groupId, params); log.info("225已进入房间准备成功: room:{} robot:{}", roomId, robotId); + result.putInt("code", 0); + result.putString("message", "success"); + return result; } /** @@ -187,7 +221,6 @@ public class EXGameController extends GameController { //发送加入房间请求到game_zp_fls client.send(Config.JOIN_ROOM_FLS, params, response -> { //成功响应后才建立映射关系 - System.out.println("能进来吗"); robotRoomMapping.put(robotUser.getConnecId(), robotUser); robotConnectionManager.reconnectToGameServer(response, robotUser, client); }); @@ -195,7 +228,6 @@ public class EXGameController extends GameController { log.info("已进入房间成功: {}", robotUser.getConnecId()); Thread.sleep(1000); if (client.isConnected()) { - System.out.println("发送准备"); client.send(Config.GAME_READY_FLS, params, response -> { log.info("1003:{}", response); }); diff --git a/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXMainServer.java b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXMainServer.java index f7b7228..d9876fe 100644 --- a/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXMainServer.java +++ b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/EXMainServer.java @@ -22,149 +22,167 @@ import static robot.zp.EXGameController.robotRoomMapping; * 福禄寿机器人主服务器 * TCP服务端接收robot_mgr的协议 同时作为客户端连接game_zp_fls处理AI逻辑 */ -public class EXMainServer extends MainServer{ - private static final Logger log = LoggerFactory.getLogger(EXMainServer.class); +public class EXMainServer extends MainServer { + private static final Logger log = LoggerFactory.getLogger(EXMainServer.class); - private static final RobotConnectionManager robotConnectionManager = new RobotConnectionManager(); + private static final RobotConnectionManager robotConnectionManager = new RobotConnectionManager(); + /** + * 机器人HTTP服务(接收 web_group 通过 HTTP 发送的 225 协议,替代TCP异步方式) + */ - @Override - public void onStart() { - super.onStart(); + private RobotHttpServer robotHttpServer; - //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()); - } - } + @Override + public void onStart() { + super.onStart(); - //关闭线程池 - robot.zp.thread.ThreadPoolConfig.shutdown(); + // 启动机器人HTTP服务(接收 web_group 通过 HTTP 发送的 225 协议) + // 替代原 TCP 异步多线程方式(TaurusClient+CompletableFuture),降低CPU占用 + try { + robotHttpServer = new RobotHttpServer(); + robotHttpServer.start(Config.HTTP_SERVER_PORT); + } catch (Exception e) { + log.error("启动 RobotHttpServer 失败,端口:" + Config.HTTP_SERVER_PORT, e); + } - log.info("优雅关闭完成"); - } catch (Exception e) { - log.error("关闭过程中发生异常", e); - } - })); + //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()); + } + } - // 1. 先启动独立的事件处理线程(只启动一次) - startNetEventThread(); + //关闭线程池 + robot.zp.thread.ThreadPoolConfig.shutdown(); - // 2. 启动资源清理定时任务 - startResourceCleanupScheduler(); + log.info("优雅关闭完成"); + } catch (Exception e) { + log.error("关闭过程中发生异常", e); + } + })); - // 3. 启动系统监控 - //startConnectionCheckScheduler(); - //测试 - Jedis jedis2 = Redis.use("group1_db2").getJedis(); - String robotskey = "g{"+Config.DEFAULT_GROUP_ID+"}:play:"+Config.DEFAULT_PID; - Map maprobot = jedis2.hgetAll(robotskey); - for(Map.Entry entry : maprobot.entrySet()) { - log.info("{}:{}", entry.getKey(), entry.getValue()); - //是否创建 - RobotUser robotUser = new RobotUser(); - robotUser.setRobotId(entry.getKey()); - robotUser.setPassword(Config.DEFAULT_PASSWORD); - robotUser.setGameHost(Config.GAME_SERVER_HOST); - robotUser.setGamePort(Config.GAME_SERVER_PORT); - robotUser.setRobotGroupid(Config.DEFAULT_GROUP_ID); - robotUser.setRobotPid(Config.DEFAULT_PID); + // 1. 先启动独立的事件处理线程(只启动一次) + startNetEventThread(); - robotRoomMapping.put(entry.getKey(), robotUser); - } + // 2. 启动资源清理定时任务 + startResourceCleanupScheduler(); - for(Map.Entry entry : robotRoomMapping.entrySet()) { - RobotUser robotUser = entry.getValue(); - //1、登录 - //判断是否登录 - if(!robotUser.isLogin){ - robotConnectionManager.login(robotUser); - } - } + // 3. 启动系统监控 + //startConnectionCheckScheduler(); + //测试 + Jedis jedis2 = Redis.use("group1_db2").getJedis(); + String robotskey = "g{" + Config.DEFAULT_GROUP_ID + "}:play:" + Config.DEFAULT_PID; + Map maprobot = jedis2.hgetAll(robotskey); + for (Map.Entry entry : maprobot.entrySet()) { + log.info("{}:{}", entry.getKey(), entry.getValue()); + //是否创建 + RobotUser robotUser = new RobotUser(); + robotUser.setRobotId(entry.getKey()); + robotUser.setPassword(Config.DEFAULT_PASSWORD); + robotUser.setGameHost(Config.GAME_SERVER_HOST); + robotUser.setGamePort(Config.GAME_SERVER_PORT); + robotUser.setRobotGroupid(Config.DEFAULT_GROUP_ID); + robotUser.setRobotPid(Config.DEFAULT_PID); - log.info("福禄寿机器人服务器已启动"); - log.info("服务器将监听端口 {} 用于接收robot_mgr管理协议", gameSetting.port); - log.info("当前线程池配置: {}", ThreadPoolConfig.getThreadPoolStatus()); + robotRoomMapping.put(entry.getKey(), robotUser); + } - jedis2.close(); - } + for (Map.Entry entry : robotRoomMapping.entrySet()) { + RobotUser robotUser = entry.getValue(); + //1、登录 + //判断是否登录 + if (!robotUser.isLogin) { + robotConnectionManager.login(robotUser); + } + } - /** - * 独立的事件处理线程 - */ - private void startNetEventThread() { - Thread eventThread = new Thread(() -> { - while (true) { - NetManager.processEvents(); - try { - Thread.sleep(2); - } catch (InterruptedException e) { - break; - } catch (Exception e) { - } - } - }, "Changsha_Thread"); + log.info("福禄寿机器人服务器已启动"); + log.info("服务器将监听端口 {} 用于接收robot_mgr管理协议", gameSetting.port); + log.info("当前线程池配置: {}", ThreadPoolConfig.getThreadPoolStatus()); - eventThread.setDaemon(true); //设置为守护线程 - eventThread.start(); - } + jedis2.close(); + } - /** - * 启动资源清理定时任务 - */ - 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"); + /** + * 独立的事件处理线程 + */ + private void startNetEventThread() { + Thread eventThread = new Thread(() -> { + while (true) { + NetManager.processEvents(); + try { + Thread.sleep(2); + } catch (InterruptedException e) { + break; + } catch (Exception e) { + } + } + }, "Changsha_Thread"); - cleanupThread.setDaemon(true); - cleanupThread.start(); - log.info("资源清理定时任务已启动"); - } + eventThread.setDaemon(true); //设置为守护线程 + eventThread.start(); + } + + /** + * 启动资源清理定时任务 + */ + private void startResourceCleanupScheduler() { + Thread cleanupThread = new Thread(() -> { + while (true) { + try { + //每30秒执行一次资源清理 + Thread.sleep(30000); + ResourceCleanupUtil.performCleanup(); + log.info("线程池状态: {}", ThreadPoolConfig.getThreadPoolStatus()); + } catch (InterruptedException e) { + break; + } catch (Exception e) { + log.error("资源清理任务异常: {}", e.getMessage(), e); + // 发生异常时尝试清理 + try { + ResourceCleanupUtil.performCleanup(); + } catch (Exception cleanupEx) { + log.error("异常清理也失败: {}", cleanupEx.getMessage(), cleanupEx); + } + } + } + }, "ResourceCleanupThread"); + + cleanupThread.setDaemon(true); + cleanupThread.start(); + log.info("资源清理定时任务已启动"); + } + @Override + public Room newRoom(String roomid, Map redis_room_map) { + return new EXRoom(roomid, redis_room_map); + } - @Override - public Room newRoom(String roomid, Map redis_room_map) { - return new EXRoom(roomid, redis_room_map); - } - - @Override - public Player newPlayer(int i, Room room, String s) { - return new EXPlayer(i, room, s); - } + @Override + public Player newPlayer(int i, Room room, String s) { + return new EXPlayer(i, room, s); + } - protected GameController newController() { - return new EXGameController(); - } + protected GameController newController() { + return new EXGameController(); + } - @Override - public void onStop() { - super.onStop(); + @Override + public void onStop() { + super.onStop(); +// 停止机器人HTTP服务 + if (robotHttpServer != null) { + robotHttpServer.stop(); + robotHttpServer = null; + } - log.info("福禄寿机器人服务器已停止"); - } + log.info("放炮罚机器人服务器已停止"); + } } \ No newline at end of file diff --git a/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/RobotHttpServer.java b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/RobotHttpServer.java new file mode 100644 index 0000000..cc7e6c8 --- /dev/null +++ b/robots/zhipai/robot_zp_fpf/src/main/java/robot/zp/RobotHttpServer.java @@ -0,0 +1,175 @@ +package robot.zp; + +import com.google.gson.Gson; +import com.robot.Global; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.taurus.core.entity.ITObject; +import com.taurus.core.entity.TObject; +import com.taurus.core.util.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + + +public class RobotHttpServer { + private static final Logger log = Logger.getLogger(RobotHttpServer.class); + + /** 工作线程数(同时处理的并发请求数) */ + private static final int WORKER_THREADS = 8; + + /** HTTP 响应超时(用于优雅关闭,秒) */ + private static final int STOP_DELAY = 2; + + private HttpServer server; + private ExecutorService executor; + private final Gson gson = new Gson(); + + /** + * 启动 HTTP 服务 + * @param port 监听端口 + * @throws IOException 端口被占用等异常 + */ + public void start(int port) throws IOException { + server = HttpServer.create(new InetSocketAddress(port), 0); + server.createContext(Config.HTTP_PATH_JOIN_ROOM, new JoinRoomHandler()); + executor = Executors.newFixedThreadPool(WORKER_THREADS); + server.setExecutor(executor); + server.start(); + log.info("RobotHttpServer已启动,监听端口:" + port + ",路径:" + Config.HTTP_PATH_JOIN_ROOM + ",工作线程:" + WORKER_THREADS); + } + + /** + * 停止 HTTP 服务 + */ + public void stop() { + if (server != null) { + server.stop(STOP_DELAY); + server = null; + } + if (executor != null) { + executor.shutdown(); + try { + if (!executor.awaitTermination(STOP_DELAY, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + Thread.currentThread().interrupt(); + } + executor = null; + } + log.info("RobotHttpServer已停止"); + } + + /** + * 处理 /robot/joinRoom 请求的 Handler + */ + private class JoinRoomHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + // 仅接受 POST + if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) { + writeJsonResponse(exchange, 405, buildErrorResponse("Method Not Allowed", "只支持POST请求")); + return; + } + + Map respData = new HashMap<>(); + try { + // 读取请求体 + String body = readRequestBody(exchange); + log.info("HTTP 收到加入房间请求:" + body); + + // 解析 JSON + @SuppressWarnings("unchecked") + Map data = gson.fromJson(body, Map.class); + if (data == null) { + writeJsonResponse(exchange, 400, buildErrorResponse("Bad Request", "请求体为空或非合法JSON")); + return; + } + + Number robotIdNum = (Number) data.get("robotid"); + Object roomIdObj = data.get("roomid"); + Number groupIdNum = (Number) data.get("groupid"); + + if (robotIdNum == null || roomIdObj == null || groupIdNum == null) { + writeJsonResponse(exchange, 400, buildErrorResponse("Bad Request", "缺少必要参数 robotid/roomid/groupid")); + return; + } + + int robotId = robotIdNum.intValue(); + String roomId = String.valueOf(roomIdObj); + int groupId = groupIdNum.intValue(); + + // 构造 ITObject 参数(与原 TCP 入参保持一致) + ITObject params = TObject.newInstance(); + params.putInt("robotid", robotId); + params.putString("roomid", roomId); + params.putInt("groupid", groupId); + + // 调用公共处理逻辑(与 TCP webGroup 方法共用) + ITObject result = ((EXGameController) Global.gameCtr).processWebGroupJoin(robotId, roomId, groupId, params); + + int code = result.containsKey("code") ? result.getInt("code") : 0; + String message = result.containsKey("message") ? result.getString("message") : "success"; + respData.put("code", code); + respData.put("message", message); + } catch (Exception e) { + log.error("HTTP处理加入房间请求异常", e); + respData.put("code", 500); + respData.put("message", "服务器内部错误: " + e.getMessage()); + } + + writeJsonResponse(exchange, 200, respData); + } + + /** + * 读取请求体为字符串 + */ + private String readRequestBody(HttpExchange exchange) throws IOException { + InputStream in = exchange.getRequestBody(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + int n; + while ((n = in.read(buf)) > 0) { + baos.write(buf, 0, n); + } + in.close(); + return new String(baos.toByteArray(), StandardCharsets.UTF_8); + } + + /** + * 写入 JSON 响应 + */ + private void writeJsonResponse(HttpExchange exchange, int httpCode, Map data) throws IOException { + String json = gson.toJson(data); + byte[] bytes = json.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8"); + exchange.sendResponseHeaders(httpCode, bytes.length); + OutputStream out = exchange.getResponseBody(); + out.write(bytes); + out.close(); + } + + /** + * 构造错误响应 + */ + private Map buildErrorResponse(String error, String message) { + Map data = new HashMap<>(); + data.put("code", -1); + data.put("error", error); + data.put("message", message); + return data; + } + } +}