机器人连接0130
parent
18a90dd89d
commit
14e81d42ab
|
|
@ -116,8 +116,8 @@ public class MainServer extends Extension {
|
|||
}
|
||||
|
||||
Map<Integer, Integer> gameRobotConfig = new HashMap<>();
|
||||
gameRobotConfig.put(10, 5);
|
||||
gameRobotConfig.put(22, 3);
|
||||
gameRobotConfig.put(10, 1);
|
||||
gameRobotConfig.put(22, 0);
|
||||
int robotIndex = 0;
|
||||
|
||||
//长沙麻将机器人
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public class RobotManager {
|
|||
private final Map<Integer, Integer> wanfaRobotIndex = new ConcurrentHashMap<>();
|
||||
|
||||
//玩法房间轮询器
|
||||
private final Map<Integer, RoomWanfaMatcher> wanfaRoomPollers = new ConcurrentHashMap<>();
|
||||
private final Map<String, RoomWanfaMatcher> wanfaRoomPollers = new ConcurrentHashMap<>();
|
||||
|
||||
//定时任务调度器
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
|
||||
|
|
@ -135,19 +135,20 @@ public class RobotManager {
|
|||
robot.setToken(tokenList.get(0));
|
||||
} else {
|
||||
//如果没有token 则执行正常登录
|
||||
loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), robot.getPassword());
|
||||
loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), "123456");
|
||||
robot.setToken(loginResult.getString("token"));
|
||||
robot.setSession(accountBusiness.getSession());
|
||||
}
|
||||
} else {
|
||||
log.debug("机器人 {} 执行常规登录", robot.getRobotId());
|
||||
//执行正常登录流程
|
||||
loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), robot.getPassword());
|
||||
loginResult = accountBusiness.idPasswordLogin(robot.getRobotId(), "123456");
|
||||
robot.setToken(loginResult.getString("token"));
|
||||
robot.setSession(accountBusiness.getSession());
|
||||
}
|
||||
robot.setOnline(true);
|
||||
robot.setWanfaId(gameId);
|
||||
robot.setGroupId(-1);
|
||||
robot.setLastActiveTime(System.currentTimeMillis());
|
||||
|
||||
HashMap<String, String> robotMap = new HashMap<>();
|
||||
|
|
@ -226,14 +227,15 @@ public class RobotManager {
|
|||
RoomWanfaMatcher poller = getWanfaRoomPoller(groupId, wanfaId);
|
||||
if (!poller.isRunning()) {
|
||||
poller.startPolling();
|
||||
log.info("为玩法ID {} 启动房间轮询器", wanfaId);
|
||||
log.info("为群组 {} 玩法ID {} 启动房间轮询器", groupId, wanfaId);
|
||||
}
|
||||
} else {
|
||||
//如果没有配置机器人 停止轮询器
|
||||
RoomWanfaMatcher poller = wanfaRoomPollers.get(wanfaId);
|
||||
String key = groupId + ":" + wanfaId;
|
||||
RoomWanfaMatcher poller = wanfaRoomPollers.get(key);
|
||||
if (poller != null) {
|
||||
poller.stopPolling();
|
||||
log.info("为玩法ID {} 停止房间轮询器", wanfaId);
|
||||
log.info("为群组 {} 玩法ID {} 停止房间轮询器", groupId, wanfaId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -274,16 +276,20 @@ public class RobotManager {
|
|||
int disconnectedCount = 0;
|
||||
for (int i = 0; i < allWanfaRobots.size() && disconnectedCount < toDisconnect; i++) {
|
||||
RobotInfo robot = allWanfaRobots.get(i);
|
||||
//不在使用中且已连接则断开连接
|
||||
//不在使用断开连接
|
||||
if (!robot.isUsing() && robot.isConnected()) {
|
||||
synchronized(robot) {
|
||||
if (!robot.isUsing() && robot.isConnected()) {
|
||||
RobotManagerInterface handler = getGameHandler(robot.getWanfaId());
|
||||
if (handler != null) {
|
||||
handler.disconnectRobot(robot);
|
||||
log.debug("仅断开机器人 {} TCP连接,保持注册状态", robot.getRobotId());
|
||||
log.debug("断开机器人 {} TCP连接,玩法ID: {}", robot.getRobotId(), wanfaId);
|
||||
}
|
||||
disconnectedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (disconnectedCount > 0) {
|
||||
log.debug("玩法 {} 成功断开 {} 个机器人的TCP连接,目标连接数: {}", wanfaId, disconnectedCount, requiredCount);
|
||||
}
|
||||
|
|
@ -316,8 +322,16 @@ public class RobotManager {
|
|||
if (!robot.isConnected() && !robot.isUsing() && reconnectedCount < (requiredCount - currentConnected)) {
|
||||
RobotManagerInterface handler = getGameHandler(robot.getWanfaId());
|
||||
if (handler != null) {
|
||||
if (shouldConnectRobot(robot)) {
|
||||
synchronized(robot) {
|
||||
if (!robot.isConnected() && !robot.isUsing()) {
|
||||
handler.connectRobot(robot);
|
||||
log.debug("重新连接玩法 {} 的机器人: {}", wanfaId, robot.getRobotId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.debug("玩法 {} 的leftover_robot为0,跳过连接机器人: {}", wanfaId, robot.getRobotId());
|
||||
}
|
||||
reconnectedCount++;
|
||||
}
|
||||
}
|
||||
|
|
@ -329,9 +343,10 @@ public class RobotManager {
|
|||
* 获取玩法房间轮询器
|
||||
*/
|
||||
public RoomWanfaMatcher getWanfaRoomPoller(int groupId, int wanfaId) {
|
||||
return wanfaRoomPollers.computeIfAbsent(wanfaId, k -> {
|
||||
String key = groupId + ":" + wanfaId;
|
||||
return wanfaRoomPollers.computeIfAbsent(key, k -> {
|
||||
RoomWanfaMatcher poller = new RoomWanfaMatcher(this, groupId, wanfaId);
|
||||
log.info("创建玩法ID {} 的房间轮询器", wanfaId);
|
||||
log.info("创建群组 {} 玩法ID {} 的房间轮询器", groupId, wanfaId);
|
||||
return poller;
|
||||
});
|
||||
}
|
||||
|
|
@ -495,7 +510,32 @@ public class RobotManager {
|
|||
public void releaseRobot(int robotId) {
|
||||
RobotInfo robot = connectedRobots.get(robotId);
|
||||
if (robot != null) {
|
||||
synchronized(robot) {
|
||||
robot.setUsing(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据玩法的leftover_robot数量检查是否连接机器人
|
||||
*/
|
||||
public boolean shouldConnectRobot(RobotInfo robot) {
|
||||
if (robot.getGroupId() == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try (Jedis jedis11 = Redis.use("group1_db11").getJedis()) {
|
||||
String playKey = "g{" + robot.getGroupId() + "}:play:" + robot.getWanfaId();
|
||||
String leftoverRobotStr = jedis11.hget(playKey, "leftover_robot");
|
||||
|
||||
if (leftoverRobotStr != null) {
|
||||
int leftoverRobot = Integer.parseInt(leftoverRobotStr);
|
||||
return leftoverRobot > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,22 @@
|
|||
package com.group.robot.handler;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.group.Protocol;
|
||||
import com.group.robot.info.RobotInfo;
|
||||
import com.group.robot.RobotManager;
|
||||
import com.group.robot.info.RoomInfo;
|
||||
import com.group.robot.matcher.RoomWanfaMatcher;
|
||||
import com.taurus.core.entity.TObject;
|
||||
import com.taurus.core.plugin.redis.Redis;
|
||||
import com.taurus.core.util.ICallback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import taurus.client.MessageResponse;
|
||||
import taurus.client.SocketCode;
|
||||
import taurus.client.TaurusClient;
|
||||
import taurus.client.Message;
|
||||
|
|
@ -36,9 +38,17 @@ public class RobotConnectionHandler {
|
|||
//机器人ID到账户信息的映射
|
||||
private final Map<Integer, RobotAccountInfo> robotAccounts = new ConcurrentHashMap<>();
|
||||
|
||||
//机器人ID到连接ID的映射,用于标识每个机器人连接的唯一性
|
||||
private final Map<Integer, String> robotConnectionIds = new ConcurrentHashMap<>();
|
||||
|
||||
//机器人管理器引用
|
||||
private final RobotManager robotManager;
|
||||
|
||||
//心跳和重连
|
||||
private final Map<Integer, ScheduledFuture<?>> heartbeatTasks = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, String> robotServerAddresses = new ConcurrentHashMap<>();
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
|
||||
|
||||
/**
|
||||
* 机器人账户信息
|
||||
*/
|
||||
|
|
@ -96,31 +106,68 @@ public class RobotConnectionHandler {
|
|||
* 连接机器人到游戏服务器
|
||||
*/
|
||||
public void connectRobot(RobotInfo robot) {
|
||||
//玩法leftover_robot大于0连接机器人
|
||||
if (!robotManager.shouldConnectRobot(robot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//异步执行连接逻辑
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
//连接前再次检查leftover_robot数量 防止并发
|
||||
if (!robotManager.shouldConnectRobot(robot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("开始连接机器人 {} 到游戏服务器,玩法ID: {}", robot.getRobotId(), robot.getWanfaId());
|
||||
//TCP客户端连接
|
||||
TaurusClient client = new TaurusClient(getGameServerAddress(robot.getWanfaId()), "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||
String serverAddress = getGameServerAddress(robot.getWanfaId());
|
||||
TaurusClient client = new TaurusClient(serverAddress, "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||
client.connect();
|
||||
|
||||
//生成唯一的connecId
|
||||
String connecId = generateConnectionId(robot.getRobotId());
|
||||
|
||||
//先清理可能存在的旧连接
|
||||
TaurusClient oldClient = robotClients.remove(robot.getRobotId());
|
||||
if (oldClient != null && oldClient.isConnected()) {
|
||||
oldClient.killConnection();
|
||||
}
|
||||
|
||||
//保存客户端连接
|
||||
robotClients.put(robot.getRobotId(), client);
|
||||
robotServerAddresses.put(robot.getRobotId(), serverAddress);
|
||||
//保存connecId
|
||||
robotConnectionIds.put(robot.getRobotId(), connecId);
|
||||
|
||||
//设置事件监听器
|
||||
setupInitializationEventListeners(client, robot);
|
||||
|
||||
//连接成功后发送初始化协议
|
||||
sendInitializationProtocol(client, robot);
|
||||
log.info("机器人 {} 成功连接到游戏服务器", robot.getRobotId());
|
||||
|
||||
//设置机器人连接状态
|
||||
robot.setConnected(true);
|
||||
log.info("机器人 {} 成功连接到游戏服务器, ConnectionId: {}", robot.getRobotId(), connecId);
|
||||
|
||||
//启动心跳
|
||||
startHeartTask(robot.getRobotId(), client, robot.getWanfaId());
|
||||
} catch (Exception e) {
|
||||
log.error("连接机器人到游戏服务器时发生异常: " + robot.getRobotId(), e);
|
||||
//清理已经建立的连接
|
||||
robot.setConnected(false);
|
||||
disconnectRobot(robot.getRobotId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的连接ID
|
||||
*/
|
||||
private String generateConnectionId(int robotId) {
|
||||
String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||
return robotId + "_" + uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置初始化阶段的事件监听器
|
||||
*/
|
||||
|
|
@ -132,8 +179,10 @@ public class RobotConnectionHandler {
|
|||
SocketCode code = (SocketCode) event.getParameter("code");
|
||||
if (code == SocketCode.Connect) {
|
||||
log.info("机器人 {} 连接游戏服务器成功", robot.getRobotId());
|
||||
robot.setConnected(true);
|
||||
} else {
|
||||
log.warn("机器人 {} 连接游戏服务器失败: {}", robot.getRobotId(), code);
|
||||
robot.setConnected(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -161,6 +210,77 @@ public class RobotConnectionHandler {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动心跳任务
|
||||
*/
|
||||
private void startHeartTask(int robotId, TaurusClient client, int wanfaId) {
|
||||
stopHeartTask(robotId);
|
||||
|
||||
ScheduledFuture<?> heartbeatTask = scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
if (client != null && client.isConnected()) {
|
||||
//发送心跳包
|
||||
ITObject heartbeatParam = new TObject();
|
||||
heartbeatParam.putString("type", "heartbeat");
|
||||
heartbeatParam.putInt("robot_id", robotId);
|
||||
heartbeatParam.putInt("wanfa_id", wanfaId);
|
||||
heartbeatParam.putString("connecId", robotConnectionIds.get(robotId));
|
||||
heartbeatParam.putLong("timestamp", System.currentTimeMillis());
|
||||
|
||||
client.send("ping", heartbeatParam, response -> {
|
||||
if (response != null) {
|
||||
log.debug("机器人 {} 心跳响应成功", robotId);
|
||||
} else {
|
||||
log.warn("机器人 {} 心跳响应失败", robotId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.warn("机器人 {} 连接已断开,无法发送心跳", robotId);
|
||||
//连接断开 尝试重连
|
||||
RobotInfo robot = robotManager.getConnectedRobot(robotId);
|
||||
if (robot != null) {
|
||||
scheduleReconnection(robot);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送心跳时发生异常,机器人: " + robotId, e);
|
||||
}
|
||||
}, 30, 30, TimeUnit.SECONDS);//30秒发送一次心跳
|
||||
|
||||
heartbeatTasks.put(robotId, heartbeatTask);
|
||||
log.info("已启动机器人 {} 的心跳任务", robotId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止心跳任务
|
||||
*/
|
||||
private void stopHeartTask(int robotId) {
|
||||
ScheduledFuture<?> task = heartbeatTasks.remove(robotId);
|
||||
if (task != null && !task.isCancelled()) {
|
||||
robotConnectionIds.remove(robotId);
|
||||
task.cancel(false);
|
||||
log.info("已停止机器人 {} 的心跳任务", robotId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计划重连任务
|
||||
*/
|
||||
private void scheduleReconnection(RobotInfo robot) {
|
||||
scheduler.schedule(() -> {
|
||||
try {
|
||||
log.info("开始重连机器人: {}", robot.getRobotId());
|
||||
//清理可能存在的旧连接
|
||||
robot.setConnected(false);
|
||||
disconnectRobot(robot.getRobotId());
|
||||
//重新连接
|
||||
connectRobot(robot);
|
||||
} catch (Exception e) {
|
||||
log.error("重连机器人失败: {}", robot.getRobotId(), e);
|
||||
}
|
||||
}, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据游戏ID获取服务器地址
|
||||
*/
|
||||
|
|
@ -168,14 +288,14 @@ public class RobotConnectionHandler {
|
|||
//根据玩法ID返回对应的服务器地址
|
||||
switch (wanfaId) {
|
||||
case 10: //长沙麻将
|
||||
return "127.0.0.1:8701";
|
||||
return "192.168.0.32:8701";
|
||||
case 22: //红中麻将
|
||||
return "8.138.242.190:6421";
|
||||
case 66: //跑得快
|
||||
return "8.138.242.190:6841";
|
||||
default:
|
||||
log.warn("未知的玩法ID: {}, 使用默认服务器地址", wanfaId);
|
||||
return "127.0.0.1:6311"; //默认地址
|
||||
return "192.168.0.32:6311"; //默认地址
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -185,10 +305,11 @@ public class RobotConnectionHandler {
|
|||
public void disconnectRobot(int robotId) {
|
||||
try {
|
||||
log.info("清理机器人 {} 的连接资源", robotId);
|
||||
stopHeartTask(robotId);
|
||||
//移除机器人TCP客户端连接
|
||||
TaurusClient client = robotClients.remove(robotId);
|
||||
if (client != null) {
|
||||
client.clearResponse();
|
||||
client.killConnection();
|
||||
log.debug("已清理机器人 {} 的客户端连接", robotId);
|
||||
}
|
||||
//移除机器人信息
|
||||
|
|
@ -220,6 +341,8 @@ public class RobotConnectionHandler {
|
|||
if (client != null) {
|
||||
//发送离开房间协议
|
||||
ITObject param = new TObject();
|
||||
param.putString("robotId", String.valueOf(robot.getRobotId()));
|
||||
param.putString("connecId", robotConnectionIds.get(robot.getRobotId())); // 添加connecId
|
||||
client.send("2005", param, response -> {
|
||||
log.debug("机器人 {} 发送离开房间请求", robot.getRobotId());
|
||||
});
|
||||
|
|
@ -281,6 +404,9 @@ public class RobotConnectionHandler {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送加入房间消息给指定机器人
|
||||
*/
|
||||
public void sendJoinRoomMessage(RobotInfo robot) {
|
||||
TaurusClient client = robotClients.get(robot.getRobotId());
|
||||
|
||||
|
|
@ -291,6 +417,8 @@ public class RobotConnectionHandler {
|
|||
|
||||
ITObject readyParam = new TObject();
|
||||
readyParam.putString("session", robot.getSession() + "," + robot.getToken());
|
||||
readyParam.putString("robotId", String.valueOf(robot.getRobotId()));
|
||||
readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId()));
|
||||
client.send("2002", readyParam, response -> {
|
||||
log.debug("机器人 {} 发送加入房间请求响应: {}", robot.getRobotId(), response);
|
||||
});
|
||||
|
|
@ -309,11 +437,62 @@ public class RobotConnectionHandler {
|
|||
|
||||
ITObject readyParam = new TObject();
|
||||
readyParam.putString("session", robot.getSession() + "," + robot.getToken());
|
||||
readyParam.putString("robotId", String.valueOf(robot.getRobotId()));
|
||||
readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId()));
|
||||
client.send("2003", readyParam, response -> {
|
||||
log.debug("机器人 {} 发送准备请求响应: {}", robot.getRobotId(), response);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步发送准备消息并等待响应
|
||||
*/
|
||||
public boolean sendReadyMessageSync(RobotInfo robot) {
|
||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
||||
TaurusClient client = robotClients.get(robot.getRobotId());
|
||||
if (client == null) {
|
||||
log.warn("机器人 {} 没有有效的TCP连接,无法发送准备消息", robot.getRobotId());
|
||||
return false;
|
||||
}
|
||||
|
||||
ITObject readyParam = new TObject();
|
||||
readyParam.putString("session", robot.getSession() + "," + robot.getToken());
|
||||
readyParam.putString("robotId", String.valueOf(robot.getRobotId()));
|
||||
readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId()));
|
||||
|
||||
client.send("2003", readyParam, response -> {
|
||||
if (response != null && response.returnCode == 0) {
|
||||
future.complete(true);
|
||||
} else {
|
||||
future.complete(false);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return future.get(15, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
log.error("发送准备消息时发生异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendReadyMessageAsync(RobotInfo robot, Consumer<Boolean> callback) {
|
||||
TaurusClient client = robotClients.get(robot.getRobotId());
|
||||
if (client == null) {
|
||||
callback.accept(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ITObject readyParam = new TObject();
|
||||
readyParam.putString("session", robot.getSession() + "," + robot.getToken());
|
||||
readyParam.putString("robotId", String.valueOf(robot.getRobotId()));
|
||||
readyParam.putString("connecId", robotConnectionIds.get(robot.getRobotId()));
|
||||
|
||||
client.send("2003", readyParam, response -> {
|
||||
boolean success = response != null && response.returnCode == 0;
|
||||
callback.accept(success);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调接口
|
||||
*/
|
||||
|
|
@ -332,9 +511,8 @@ public class RobotConnectionHandler {
|
|||
*/
|
||||
public void sendCreateRoom(RobotInfo robot, int groupId, int wanfaId) {
|
||||
TaurusClient client = robotClients.get(robot.getRobotId());
|
||||
if (!client.isConnected()) {
|
||||
log.warn("机器人 {} 的TCP连接未激活,无法发送创建房间请求", robot.getRobotId());
|
||||
robotClients.remove(robot.getRobotId());
|
||||
if (client == null ) {
|
||||
connectRobot(robot);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -357,5 +535,4 @@ public class RobotConnectionHandler {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ public class RoomWanfaMatcher {
|
|||
}
|
||||
|
||||
isRunning = true;
|
||||
log.info("开始为玩法ID {} 启动房间轮询", wanfaId);
|
||||
log.info("开始为群组 {} 玩法ID {} 启动房间轮询", groupId, wanfaId);
|
||||
|
||||
//10秒轮询一次
|
||||
scheduler.scheduleWithFixedDelay(this::pollRooms, 0, 10, TimeUnit.SECONDS);
|
||||
|
|
@ -93,44 +93,37 @@ public class RoomWanfaMatcher {
|
|||
}
|
||||
|
||||
try (Jedis jedis0 = Redis.use().getJedis(); Jedis jedis11 = Redis.use("group1_db11").getJedis()) {
|
||||
Set<String> roomIds = jedis0.keys("room:*");
|
||||
if (roomIds.isEmpty()) {
|
||||
createRobotWanfaRoomTCP(groupId, this.wanfaId);
|
||||
}
|
||||
String playKey = "g{" + groupId + "}:play:" + wanfaId;
|
||||
String leftoverRobotStr = jedis11.hget(playKey, "leftover_robot");
|
||||
|
||||
int groupId = -1;
|
||||
|
||||
for (String roomId : roomIds) {
|
||||
int wanfaId = Integer.parseInt(jedis0.hget(roomId, "gpid"));
|
||||
if (wanfaId == this.wanfaId) {
|
||||
groupId = Integer.parseInt(jedis0.hget(roomId, "group"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (groupId != -1) {
|
||||
//检查该玩法是否配置了机器人
|
||||
String playKey = "g{" + groupId + "}:play:" + this.wanfaId;
|
||||
int leftoverRobot = Integer.parseInt(jedis11.hget(playKey, "leftover_robot"));
|
||||
int leftoverRobot = Integer.parseInt(leftoverRobotStr);
|
||||
|
||||
//根据leftover_robot数量管理机器人连接状态
|
||||
manageRobotConnections(leftoverRobot);
|
||||
|
||||
if (leftoverRobot > 0) {
|
||||
//查询当前群组和玩法的房间
|
||||
Set<String> roomIds = jedis0.keys("room:*");
|
||||
|
||||
if (roomIds.isEmpty()) {
|
||||
createRobotWanfaRoomTCP(groupId, this.wanfaId);
|
||||
return;
|
||||
}
|
||||
|
||||
//满人房间
|
||||
int fullRooms = 0;
|
||||
//所有房间
|
||||
int totalRooms = 0;
|
||||
List<String> roomsList = new ArrayList<>();
|
||||
|
||||
//统计所有2人房间
|
||||
//统计所有当前群组和玩法的2人房间
|
||||
for (String roomId : roomIds) {
|
||||
int currentWanfaId = Integer.parseInt(jedis0.hget(roomId, "gpid"));
|
||||
int status = Integer.parseInt(jedis0.hget(roomId, "status"));
|
||||
int currentGroup = Integer.parseInt(jedis0.hget(roomId, "group"));
|
||||
int maxPlayers = Integer.parseInt(jedis0.hget(roomId, "maxPlayers"));
|
||||
|
||||
//处理当前玩法maxPlayers为2的房间
|
||||
if (maxPlayers == 2 && currentWanfaId == this.wanfaId && status == 0) {
|
||||
//处理当前群组和玩法maxPlayers为2的房间
|
||||
if (maxPlayers == 2 && currentWanfaId == this.wanfaId && currentGroup == this.groupId) {
|
||||
totalRooms++;
|
||||
|
||||
String playersStr = jedis0.hget(roomId, "players");
|
||||
|
|
@ -150,13 +143,13 @@ public class RoomWanfaMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
//该玩法机器人房间满了 创建新房间
|
||||
if (fullRooms == totalRooms && totalRooms > 0) {
|
||||
//todo该玩法机器人房间满了 创建新房间
|
||||
/*if (fullRooms == totalRooms && totalRooms > 0) {
|
||||
createRobotWanfaRoomTCP(groupId, this.wanfaId);
|
||||
}
|
||||
return;
|
||||
}*/
|
||||
|
||||
for (String roomId : roomsList) {
|
||||
int group = Integer.parseInt(jedis0.hget(roomId, "group"));
|
||||
int status = Integer.parseInt(jedis0.hget(roomId, "status"));
|
||||
|
||||
//处理未开始的陪打房间
|
||||
|
|
@ -164,7 +157,7 @@ public class RoomWanfaMatcher {
|
|||
String playersStr = jedis0.hget(roomId, "players");
|
||||
RobotInfo robot = new RobotInfo();
|
||||
robot.setWanfaId(this.wanfaId);
|
||||
robot.setGroupId(group);
|
||||
robot.setGroupId(this.groupId);
|
||||
robot.setRoomId(roomId);
|
||||
robot.setRobotId(-1);
|
||||
|
||||
|
|
@ -189,9 +182,8 @@ public class RoomWanfaMatcher {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.error("轮询玩法ID {} 的房间时发生严重错误", wanfaId, t);
|
||||
log.error("轮询群组 {} 玩法ID {} 的房间时发生严重错误", groupId, wanfaId, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,31 +205,51 @@ public class RoomWanfaMatcher {
|
|||
}
|
||||
|
||||
//加入房间
|
||||
try {
|
||||
GroupRoomBusiness.joinRoom(robotInfo.getGroupId(), robotInfo.getRoomId(), robotInfo.getSession(), null);
|
||||
Thread.sleep(5000);
|
||||
robotConnectionHandler.sendJoinRoomMessage(robotInfo);
|
||||
Thread.sleep(1000);
|
||||
}catch (Exception e) {
|
||||
log.error("加入房间失败", e);
|
||||
processingRooms.remove(roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
//准备
|
||||
Thread.sleep(1000);
|
||||
robotConnectionHandler.sendReadyMessage(robotInfo);
|
||||
|
||||
RobotInfo finalRobotInfo = robotInfo;
|
||||
robotConnectionHandler.sendReadyMessageAsync(robotInfo, readySuccess -> {
|
||||
if (readySuccess) {
|
||||
roomToRobotMap.put(roomId, finalRobotInfo.getRobotId());
|
||||
updateRobotStatusRedis(finalRobotInfo);
|
||||
if (isRobot) {
|
||||
//6秒没有玩家加入 则退出房间
|
||||
robotConnectionHandler.readyTimeRobotExit(finalRobotInfo, joinRoomLock);
|
||||
}
|
||||
} else {
|
||||
System.out.println("机器人准备失败");
|
||||
}
|
||||
if (roomId != null) {
|
||||
processingRooms.remove(roomId);
|
||||
}
|
||||
});
|
||||
/*boolean readySuccess = robotConnectionHandler.sendReadyMessageSync(robotInfo);
|
||||
if (readySuccess) {
|
||||
//记录机器人和房间的关联
|
||||
roomToRobotMap.put(roomId, robotInfo.getRobotId());
|
||||
|
||||
updateRobotStatusRedis(robotInfo);
|
||||
|
||||
if (isRobot) {
|
||||
updateRobotStatusRedis(robotInfo, isRobot);
|
||||
//6秒没有玩家加入 则退出房间
|
||||
robotConnectionHandler.readyTimeRobotExit(robotInfo, joinRoomLock);
|
||||
} else {
|
||||
updateRobotStatusRedis(robotInfo, isRobot);
|
||||
}
|
||||
} else {
|
||||
//robotConnectionHandler.disconnectRobot(robotInfo.getRobotId());
|
||||
}*/
|
||||
} catch (Exception e) {
|
||||
if (robotInfo != null) {
|
||||
robotConnectionHandler.disconnectRobot(robotInfo.getRobotId());
|
||||
}
|
||||
} finally {
|
||||
if (roomId != null) {
|
||||
processingRooms.remove(roomId);
|
||||
//robotConnectionHandler.disconnectRobot(robotInfo.getRobotId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -309,10 +321,10 @@ public class RoomWanfaMatcher {
|
|||
private void manageRobotConnections(int currentLeftoverRobot) {
|
||||
if (lastLeftoverRobot != currentLeftoverRobot) {
|
||||
if (currentLeftoverRobot <= 0 && lastLeftoverRobot > 0) {
|
||||
//leftover_robot变为0 断开该玩法未连接的机器人
|
||||
//leftover_robot变为0 断开该玩法与robot_mj_cs的连接
|
||||
robotManager.disconnectExcessRobots(wanfaId, 0);
|
||||
} else if (currentLeftoverRobot > 0 && lastLeftoverRobot <= 0) {
|
||||
//leftover_robot变为正数 重新连接机器人
|
||||
//leftover_robot变为正数 重新连接机器人到robot_mj_cs
|
||||
robotManager.connectRequiredRobots(wanfaId, currentLeftoverRobot);
|
||||
}
|
||||
lastLeftoverRobot = currentLeftoverRobot;
|
||||
|
|
@ -341,28 +353,24 @@ public class RoomWanfaMatcher {
|
|||
}
|
||||
|
||||
/**
|
||||
* 机器人加入真人房间
|
||||
* 更新机器人状态 - 减少leftover_robot计数并更新机器人start状态
|
||||
*/
|
||||
private void updateRobotStatusRedis(RobotInfo robot, boolean isRobot) {
|
||||
private void updateRobotStatusRedis(RobotInfo robot) {
|
||||
try (Jedis jedis11 = Redis.use("group1_db11").getJedis(); Jedis jedis2 = Redis.use("group1_db2").getJedis()) {
|
||||
|
||||
String playKey = "g{" + robot.getGroupId() + "}:play:" + robot.getWanfaId();
|
||||
|
||||
if (!isRobot) {
|
||||
jedis11.hincrBy(playKey, "leftover_robot", -1);
|
||||
|
||||
String gRobotKey = "{grobot}:" + robot.getRobotId();
|
||||
jedis2.hset(gRobotKey, "start", "1");
|
||||
|
||||
log.debug("机器人 {} 加入非机器人房间,减少leftover_robot并更新redis2状态,群组={}, 玩法={}", robot.getRobotId(), robot.getGroupId(), robot.getWanfaId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("更新机器人状态时异常,机器人={}, 群组={}, 玩法={}", robot.getRobotId(), robot.getGroupId(), robot.getWanfaId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知房间处理完成,从processingRooms集合中移除指定房间
|
||||
* 通知房间处理完成 从processingRooms集合中移除指定房间
|
||||
*/
|
||||
public void roomProcessingFinish(String roomId) {
|
||||
processingRooms.remove(roomId);
|
||||
|
|
|
|||
|
|
@ -257,6 +257,8 @@ public class GameController implements IController{
|
|||
AccountBean acc = AccountCache.getAccount(session_key);
|
||||
int playerid = acc.id;
|
||||
|
||||
Global.logger.info("joinRoom - 正在处理加入请求-PlayerId: {}, Session: {}, Sender: {}", playerid, session_key, sender);
|
||||
|
||||
boolean full = owner.playerMapBySeat.size() >= owner.maxPlayers;
|
||||
boolean reload = owner.status == Constant.ROOM_STATUS_PLAYING;
|
||||
|
||||
|
|
@ -269,6 +271,7 @@ public class GameController implements IController{
|
|||
boolean new_player = false;
|
||||
if (owner.playerMapById.containsKey(playerid)) {
|
||||
player = owner.playerMapById.get(playerid);
|
||||
Global.logger.info("joinRoom - 已找到现有玩家-PlayerId: {}, Player: {}, isConnect: {}", playerid, player, player.isConnect);
|
||||
if (player.isReload) {
|
||||
player.isReload = false;
|
||||
player.stateMachine.changeState(Global.getState(PlayerInitState.class));
|
||||
|
|
@ -280,6 +283,7 @@ public class GameController implements IController{
|
|||
if (full) {
|
||||
sender.setHashId(null);
|
||||
delRoomSeat(session_key, owner.room_key);
|
||||
Global.logger.warn("joinRoom - 房间已满,拒绝玩家: {}", playerid);
|
||||
return ErrorCode.ROOM_CLOSE;
|
||||
}
|
||||
// 检测是否打开GPS 、IP检测
|
||||
|
|
@ -318,6 +322,7 @@ public class GameController implements IController{
|
|||
}
|
||||
}
|
||||
player = MainServer.instance.newPlayer(playerid, owner, session_key);
|
||||
Global.logger.info("joinRoom - 已创建新玩家-PlayerId: {}, Player: {}", playerid, player);
|
||||
player.hp.cur_hp = cur_hp;
|
||||
|
||||
owner.addPlayer(player, false, onseat);
|
||||
|
|
@ -333,6 +338,9 @@ public class GameController implements IController{
|
|||
if (StringUtil.isNotEmpty(gps_pos))
|
||||
player.gps_pos = gps_pos;
|
||||
|
||||
Global.logger.info("joinRoom - 播放器设置完成-PlayerId: {}, Nick: {}, isConnect: {}, Sender: {}",
|
||||
player.playerid, player.nick, player.isConnect, player.sender);
|
||||
|
||||
ITObject data = new TObject();
|
||||
if (reload) {
|
||||
data.putTObject("reloadInfo", owner.getReloadInfo(player));
|
||||
|
|
|
|||
|
|
@ -114,7 +114,8 @@ public abstract class MainServer extends Extension implements IEventListener{
|
|||
* @param recipients 客户端session列表
|
||||
*/
|
||||
public void sendEvent(String cmdName, ITObject params, List<Session> recipients) {
|
||||
TPServer.me().getController().sendEvent(cmdName, params, recipients);
|
||||
TPServer.me().getController().sendEv
|
||||
ent(cmdName, params, recipients);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -191,23 +191,28 @@ public class Player {
|
|||
public void setSender(Session sender) {
|
||||
// 服务器从崩溃中重启
|
||||
if (sender == null) {
|
||||
Global.logger.error("setSender - 为Player设置空会话: {}, PlayerId: {}, Nick: {}, changing isConnect to false",
|
||||
this.toString(), this.playerid, this.nick);
|
||||
this.isReload = true;
|
||||
this.isConnect = false;
|
||||
this.stateMachine.changeState(Global.getState(PlayerReloadState.class));
|
||||
return;
|
||||
}
|
||||
if (this.isConnect && this.sender != null && this.sender != sender) {
|
||||
Global.logger.warn("setSender - 替换Player的现有会话: {}, PlayerId: {}, Old sender: {}, New sender: {}",
|
||||
this.toString(), this.playerid, this.sender, sender);
|
||||
Global.sessionMgr.deleteSession(this.sender);
|
||||
TPServer.me().getController().disconnect(this.sender);
|
||||
}
|
||||
// 已经连接
|
||||
this.sender = sender;
|
||||
Global.sessionMgr.putPlayer(sender, this);
|
||||
this.isConnect = true;
|
||||
this.isConnect = true; // 这里设置连接状态为 true
|
||||
Global.logger.info("setSender - 已成功为玩家设置会话: {}, PlayerId: {}, Nick: {}, isConnect: true",
|
||||
this.toString(), this.playerid, this.nick);
|
||||
// if (this.stateMachine.curState == null || this.isReload) {
|
||||
// this.stateMachine.changeState(Global.getState(PlayerInitState.class));
|
||||
this.isReload = false;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -416,8 +421,13 @@ public class Player {
|
|||
* @param param
|
||||
*/
|
||||
public void sendEvent(String cmd, ITObject param) {
|
||||
if (!this.isConnect)
|
||||
if (!this.isConnect) {
|
||||
Global.logger.error("sendEvent failed - Player: {}, PlayerId: {}, Nick: {}, isConnect: false, Cmd: {}",
|
||||
this.toString(), this.playerid, this.nick, cmd);
|
||||
return;
|
||||
}
|
||||
Global.logger.info("sendEvent success - Player: {}, PlayerId: {}, Nick: {}, isConnect: true, Cmd: {}",
|
||||
this.toString(), this.playerid, this.nick, cmd);
|
||||
MainServer.instance.sendEvent(cmd, param, this.sender);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -721,9 +721,13 @@ public class Room implements Runnable {
|
|||
* @param reload
|
||||
*/
|
||||
public void addPlayer(Player player, boolean reload, boolean onseat) {
|
||||
Global.logger.info("addPlayer-将玩家添加到房间-房间: {}, PlayerId: {}, Nick: {}, Reload: {}, OnSeat: {}",
|
||||
this.roomid, player.playerid, player.nick, reload, onseat);
|
||||
|
||||
if (!reload) {
|
||||
if (onseat) {
|
||||
player.seat = this.getSeat();
|
||||
Global.logger.info("addPlayer-分配的座位: {} to player: {}", player.seat, player.playerid);
|
||||
}
|
||||
Redis.use().hset(player.session_id, "seat", player.seat + "");
|
||||
if (groupId > 0) {
|
||||
|
|
@ -740,10 +744,14 @@ public class Room implements Runnable {
|
|||
}
|
||||
if (onseat) {
|
||||
this.playerMapBySeat.put(player.seat, player);
|
||||
Global.logger.info("addPlayer-将玩家添加到座位映射-座位 - Seat: {}, Player: {}", player.seat, player.playerid);
|
||||
} else {
|
||||
this.playerMapBySpectator.put(player.playerid, player);
|
||||
Global.logger.info("addPlayer-将玩家添加到观众映射中 - PlayerId: {}", player.playerid);
|
||||
}
|
||||
this.playerMapById.put(player.playerid, player);
|
||||
Global.logger.info("addPlayer-将玩家添加到ID映射中-房间中的玩家总数: {}, Player: {}",
|
||||
this.playerMapById.size(), player.playerid);
|
||||
|
||||
if (!reload && onseat) {
|
||||
updateRedisMap();
|
||||
|
|
@ -894,15 +902,33 @@ public class Room implements Runnable {
|
|||
if (!isActive)
|
||||
return;
|
||||
List<Session> list = new ArrayList<Session>();
|
||||
Global.logger.info("broadCastToClient-向房间中的玩家广播cmd: {}, to players in room: {}, total players: {}",
|
||||
cmd, this.roomid, this.playerMapById.size());
|
||||
|
||||
int skippedPlayers = 0;
|
||||
int addedPlayers = 0;
|
||||
|
||||
for (Entry<Integer, Player> entry : this.playerMapById.entrySet()) {
|
||||
Player player = entry.getValue();
|
||||
if (player.playerid == withOutPlayerid) {
|
||||
Global.logger.debug("broadCastToClient - 由于withOutPlayelid而跳过玩家: {}", player.playerid);
|
||||
continue;
|
||||
}
|
||||
if (!player.isConnect)
|
||||
if (!player.isConnect) {
|
||||
Global.logger.warn("broadCastToClient - 跳过玩家(未连接): {}, PlayerId: {}, Nick: {}, isConnect: {}",
|
||||
player.toString(), player.playerid, player.nick, player.isConnect);
|
||||
skippedPlayers++;
|
||||
continue;
|
||||
}
|
||||
Global.logger.info("broadCastToClient - 将玩家添加到广播列表: {}, PlayerId: {}, Nick: {}, isConnect: {}",
|
||||
player.toString(), player.playerid, player.nick, player.isConnect);
|
||||
list.add(player.sender);
|
||||
addedPlayers++;
|
||||
}
|
||||
|
||||
Global.logger.info("broadCastToClient - 最终广播列表大小: {}, Added: {}, Skipped: {}, Command: {}",
|
||||
list.size(), addedPlayers, skippedPlayers, cmd);
|
||||
|
||||
MainServer.instance.sendEvent(cmd, param, list);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import taurus.client.Message;
|
|||
import taurus.client.TaurusClient;
|
||||
import taurus.util.CardUtil;
|
||||
import taurus.util.ChangShaSuanFaTest;
|
||||
import taurus.util.HongZhongSuanFaTest;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
|
|
|||
|
|
@ -750,7 +750,8 @@ public class JiQiRens {
|
|||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
} else if ("108".equalsIgnoreCase(wanfaId)) {//转转麻将
|
||||
}
|
||||
else if ("108".equalsIgnoreCase(wanfaId)) {//转转麻将
|
||||
if ("811".equalsIgnoreCase(command)) {//初始化收手牌
|
||||
huNanZhuanZhuan.cardInHead(command, message, client);
|
||||
} else if ("812".equalsIgnoreCase(command)) {//出牌广播
|
||||
|
|
@ -779,21 +780,27 @@ public class JiQiRens {
|
|||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
} else if ("10".equalsIgnoreCase(wanfaId)) {
|
||||
}
|
||||
else if ("10".equalsIgnoreCase(wanfaId)) {
|
||||
//长沙麻将
|
||||
System.out.println("11111-----------command" + command);
|
||||
if ("811".equalsIgnoreCase(command)) {//初始化收手牌
|
||||
huNanChangSha.cardInHead(command, message, client);
|
||||
} else if ("812".equalsIgnoreCase(command)) {//出牌广播
|
||||
}
|
||||
else if ("812".equalsIgnoreCase(command)) {//出牌广播
|
||||
HuNanChangSha.drawCard(command, message);
|
||||
} else if ("819".equalsIgnoreCase(command)) {//摸牌
|
||||
}
|
||||
else if ("819".equalsIgnoreCase(command)) {//摸牌
|
||||
huNanChangSha.getCard(command, message);
|
||||
} else if ("813".equalsIgnoreCase(command)) {//出牌提示
|
||||
}
|
||||
else if ("813".equalsIgnoreCase(command)) {//出牌提示
|
||||
System.out.println("出牌++++++++++++++++++++++++");
|
||||
huNanChangSha.outCard(client);
|
||||
} else if ("814".equalsIgnoreCase(command)) {//放招提示
|
||||
}
|
||||
else if ("814".equalsIgnoreCase(command)) {//放招提示
|
||||
huNanChangSha.actionCard(param, client);
|
||||
} else if ("2009".equalsIgnoreCase(command)) {
|
||||
}
|
||||
else if ("2009".equalsIgnoreCase(command)) {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
sleepTime(1000);
|
||||
if (i == 6) {
|
||||
|
|
@ -872,7 +879,8 @@ public class JiQiRens {
|
|||
|
||||
}
|
||||
}
|
||||
} else if ("817".equalsIgnoreCase(command)) {//结算
|
||||
}
|
||||
else if ("817".equalsIgnoreCase(command)) {//结算
|
||||
huNanChangSha.getChangShaCardInhand().clear();
|
||||
huNanChangSha.getChuGuoCardInhand().clear();
|
||||
huNanChangSha.getpongGroup().clear();
|
||||
|
|
@ -928,11 +936,15 @@ public class JiQiRens {
|
|||
ready();
|
||||
//写定时器
|
||||
|
||||
} else if ("815".equalsIgnoreCase(command)) { //服务器通知客户端有玩家执行了操作
|
||||
}
|
||||
else if ("815".equalsIgnoreCase(command)) { //服务器通知客户端有玩家执行了操作
|
||||
huNanChangSha.shanchuchuguopai(param);
|
||||
} else if ("820".equalsIgnoreCase(command)) {//换牌提示
|
||||
}
|
||||
else if ("820".equalsIgnoreCase(command)) {//换牌提示
|
||||
huNanChangSha.changePlayer(command, message);
|
||||
} else if ("2008".equalsIgnoreCase(command)) { //解散房间时候恢复机器人账号可以使用
|
||||
}
|
||||
else if ("2008".equalsIgnoreCase(command)) {
|
||||
//解散房间时候恢复机器人账号可以使用
|
||||
// Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||
// String key = "g{" + groupId + "}:play:" + pid;
|
||||
//
|
||||
|
|
@ -971,21 +983,24 @@ public class JiQiRens {
|
|||
// } catch (SQLException e) {
|
||||
// System.out.println(e);
|
||||
// }
|
||||
} else if ("825".equalsIgnoreCase(command)) {
|
||||
}
|
||||
else if ("825".equalsIgnoreCase(command)) {
|
||||
ITObject params = TObject.newInstance();
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 1);
|
||||
client.send("612", params, response -> {
|
||||
System.out.println("操作成功: " + response.returnCode);
|
||||
});
|
||||
} else if ("822".equalsIgnoreCase(command)) {
|
||||
}
|
||||
else if ("822".equalsIgnoreCase(command)) {
|
||||
ITObject params = TObject.newInstance();
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 1);
|
||||
client.send("612", params, response -> {
|
||||
System.out.println("操作成功: " + response.returnCode);
|
||||
});
|
||||
} else if ("835".equalsIgnoreCase(command)) { //听牌天听
|
||||
}
|
||||
else if ("835".equalsIgnoreCase(command)) { //听牌天听
|
||||
ITObject params = TObject.newInstance();
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 1);
|
||||
|
|
|
|||
|
|
@ -158,6 +158,11 @@ public class EXGameController extends GameController {
|
|||
owner.getRoom().currenDiscardSeat = owner.seat;
|
||||
ITObject param = new TObject();
|
||||
param.putBoolean("auto",owner.autoOutCard);
|
||||
|
||||
// 添加调试日志,检查连接状态
|
||||
Global.logger.info("discardTipEvent - Player: {}, PlayerId: {}, Seat: {}, isConnect: {}, Sender: {}",
|
||||
owner.nick, owner.playerid, owner.seat, owner.isConnect, owner.sender);
|
||||
|
||||
owner.sendEvent(Config.GAME_EVT_DISCARD_TIP, param);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,36 +98,33 @@ public class Config {
|
|||
|
||||
public static final String GAME_EVT_TING = "836";
|
||||
|
||||
public static final String CREATE_ROOM_ROBOT = "create_room_for_robot";
|
||||
// 初始化连接协议
|
||||
public static final String INIT_CONNECTION = "init_connection";
|
||||
|
||||
/**
|
||||
* 加入房间 - robot_mgr to robot_mj_cs 的内部协议号
|
||||
*/
|
||||
// 创建房间机器人协议
|
||||
public static final String CREATE_ROOM_ROBOT = "create_room_for_robot";
|
||||
|
||||
// 加入房间协议
|
||||
public static final String JOIN_ROOM = "2002";
|
||||
|
||||
/**
|
||||
* 发送准备 - robot_mgr to robot_mj_cs 的内部协议号
|
||||
*/
|
||||
public static final String GAME_READY = "2003";
|
||||
|
||||
/**
|
||||
* 退出房间 - robot_mgr to robot_mj_cs 的内部协议号
|
||||
*/
|
||||
public static final String EXIT_ROOM = "2005";
|
||||
|
||||
/**
|
||||
* 发送准备 - robot_mj_cs to game_mj_cs 的协议号
|
||||
*/
|
||||
public static final String GAME_READY_CS = "1003";
|
||||
|
||||
/**
|
||||
* 加入房间 - robot_mgr to game_mj_cs 的内部协议号
|
||||
*/
|
||||
// 加入房间CS协议
|
||||
public static final String JOIN_ROOM_CS = "1002";
|
||||
|
||||
/**
|
||||
* 退出房间 - robot_mgr to game_mj_cs 的内部协议号
|
||||
*/
|
||||
public static final String EXIT_ROOM_CS = "1005";
|
||||
// 游戏准备协议
|
||||
public static final String GAME_READY = "2003";
|
||||
|
||||
// 游戏准备CS协议
|
||||
public static final String GAME_READY_CS = "1003";
|
||||
|
||||
// 退出房间协议
|
||||
public static final String EXIT_ROOM = "2004";
|
||||
|
||||
// 退出房间CS协议
|
||||
public static final String EXIT_ROOM_CS = "1004";
|
||||
|
||||
// 其他可能的游戏相关协议
|
||||
public static final String GAME_CHAT = "1006";
|
||||
|
||||
// 心跳协议
|
||||
public static final String HEARTBEAT = "9000";
|
||||
}
|
||||
|
|
@ -5,12 +5,16 @@ import com.robot.GameInterceptor;
|
|||
import com.robot.MainServer;
|
||||
import com.taurus.core.entity.ITObject;
|
||||
import com.taurus.core.entity.TObject;
|
||||
import com.taurus.core.events.Event;
|
||||
import com.taurus.core.events.IEventListener;
|
||||
import com.taurus.core.routes.ActionKey;
|
||||
import com.taurus.core.util.ICallback;
|
||||
import com.taurus.core.util.StringUtil;
|
||||
import com.taurus.permanent.data.Session;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import robot.mj.handler.HuNanChangShaHandler;
|
||||
import robot.mj.handler.HuNanChangSha;
|
||||
import taurus.client.Message;
|
||||
import taurus.client.MessageResponse;
|
||||
import taurus.client.TaurusClient;
|
||||
|
||||
|
|
@ -20,6 +24,8 @@ import taurus.client.TaurusClient;
|
|||
public class EXGameController extends GameController {
|
||||
private static final Logger log = LoggerFactory.getLogger(EXGameController.class);
|
||||
|
||||
private static RobotConnectionManager connectionManager = new RobotConnectionManager();
|
||||
|
||||
public EXGameController() {
|
||||
super();
|
||||
log.info("长沙麻将游戏控制器已初始化");
|
||||
|
|
@ -35,12 +41,13 @@ public class EXGameController extends GameController {
|
|||
|
||||
String type = params.getString("type");
|
||||
String client = params.getString("client");
|
||||
String sessionToken = params.getString("session");
|
||||
|
||||
log.info("客户端类型: {}, 客户端标识: {}", type, client);
|
||||
|
||||
// 验证参数
|
||||
//验证参数
|
||||
if ("manager_connection".equals(type) && "robot_mgr".equals(client)) {
|
||||
// 返回成功响应
|
||||
//返回成功响应
|
||||
ITObject response = TObject.newInstance();
|
||||
response.putString("status", "success");
|
||||
response.putString("message", "初始化成功");
|
||||
|
|
@ -53,7 +60,6 @@ public class EXGameController extends GameController {
|
|||
ITObject response = TObject.newInstance();
|
||||
response.putString("status", "failed");
|
||||
response.putString("message", "参数验证失败");
|
||||
|
||||
MainServer.instance.sendResponse(gid, 1, response, session);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -73,8 +79,6 @@ public class EXGameController extends GameController {
|
|||
@ActionKey(value = Config.CREATE_ROOM_ROBOT, validate = GameInterceptor.NOT_PLAYER)
|
||||
public void createRoomForRobot(Session session, ITObject params, int gid) {
|
||||
try {
|
||||
log.info("收到robot_mgr的创建房间请求,Session: {}, GID: {}, 参数: {}", session, gid, params);
|
||||
|
||||
int reqGroupId = params.getInt("groupId");
|
||||
int wanfaId = params.getInt("wanfaId");
|
||||
int robotId = params.getInt("robotId");
|
||||
|
|
@ -97,17 +101,9 @@ public class EXGameController extends GameController {
|
|||
MainServer.instance.sendResponse(gid, 0, response, session);
|
||||
} else {
|
||||
log.error("创建房间失败,群组ID: {}, 玩法ID: {}", reqGroupId, wanfaId);
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "创建房间失败");
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理创建房间请求时发生错误", e);
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "服务器内部错误: " + e.getMessage());
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,32 +113,40 @@ public class EXGameController extends GameController {
|
|||
@ActionKey(value = Config.JOIN_ROOM, validate = GameInterceptor.NOT_PLAYER)
|
||||
public void joinRoom(Session session, ITObject params, int gid) {
|
||||
try {
|
||||
log.info("收到robot_mgr的机器人准备请求,Session: {}, GID: {}, 参数: {}", session, gid, params);
|
||||
System.out.println("收到加入房间请求,Session: {}, GID: {}, 参数: {}");
|
||||
|
||||
//在收到加入房间请求 建立到长沙麻将游戏服务器的连接
|
||||
TaurusClient client = GameServerConnector.getCsMjGameServerConnection();
|
||||
String connecId = params.getString("connecId");
|
||||
TaurusClient client = null;// getCsMjGameServerConnection(connecId);
|
||||
client = new TaurusClient("8.134.76.43" + ":" + "6311", "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||
client.connect();
|
||||
if (client == null) {
|
||||
log.error("无法连接到长沙麻将游戏服务器");
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "无法连接到游戏服务器");
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
return;
|
||||
}
|
||||
|
||||
//设置session和token
|
||||
String sessionToken = params.getString("session");
|
||||
if (sessionToken != null && sessionToken.contains(",")) {
|
||||
String[] sessionParts = sessionToken.split(",");
|
||||
if (sessionParts.length >= 2) {
|
||||
String actualSessionFromParams = sessionParts[0];
|
||||
String token = sessionParts[1];
|
||||
connectionManager.setSessionAndToken(actualSessionFromParams, token);
|
||||
}
|
||||
}
|
||||
|
||||
//发送准备请求
|
||||
//发送加入房间请求
|
||||
client.send(Config.JOIN_ROOM_CS, params, response -> {
|
||||
System.out.println("进入房间成功: " + response.returnCode);
|
||||
System.out.println("csmj OnEvent " + response.returnCode);
|
||||
log.info("加入房间请求发送结果: returnCode={}", response.returnCode);
|
||||
});
|
||||
client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() {
|
||||
@Override
|
||||
public void handleEvent(Event event) {
|
||||
System.out.println("csmj OnEvent 能监听:jefe " );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "服务器内部错误: " + e.getMessage());
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
log.error("处理加入房间请求时发生错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,79 +156,69 @@ public class EXGameController extends GameController {
|
|||
@ActionKey(value = Config.GAME_READY, validate = GameInterceptor.NOT_PLAYER)
|
||||
public void robotReadyRoom(Session session, ITObject params, int gid) {
|
||||
try {
|
||||
log.info("收到robot_mgr的机器人准备请求,Session: {}, GID: {}, 参数: {}", session, gid, params);
|
||||
|
||||
//在收到准备请求 建立到长沙麻将游戏服务器的连接
|
||||
TaurusClient client = GameServerConnector.getCsMjGameServerConnection();
|
||||
String connecId = params.getString("connecId");
|
||||
TaurusClient client = getCsMjGameServerConnection(connecId);
|
||||
System.out.println("session: " + session);
|
||||
System.out.println("client: " + client);
|
||||
if (client == null) {
|
||||
log.error("无法连接到长沙麻将游戏服务器");
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "无法连接到游戏服务器");
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
return;
|
||||
}
|
||||
|
||||
params.del("connecId");
|
||||
client.send(Config.GAME_READY_CS, params, null);
|
||||
|
||||
|
||||
//发送准备请求
|
||||
client.send(Config.GAME_READY_CS, params, new ICallback<MessageResponse>() {
|
||||
@Override
|
||||
public void action(MessageResponse readyResponse) {
|
||||
log.info("机器人准备请求发送结果: returnCode={}", readyResponse.returnCode);
|
||||
|
||||
if (readyResponse.returnCode == 0) {
|
||||
log.info("机器人准备成功");
|
||||
|
||||
ITObject successResponse = TObject.newInstance();
|
||||
successResponse.putString("status", "success");
|
||||
successResponse.putString("message", "机器人准备成功");
|
||||
MainServer.instance.sendResponse(gid, 0, successResponse, session);
|
||||
} else {
|
||||
log.error("机器人准备失败,返回码: {}", readyResponse.returnCode);
|
||||
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "机器人准备失败");
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ITObject paramsReq = TObject.newInstance();
|
||||
paramsReq.putString("status", "success");
|
||||
MainServer.instance.sendResponse(gid, 0, paramsReq, session);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("处理机器人准备请求时发生错误", e);
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "服务器内部错误: " + e.getMessage());
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收来自robot_mgr的退出房间协议
|
||||
* 断开与game_mj_cs的TCP连接
|
||||
*/
|
||||
@ActionKey(value = Config.EXIT_ROOM, validate = GameInterceptor.NOT_PLAYER)
|
||||
public void exitRoom(Session session, ITObject params, int gid) {
|
||||
try {
|
||||
log.info("收到robot_mgr的机器人准备请求,Session: {}, GID: {}, 参数: {}", session, gid, params);
|
||||
String connecId = params.getString("connecId");
|
||||
TaurusClient client = getCsMjGameServerConnection(connecId);
|
||||
|
||||
TaurusClient client = GameServerConnector.getCsMjGameServerConnection();
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//发送退出房间请求
|
||||
client.send(Config.EXIT_ROOM_CS, params, response -> {
|
||||
System.out.println("退出房间成功: " + response.returnCode);
|
||||
});
|
||||
|
||||
|
||||
//断开与游戏服务器的连接
|
||||
//connectionManager.disconnectFromGameServer(connecId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
ITObject errorResponse = TObject.newInstance();
|
||||
errorResponse.putInt("error", 1);
|
||||
errorResponse.putString("message", "服务器内部错误: " + e.getMessage());
|
||||
MainServer.instance.sendResponse(gid, 1, errorResponse, session);
|
||||
log.error("处理退出房间请求时发生错误", e);
|
||||
String connecId = params.getString("connecId");
|
||||
connectionManager.disconnectFromGameServer(connecId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据机器人ID和连接ID获取长沙麻将游戏服务器连接
|
||||
*/
|
||||
public static TaurusClient getCsMjGameServerConnection(String connecId) {
|
||||
TaurusClient taurusClient = connectionManager.getGameServerConnection(connecId);
|
||||
if (taurusClient != null) {
|
||||
return taurusClient;
|
||||
}
|
||||
|
||||
boolean connected = connectionManager.connectToGameServer(connecId);
|
||||
if (connected) {
|
||||
taurusClient = connectionManager.getGameServerConnection(connecId);
|
||||
if (taurusClient != null) {
|
||||
return taurusClient;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
package robot.mj;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.robot.GameController;
|
||||
import com.robot.Global;
|
||||
|
|
@ -14,7 +19,7 @@ import com.taurus.permanent.core.TPEvents;
|
|||
import com.taurus.permanent.data.Session;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import robot.mj.handler.HuNanChangShaHandler;
|
||||
import robot.mj.handler.HuNanChangSha;
|
||||
|
||||
/**
|
||||
* 长沙麻将机器人主服务器
|
||||
|
|
@ -27,7 +32,11 @@ public class EXMainServer extends MainServer{
|
|||
private static RobotConnectionManager robotConnectionManager;
|
||||
|
||||
//长沙麻将AI处理器
|
||||
private static HuNanChangShaHandler huNanChangShaHandler;
|
||||
private static HuNanChangSha huNanChangSha;
|
||||
|
||||
//心跳和重连
|
||||
private final Map<String, ScheduledFuture<?>> serverHeartbeatTasks = new ConcurrentHashMap<>();
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
|
|
@ -41,24 +50,26 @@ public class EXMainServer extends MainServer{
|
|||
initializeComponents();
|
||||
|
||||
//添加会话断开事件监听器
|
||||
TPServer.me().getEventManager().addEventListener(TPEvents.EVENT_SESSION_DISCONNECT, new IEventListener() {
|
||||
/*TPServer.me().getEventManager().addEventListener(TPEvents.EVENT_SESSION_DISCONNECT, new IEventListener() {
|
||||
@Override
|
||||
public void handleEvent(Event event) {
|
||||
Session session = (Session) event.getParameter(TPEvents.PARAM_SESSION);
|
||||
log.info("会话断开: {}", session.getId());
|
||||
String sessionId = String.valueOf(session.getId());
|
||||
log.info("会话断开: {}", sessionId);
|
||||
System.out.println("robot_mj_cs会话断开: " + sessionId);
|
||||
|
||||
Global.sessionMgr.disconnect(session);
|
||||
}
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化组件
|
||||
*/
|
||||
private void initializeComponents() {
|
||||
robotConnectionManager = RobotConnectionManager.getInstance();
|
||||
|
||||
robotConnectionManager = new RobotConnectionManager();
|
||||
//长沙麻将AI处理器
|
||||
huNanChangShaHandler = new HuNanChangShaHandler();
|
||||
huNanChangSha = new HuNanChangSha();
|
||||
|
||||
log.info("长沙麻将机器人服务器组件初始化完成");
|
||||
}
|
||||
|
|
@ -68,14 +79,12 @@ public class EXMainServer extends MainServer{
|
|||
return new EXRoom(roomid, redis_room_map);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Player newPlayer(int playerid, Room room, String session_id) {
|
||||
return new EXPlayer(playerid, room, session_id);
|
||||
public Player newPlayer(int i, Room room, String s) {
|
||||
return new EXPlayer(i, room, s);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected GameController newController() {
|
||||
return new EXGameController();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
package robot.mj;
|
||||
|
||||
import com.taurus.core.util.ICallback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import taurus.client.TaurusClient;
|
||||
import taurus.client.Message;
|
||||
import taurus.client.SocketCode;
|
||||
import taurus.client.MessageResponse;
|
||||
import com.taurus.core.entity.ITObject;
|
||||
import com.taurus.core.entity.TObject;
|
||||
import com.taurus.core.events.Event;
|
||||
import com.taurus.core.events.IEventListener;
|
||||
|
||||
/**
|
||||
* 游戏服务器连接器 - 管理到长沙麻将游戏服务器的连接
|
||||
*/
|
||||
public class GameServerConnector {
|
||||
private static final Logger log = LoggerFactory.getLogger(GameServerConnector.class);
|
||||
|
||||
//长沙麻将服务器地址
|
||||
private static final String CS_MJ_SERVER_ADDRESS = "8.134.76.43:6311";
|
||||
|
||||
//长沙麻将服务器连接
|
||||
private static volatile TaurusClient csMjConnection;
|
||||
|
||||
/**
|
||||
* 获取长沙麻将游戏服务器连接
|
||||
*/
|
||||
public static TaurusClient getCsMjGameServerConnection() {
|
||||
if (csMjConnection != null && csMjConnection.isConnected()) {
|
||||
return csMjConnection;
|
||||
}
|
||||
|
||||
synchronized (GameServerConnector.class) {
|
||||
if (csMjConnection != null) {
|
||||
return csMjConnection;
|
||||
}
|
||||
|
||||
csMjConnection = createConnection(CS_MJ_SERVER_ADDRESS, "长沙麻将");
|
||||
if (csMjConnection != null) {
|
||||
log.info("成功创建到长沙麻将游戏服务器的连接: {}", CS_MJ_SERVER_ADDRESS);
|
||||
} else {
|
||||
log.error("无法连接到长沙麻将游戏服务器: {}", CS_MJ_SERVER_ADDRESS);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return csMjConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建到游戏服务器的连接
|
||||
*/
|
||||
private static TaurusClient createConnection(String serverAddress, String gameName) {
|
||||
try {
|
||||
log.info("正在连接到{}服务器: {}", gameName, serverAddress);
|
||||
|
||||
TaurusClient client = new TaurusClient(serverAddress, "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||
|
||||
//添加连接事件监听器
|
||||
client.addEventListener(TaurusClient.NetClientEvent.Connect, new IEventListener() {
|
||||
@Override
|
||||
public void handleEvent(Event event) {
|
||||
SocketCode code = (SocketCode) event.getParameter("code");
|
||||
if (code == SocketCode.Connect) {
|
||||
log.info("成功连接到{}服务器: {}", gameName, serverAddress);
|
||||
} else {
|
||||
log.warn("连接到{}服务器失败: {},错误码: {}", gameName, serverAddress, code);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//添加消息接收事件监听器
|
||||
client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() {
|
||||
@Override
|
||||
public void handleEvent(Event event) {
|
||||
Message message = (Message) event.getParameter("msg");
|
||||
if (message != null) {
|
||||
log.debug("从{}服务器收到消息: 命令={},参数={}", gameName, message.command, message.param);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.connect();
|
||||
Thread.sleep(100);
|
||||
|
||||
return client;
|
||||
} catch (Exception e) {
|
||||
log.error("连接到{}服务器时发生错误: {}", gameName, serverAddress, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送协议到长沙麻将游戏服务器
|
||||
*/
|
||||
public static void sendProtocolToCsMj(String protocol, ITObject params, ICallback<MessageResponse> responseCallback) {
|
||||
TaurusClient connection = getCsMjGameServerConnection();
|
||||
if (connection != null) {
|
||||
connection.send(protocol, params, responseCallback);
|
||||
} else {
|
||||
log.error("无法发送协议到长沙麻将游戏服务器,连接不可用");
|
||||
// 在回调中返回错误
|
||||
if (responseCallback != null) {
|
||||
MessageResponse errorResponse = new MessageResponse();
|
||||
errorResponse.returnCode = 1;
|
||||
errorResponse.messageData = new taurus.client.Message();
|
||||
errorResponse.messageData.param = new com.taurus.core.entity.TObject();
|
||||
responseCallback.action(errorResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.taurus.core.entity.ITArray;
|
|||
import com.taurus.core.entity.ITObject;
|
||||
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.plugin.database.DataBase;
|
||||
import com.taurus.core.util.Logger;
|
||||
|
|
@ -11,9 +12,16 @@ import com.taurus.core.util.StringUtil;
|
|||
import taurus.client.Message;
|
||||
import taurus.client.TaurusClient;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import robot.mj.handler.HuNanChangShaHandler;
|
||||
import robot.mj.handler.HuNanChangSha;
|
||||
import taurus.client.business.GroupRoomBusiness;
|
||||
import taurus.util.ChangShaSuanFaTest;
|
||||
import taurus.util.TinHuChi;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static java.lang.Thread.sleep;
|
||||
|
||||
/**
|
||||
* 机器人连接管理器 - 管理与游戏服务器的连接
|
||||
|
|
@ -21,59 +29,105 @@ import java.util.*;
|
|||
public class RobotConnectionManager {
|
||||
|
||||
private Logger log = Logger.getLogger(RobotConnectionManager.class);
|
||||
private static RobotConnectionManager instance;
|
||||
private Map<String, TaurusClient> gameClients;
|
||||
//存储全局的长沙麻将AI处理器
|
||||
private HuNanChangShaHandler huNanChangShaHandler;
|
||||
private HuNanChangSha huNanChangSha;
|
||||
|
||||
private RobotConnectionManager() {
|
||||
private final String host="8.134.76.43";
|
||||
private final int port=6311;
|
||||
|
||||
private TaurusClient client = null;
|
||||
|
||||
private Map<Integer, List<Integer>> playerOutcardsMap = new HashMap<>();
|
||||
|
||||
private Map<Integer, List<Integer>> playerchisMap = new HashMap<>();
|
||||
|
||||
private Map<Integer, List<Integer>> playerpengsMap = new HashMap<>();
|
||||
|
||||
private Map<Integer, List<Integer>> playermingsMap = new HashMap<>();
|
||||
|
||||
private Map<Integer, List<Integer>> playerzisMap = new HashMap<>();
|
||||
|
||||
// 全局变量定义
|
||||
private int groupId = 0;
|
||||
private int pid = 0;
|
||||
private int playerId = 0;
|
||||
private String playKey = "";
|
||||
private Map<Integer, Integer> count = new HashMap<Integer, Integer>();
|
||||
|
||||
|
||||
public RobotConnectionManager() {
|
||||
gameClients = new HashMap<>();
|
||||
huNanChangShaHandler = new HuNanChangShaHandler();
|
||||
huNanChangSha = new HuNanChangSha();
|
||||
}
|
||||
|
||||
public static synchronized RobotConnectionManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new RobotConnectionManager();
|
||||
/**
|
||||
* 获取所有session列表
|
||||
*/
|
||||
public Set<String> getAllSessions() {
|
||||
return new HashSet<>(gameClients.keySet());
|
||||
}
|
||||
return instance;
|
||||
|
||||
/**
|
||||
* 设置会话和令牌
|
||||
*/
|
||||
public void setSessionAndToken(String session, String token) {
|
||||
huNanChangSha.session = session;
|
||||
huNanChangSha.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接到长沙麻将游戏服务器
|
||||
*/
|
||||
public boolean connectToGameServer(String host, int port, int playerId, int groupId, String roomId) {
|
||||
public boolean connectToGameServer(String connecId) {
|
||||
try {
|
||||
//检查是否已经连接到该房间
|
||||
if (gameClients.containsKey(roomId)) {
|
||||
TaurusClient existingClient = gameClients.get(roomId);
|
||||
//检查是否已经连接
|
||||
if (gameClients.containsKey(connecId)) {
|
||||
TaurusClient existingClient = gameClients.get(connecId);
|
||||
if (existingClient.isConnected()) {
|
||||
log.info("房间 " + roomId + " 已经连接到游戏服务器: " + host + ":" + port);
|
||||
return true;
|
||||
} else {
|
||||
//如果连接已断开 先移除旧的客户端
|
||||
gameClients.remove(roomId);
|
||||
//连接断开 移除旧的客户端
|
||||
disconnectFromGameServer(connecId);
|
||||
}
|
||||
}
|
||||
|
||||
//创建Taurus客户端
|
||||
TaurusClient client = new TaurusClient(host + ":" + port, "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||
|
||||
//连接到游戏服务器
|
||||
client = new TaurusClient(host + ":" + port, "game", TaurusClient.ConnectionProtocol.Tcp);
|
||||
client.connect();
|
||||
|
||||
//添加事件监听器处理网络消息
|
||||
client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new com.taurus.core.events.IEventListener() {
|
||||
|
||||
|
||||
System.out.println("csmj OnEvent " + client.getSession());
|
||||
/// GroupRoomBusiness.joinRoom(383709, "room:592473", "{user}:101555", null);
|
||||
//Thread.sleep(5000);
|
||||
System.out.println("csmj OnEvent2 " + client.getSession());
|
||||
|
||||
|
||||
|
||||
|
||||
System.out.println("client.getSession()1111" +client.getSession());
|
||||
System.out.println("client.getId()1111" +client.getId());
|
||||
//添加事件监听器处理网络消息和连接断开
|
||||
client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new IEventListener() {
|
||||
@Override
|
||||
public void handleEvent(Event event) {
|
||||
System.out.println("csmj OnEvent 能监听: " );
|
||||
|
||||
//获取 msg
|
||||
Message message = (Message) event.getParameter("msg");
|
||||
if (message == null) {
|
||||
System.out.println("在OnEvent中收到空消息");
|
||||
return;
|
||||
}
|
||||
|
||||
ITObject param = message.param;
|
||||
//回调协议号
|
||||
String command = message.command;
|
||||
System.out.println("csmj OnEvent msg: " + command);
|
||||
|
||||
// 添加更详细的日志
|
||||
System.out.println("收到协议命令: " + command + " params: " + param);
|
||||
|
||||
//根据玩法ID处理不同的回调
|
||||
if (StringUtil.isNotEmpty(command)) {
|
||||
|
|
@ -84,282 +138,248 @@ public class RobotConnectionManager {
|
|||
});
|
||||
|
||||
//添加连接状态监听器
|
||||
client.addEventListener(TaurusClient.NetClientEvent.Connect, new com.taurus.core.events.IEventListener() {
|
||||
client.addEventListener(TaurusClient.NetClientEvent.Connect, new IEventListener() {
|
||||
@Override
|
||||
public void handleEvent(Event event) {
|
||||
log.info("长沙麻将AI机器人连接成功到游戏服务器: " + host + ":" + port + ",房间: " + roomId);
|
||||
|
||||
//通知robot_mgr连接成功
|
||||
notifyConnectionStatus(roomId, playerId + "", host, port, "connected");
|
||||
System.out.println("Connect");
|
||||
System.out.println("csmj Connect connecId: " + connecId);
|
||||
}
|
||||
});
|
||||
|
||||
//添加断开连接监听器
|
||||
client.addEventListener(TaurusClient.NetClientEvent.OnEvent, new com.taurus.core.events.IEventListener() {
|
||||
@Override
|
||||
public void handleEvent(com.taurus.core.events.Event event) {
|
||||
log.info("长沙麻将AI机器人连接断开,房间: " + roomId);
|
||||
//从连接列表中移除断开的客户端
|
||||
for (Map.Entry<String, TaurusClient> entry : gameClients.entrySet()) {
|
||||
if (entry.getValue() == client) {
|
||||
gameClients.remove(entry.getKey());
|
||||
log.info("已从连接列表中移除断开的连接: " + entry.getKey());
|
||||
|
||||
//通知robot_mgr连接断开
|
||||
notifyConnectionStatus(entry.getKey(), playerId + "", "", 0, "disconnected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ITObject readyParam = new TObject();
|
||||
readyParam.putString("session","{user}:101555,c739ec6c3e93e9655e507397f78d857f");
|
||||
readyParam.putString("pos", "10,10");
|
||||
//readyParam.putString("connecId", "1");
|
||||
//pos
|
||||
client.send(Config.JOIN_ROOM_CS, readyParam, response -> {
|
||||
System.out.println("csmj OnEvent " + response.returnCode);
|
||||
log.info("加入房间请求发送结果: returnCode={}", response.returnCode);
|
||||
});
|
||||
|
||||
HuNanChangShaHandler.playerId = playerId;
|
||||
readyParam.del("connecId");
|
||||
sleep(5000);
|
||||
client.send(Config.GAME_READY_CS, readyParam, null);
|
||||
|
||||
gameClients.put(roomId, client);
|
||||
|
||||
log.info("长沙麻将AI机器人成功连接到游戏服务器: " + host + ":" + port + ",房间: " + roomId + ",玩家: " + playerId);
|
||||
System.out.println(client);
|
||||
System.out.println("client.getSession()2222" +client.getSession());
|
||||
System.out.println("client.getId()2222" +client.getId());
|
||||
gameClients.put(connecId, client);
|
||||
System.out.println("gameClients" + gameClients.get(connecId));
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("连接到游戏服务器时发生异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知robot_mgr连接状态
|
||||
* 断开与游戏服务器的连接
|
||||
*/
|
||||
private void notifyConnectionStatus(String roomId, String robotId, String host, int port, String status) {
|
||||
public void disconnectFromGameServer(String compositeKey) {
|
||||
TaurusClient client = gameClients.remove(compositeKey);
|
||||
if (client != null && client.isConnected()) {
|
||||
try {
|
||||
Jedis jedis = Redis.use("group1_db11").getJedis();
|
||||
try {
|
||||
String notificationKey = "robot_connection_notify:" + roomId + ":" + robotId;
|
||||
Map<String, String> notification = new HashMap<>();
|
||||
notification.put("status", status);
|
||||
if (!host.isEmpty()) {
|
||||
notification.put("game_server", host + ":" + port);
|
||||
}
|
||||
notification.put("room_id", roomId);
|
||||
notification.put("robot_id", robotId);
|
||||
notification.put("robot_server", "robot_mj_cs");
|
||||
notification.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
||||
|
||||
jedis.hmset(notificationKey, notification);
|
||||
jedis.expire(notificationKey, 300); //5分钟过期
|
||||
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
client.killConnection();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("断开客户端连接时发生异常", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
RobotConnectionManager rb=new RobotConnectionManager();
|
||||
boolean i = rb.connectToGameServer("1");
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据connecId获取游戏服务器连接
|
||||
*/
|
||||
public TaurusClient getGameServerConnection(String connecId) {
|
||||
TaurusClient client = gameClients.get(connecId);
|
||||
|
||||
if (client == null) {
|
||||
System.out.println("Attempting to reconnect: " + connecId);
|
||||
// 尝试重新连接
|
||||
connectToGameServer(connecId);
|
||||
client = gameClients.get(connecId);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理接收到的游戏协议
|
||||
*/
|
||||
private void handleProtocol(String command, taurus.client.Message message, TaurusClient client) {
|
||||
ITObject param = message.param;
|
||||
|
||||
try (Jedis jedis0 = Redis.use().getJedis()){
|
||||
switch (command) {
|
||||
case "811": //初始化手牌
|
||||
handleInitCards(command, message, client);
|
||||
huNanChangSha.cardInHead(command, message, client);
|
||||
break;
|
||||
case "812": //出牌广播
|
||||
handleDiscardBroadcast(command, message);
|
||||
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");
|
||||
|
||||
// 清空旧数据,用新数据完全覆盖
|
||||
playerOutcardsMap.clear();
|
||||
playerchisMap.clear();
|
||||
playerpengsMap.clear();
|
||||
playermingsMap.clear();
|
||||
playerzisMap.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>
|
||||
List<Integer> outcardsList = new ArrayList<>();
|
||||
for (int j = 0; j < outcardsArray.size(); j++) {
|
||||
outcardsList.add(outcardsArray.getInt(j));
|
||||
}
|
||||
|
||||
// 存储到Map中(覆盖旧数据)
|
||||
playerOutcardsMap.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));
|
||||
}
|
||||
playerchisMap.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));
|
||||
}
|
||||
playerpengsMap.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));
|
||||
}
|
||||
playermingsMap.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));
|
||||
}
|
||||
playerzisMap.put(playerId, outziList);
|
||||
}
|
||||
}
|
||||
HuNanChangSha.drawCard(command, message);
|
||||
break;
|
||||
case "819": //摸牌
|
||||
handleDrawCard(command, message);
|
||||
System.out.println("Handling 819 - Draw card");
|
||||
huNanChangSha.getCard(command, message);
|
||||
break;
|
||||
case "813": //出牌提示
|
||||
handleDiscardPrompt(client);
|
||||
System.out.println("Handling 813 - Discard card tip received!");
|
||||
huNanChangSha.outCard(client,playerOutcardsMap,playerchisMap,playerpengsMap,playermingsMap,playerzisMap);
|
||||
break;
|
||||
case "814": //操作提示(吃碰杠胡)
|
||||
handleActionPrompt(param, client);
|
||||
break;
|
||||
case "817": //结算
|
||||
handleSettlement(param, client);
|
||||
break;
|
||||
case "815": //服务器通知客户端有玩家执行了操作
|
||||
handleServerNotification(param);
|
||||
break;
|
||||
case "820": //换牌提示
|
||||
handleChangeCardTip(command, message);
|
||||
break;
|
||||
case "825":
|
||||
case "822":
|
||||
case "835": //听牌相关
|
||||
handleTingPai(command, client);
|
||||
break;
|
||||
case "2008": //解散房间
|
||||
handleRoomDisband();
|
||||
System.out.println("Handling 814 - Action tips (Chi, Peng, Gang, Hu)");
|
||||
huNanChangSha.actionCard(param, client);
|
||||
break;
|
||||
case "2009": //其他操作 - 房间检查和踢人逻辑
|
||||
handleRoomCheckAndKick(param, client);
|
||||
break;
|
||||
case "838": //补杠事件
|
||||
handleBuGang(param, client);
|
||||
break;
|
||||
case "816": //抢杠胡
|
||||
handleQiangGangHu(param, client);
|
||||
break;
|
||||
case "821": //吃牌
|
||||
handleChiCard(param, client);
|
||||
break;
|
||||
case "823": //杠后补牌
|
||||
handleGangBuPai(param, client);
|
||||
break;
|
||||
default:
|
||||
handleUnknownCommand(command, param, client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理初始化手牌协议
|
||||
*/
|
||||
private void handleInitCards(String command, taurus.client.Message message, TaurusClient client) {
|
||||
huNanChangShaHandler.cardInHead(command, message, client);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理出牌广播协议
|
||||
*/
|
||||
private void handleDiscardBroadcast(String command, taurus.client.Message message) {
|
||||
HuNanChangShaHandler.drawCard(command, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理摸牌协议
|
||||
*/
|
||||
private void handleDrawCard(String command, taurus.client.Message message) {
|
||||
huNanChangShaHandler.getCard(command, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理出牌提示协议
|
||||
*/
|
||||
private void handleDiscardPrompt(TaurusClient client) {
|
||||
huNanChangShaHandler.outCard(client, new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理操作提示协议(吃碰杠胡)
|
||||
*/
|
||||
private void handleActionPrompt(com.taurus.core.entity.ITObject param, TaurusClient client) {
|
||||
huNanChangShaHandler.actionCard(param, client);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理结算协议
|
||||
*/
|
||||
private void handleSettlement(com.taurus.core.entity.ITObject param, TaurusClient client) {
|
||||
|
||||
huNanChangShaHandler.getChangShaCardInhand().clear();
|
||||
huNanChangShaHandler.getChuGuoCardInhand().clear();
|
||||
huNanChangShaHandler.getPongGroup().clear();
|
||||
huNanChangShaHandler.getChowGroup().clear();
|
||||
|
||||
// 发送准备协议
|
||||
sendReadyCommand(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理服务器通知协议
|
||||
*/
|
||||
private void handleServerNotification(com.taurus.core.entity.ITObject param) {
|
||||
huNanChangShaHandler.shanchuchuguopai(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理换牌提示协议
|
||||
*/
|
||||
private void handleChangeCardTip(String command, taurus.client.Message message) {
|
||||
HuNanChangShaHandler.changePlayer(command, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理听牌协议
|
||||
*/
|
||||
private void handleTingPai(String command, TaurusClient client) {
|
||||
com.taurus.core.entity.ITObject params = new com.taurus.core.entity.TObject();
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 1);
|
||||
params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token);
|
||||
|
||||
client.send("612", params, response -> {
|
||||
System.out.println("听牌操作: " + response.returnCode);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理解散房间协议
|
||||
*/
|
||||
private void handleRoomDisband() {
|
||||
log.info("房间解散,准备下一局");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理房间检查和踢人逻辑
|
||||
*/
|
||||
private void handleRoomCheckAndKick(ITObject param, TaurusClient client) {
|
||||
System.out.println("Handling 2009 - Other operations");
|
||||
try {
|
||||
Jedis jedis0 = Redis.use().getJedis();
|
||||
Jedis jedis11 = Redis.use("group1_db11").getJedis();
|
||||
|
||||
Jedis jedis22 = Redis.use().getJedis();
|
||||
// 使用JiQiRens的sleepTime方法
|
||||
try {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
Thread.sleep(1000);
|
||||
if (i == 6) {
|
||||
sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
ITObject readyParams = new TObject();
|
||||
ITObject params = TObject.newInstance();
|
||||
String[] playerIds2 = null;
|
||||
Set<String> roomIds = jedis0.keys("room:*");
|
||||
String roomKey = "";
|
||||
|
||||
//获取所有机器人ID
|
||||
Set<String> roomIds = jedis22.keys("room:*");
|
||||
String roomKey = "";
|
||||
//拿到所有的机器人
|
||||
List<Integer> robotIdsList = new ArrayList<>();
|
||||
String sql2 = "SELECT id FROM `account` WHERE jiqiren=9998";
|
||||
ITArray robotId2 = null;
|
||||
try {
|
||||
robotId2 = DataBase.use().executeQueryByTArray(sql2);
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (robotId2 != null) {
|
||||
for (int j = 0; j < robotId2.size(); j++) {
|
||||
for (int j = 0; j < Objects.requireNonNull(robotId2).size(); j++) {
|
||||
robotIdsList.add(robotId2.getTObject(j).getInt("id"));
|
||||
}
|
||||
}
|
||||
|
||||
for (String roomId : roomIds) {
|
||||
String roomId1 = roomId.substring(roomId.indexOf(":") + 1);
|
||||
roomKey = "room:" + roomId1;
|
||||
log.info("roomKey +++++++++++++++++" + roomKey);
|
||||
|
||||
if (jedis0.hget(roomKey, "players") != null) {
|
||||
String players = jedis0.hget(roomKey, "players");
|
||||
if (jedis22.hget(roomKey, "players") != null) {
|
||||
String players = jedis22.hget(roomKey, "players");
|
||||
if (!players.equals("[]")) {
|
||||
players = players.substring(1, players.length() - 1);
|
||||
playerIds2 = players.split(",");
|
||||
log.info("playerIds2 ++++++===========" + Arrays.toString(playerIds2));
|
||||
|
||||
if (playerIds2.length == 1) {
|
||||
for (String s : playerIds2) {
|
||||
if (robotIdsList.contains(Integer.parseInt(s))) { //房间里的人是机器人
|
||||
String gpid = jedis0.hget(roomKey, "gpid");
|
||||
String gpId = jedis0.hget(roomKey, "group");
|
||||
String gpid = jedis22.hget(roomKey, "gpid");
|
||||
String gpId = jedis22.hget(roomKey, "group");
|
||||
String key = "g{" + gpId + "}:play:" + gpid;
|
||||
if (!players.equals("[]")) {
|
||||
System.out.println("roomKey ++++" + roomKey);
|
||||
System.out.println("gpid ++++++" + gpid);
|
||||
System.out.println("groupid ++++++" + gpId);
|
||||
System.out.println("key +++++++" + key);
|
||||
System.out.println("===================清空机器人准备时间超过六秒的房间 8888888888888888");
|
||||
if (!players.equals("[]") && pid == Integer.parseInt(gpid)) {
|
||||
|
||||
if (HuNanChangShaHandler.changShaCard != 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Jedis jedis20 = Redis.use("group1_db11").getJedis();
|
||||
|
||||
jedis20.hincrBy(key, "leftover_robot", 1);
|
||||
|
|
@ -368,109 +388,212 @@ public class RobotConnectionManager {
|
|||
|
||||
try {
|
||||
DataBase.use().executeUpdate(sql);
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
readyParams.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token);
|
||||
client.send("1005", params, response -> {
|
||||
|
||||
client.send("601", readyParams, response -> {
|
||||
System.out.println("机器人准备操作: " + response.returnCode);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jedis22.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
case "817": //结算
|
||||
huNanChangSha.getChangShaCardInhand().clear();
|
||||
huNanChangSha.getChuGuoCardInhand().clear();
|
||||
huNanChangSha.getpongGroup().clear();
|
||||
huNanChangSha.getchowGroup().clear();
|
||||
TinHuChi.lastTingCount = 0;
|
||||
TinHuChi.isMoreThanLast = false;
|
||||
ChangShaSuanFaTest.isTin=false;
|
||||
ChangShaSuanFaTest.isChi=false;
|
||||
ChangShaSuanFaTest.isPeng=false;
|
||||
ChangShaSuanFaTest.tinCards.clear();
|
||||
Integer type = param.getInt("type");
|
||||
if (type == 1 || type == 2) { //为1为大结算 为2为解散
|
||||
Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||
try {
|
||||
String key = "g{" + groupId + "}:play:" + pid;
|
||||
jedis11s.hincrBy(key, "leftover_robot", 1);
|
||||
//
|
||||
if (count != null && count.containsKey(pid)) {
|
||||
Integer currentValue = count.get(pid);
|
||||
if (currentValue > 0) {
|
||||
count.put(pid, currentValue - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String sql = String.format("UPDATE `account` SET start = %d WHERE id = %d", 0, playerId);
|
||||
try {
|
||||
DataBase.use().executeUpdate(sql);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
jedis0.close();
|
||||
jedis11.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
jedis11s.close();
|
||||
}
|
||||
|
||||
//发送准备协议
|
||||
sendReadyCommand(client);
|
||||
if (count != null && count.containsKey(pid)) {
|
||||
Integer value = count.get(pid);
|
||||
|
||||
// 如果找到了对应的 pid
|
||||
Jedis jedis12 = Redis.use("group1_db11").getJedis();
|
||||
|
||||
String shangxianRobot = jedis12.hget(playKey, "shangxian_robot");
|
||||
String leftoverRobot = jedis12.hget(playKey, "leftover_robot");
|
||||
|
||||
if (shangxianRobot != null && leftoverRobot != null) {
|
||||
if (value == 0) {
|
||||
jedis12.hset(playKey, "leftover_robot", shangxianRobot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理补杠协议
|
||||
*/
|
||||
private void handleBuGang(ITObject param, TaurusClient client) {
|
||||
int card = param.getInt("card");
|
||||
ITObject params = new TObject();
|
||||
params.putInt("type", 21); // 胡牌类型
|
||||
params.putInt("card", card);
|
||||
params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token);
|
||||
|
||||
client.send("612", params, response -> {
|
||||
log.info("补杠胡操作: " + response.returnCode);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理抢杠胡协议
|
||||
*/
|
||||
private void handleQiangGangHu(ITObject param, TaurusClient client) {
|
||||
|
||||
int card = param.getInt("card");
|
||||
ITObject params = new TObject();
|
||||
params.putInt("type", 21); //胡牌类型
|
||||
params.putInt("card", card);
|
||||
params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token);
|
||||
|
||||
client.send("612", params, response -> {
|
||||
log.info("抢杠胡操作: " + response.returnCode);
|
||||
});
|
||||
jedis12.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理吃牌协议
|
||||
*/
|
||||
private void handleChiCard(ITObject param, TaurusClient client) {
|
||||
|
||||
int card = param.getInt("card");
|
||||
ITObject params = new TObject();
|
||||
params.putInt("type", 21); //吃牌类型
|
||||
params.putInt("card", card);
|
||||
params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token);
|
||||
|
||||
client.send("612", params, response -> {
|
||||
log.info("吃牌操作响应: " + response.returnCode);
|
||||
});
|
||||
}
|
||||
// playerState.pongGroups.clear();;
|
||||
// playerState.handCards.clear();
|
||||
// playerState.chiGroups.clear();
|
||||
// playerState.gangGroups.clear();;
|
||||
|
||||
/**
|
||||
* 处理杠后补牌协议
|
||||
*/
|
||||
private void handleGangBuPai(ITObject param, TaurusClient client) {
|
||||
//处理杠后补牌逻辑
|
||||
int card = param.getInt("card");
|
||||
huNanChangShaHandler.getChangShaCardInhand().add(card);
|
||||
log.info("杠后补牌: " + card + ", 当前手牌: " + huNanChangShaHandler.getChangShaCardInhand());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未知协议命令
|
||||
*/
|
||||
private void handleUnknownCommand(String command, ITObject param, TaurusClient client) {
|
||||
log.info("收到未知协议命令: " + command + ", 参数: " + param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送准备命令
|
||||
*/
|
||||
private void sendReadyCommand(TaurusClient client) {
|
||||
com.taurus.core.entity.ITObject params = new com.taurus.core.entity.TObject();
|
||||
params.putString("session", HuNanChangShaHandler.session + "," + HuNanChangShaHandler.token);
|
||||
|
||||
client.send("601", params, response -> {
|
||||
// 发送准备协议
|
||||
ITObject readyParams = new TObject();
|
||||
readyParams.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token);
|
||||
client.send("1005", readyParams, response -> {
|
||||
System.out.println("准备操作响应: " + response.returnCode);
|
||||
});
|
||||
break;
|
||||
case "815": //服务器通知客户端有玩家执行了操作
|
||||
System.out.println("Handling 815 - Server notifies client of player action");
|
||||
//[TCP->815] data:{"playerid":101555,"card":104,"opcard":[105,103],"from_seat":2,"type":1,"opengang":false}
|
||||
huNanChangSha.shanchuchuguopai(param);
|
||||
break;
|
||||
case "820": //换牌提示
|
||||
System.out.println("Handling 820 - Change card tips");
|
||||
HuNanChangSha.changePlayer(command, message);
|
||||
break;
|
||||
case "825":
|
||||
System.out.println("Handling 825");
|
||||
ITObject params25 = TObject.newInstance();
|
||||
params25.putInt("qi", 0);
|
||||
params25.putInt("id", 1);
|
||||
params25.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token);
|
||||
client.send("612", params25, response -> {
|
||||
|
||||
});
|
||||
break;
|
||||
case "822":
|
||||
System.out.println("Handling 822");
|
||||
ITObject params22 = TObject.newInstance();
|
||||
//params.putInt("qi", 0);
|
||||
params22.putInt("id", 1);
|
||||
params22.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token);
|
||||
//[TCP->822] data:{"tip_list":[{"type":8,"id":1,"opcard":[],"weight":8,"card":0}],"types":[{"type":21,"value":1}]}
|
||||
//板胡Event [TCP->823] data:{"type":8,"seat":1,"data":[{"opcard":[204,204,204,108,108,108],"type":21,"value":1}]}
|
||||
client.send("612", params22, response -> {
|
||||
|
||||
});
|
||||
break;
|
||||
case "2008": //解散房间时候恢复机器人账号可以使用
|
||||
System.out.println("Handling 2008 - Restore robot accounts when room is dismissed");
|
||||
// Jedis jedis11s = Redis.use("group1_db11").getJedis();
|
||||
// String key = "g{" + groupId + "}:play:" + pid;
|
||||
//
|
||||
// jedis11s.hincrBy(key, "leftover_robot", 1);
|
||||
// jedis11s.close();
|
||||
// try {
|
||||
|
||||
Set<String> roomIds08 = jedis0.keys("room:*");
|
||||
String[] playerIds08 = null;
|
||||
for (String roomId : roomIds08) {
|
||||
String rid = roomId.substring(roomId.indexOf(":") + 1);
|
||||
String roomKey08 = "room:" + rid;
|
||||
|
||||
if (jedis0.hget(roomKey08, "players") != null) {
|
||||
String players = jedis0.hget(roomKey08, "players");
|
||||
|
||||
if (!players.equals("[]")) {
|
||||
players = players.substring(1, players.length() - 1);
|
||||
playerIds08 = players.split(",");
|
||||
|
||||
for (String pyids : playerIds08) {
|
||||
|
||||
if (Integer.parseInt(pyids) == playerId) {
|
||||
jedis0.del(roomId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "838": //补杠事件
|
||||
System.out.println("Handling 838 - Bu Gang event");
|
||||
int card838 = param.getInt("card");
|
||||
ITObject params838 = new TObject();
|
||||
params838.putInt("card", card838);
|
||||
params838.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token);
|
||||
client.send("839", params838, response -> {
|
||||
|
||||
});
|
||||
break;
|
||||
case "816": //抢杠胡
|
||||
System.out.println("Handling 816 - Qiang Gang Hu");
|
||||
int card816 = param.getInt("card");
|
||||
ITObject params816 = new TObject();
|
||||
params816.putInt("type", 21); //胡牌类型
|
||||
params816.putInt("card", card816);
|
||||
params816.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token);
|
||||
client.send("612", params816, response -> {
|
||||
log.info("抢杠胡操作: " + response.returnCode);
|
||||
});
|
||||
break;
|
||||
case "821": //吃牌
|
||||
System.out.println("Handling 821 - Chi Pai");
|
||||
int card821 = param.getInt("card");
|
||||
ITObject params821 = new TObject();
|
||||
params821.putInt("type", 21); //吃牌类型
|
||||
params821.putInt("card", card821);
|
||||
params821.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token);
|
||||
client.send("612", params821, response -> {
|
||||
log.info("吃牌操作响应: " + response.returnCode);
|
||||
});
|
||||
break;
|
||||
case "823": //杠后补牌
|
||||
System.out.println("Handling 823 - Gang after draw card");
|
||||
//处理杠后补牌逻辑
|
||||
int card823 = param.getInt("card");
|
||||
huNanChangSha.getChangShaCardInhand().add(card823);
|
||||
log.info("杠后补牌: " + card823 + ", 当前手牌: " + huNanChangSha.getChangShaCardInhand());
|
||||
break;
|
||||
case "835": //听牌相关
|
||||
System.out.println("Handling 835 - Ting Pai related");
|
||||
ITObject params835 = new TObject();
|
||||
params835.putInt("qi", 0);
|
||||
params835.putInt("id", 1);
|
||||
params835.putString("session", HuNanChangSha.session + "," + HuNanChangSha.token);
|
||||
client.send("612", params835, response -> {
|
||||
|
||||
});
|
||||
break;
|
||||
default:
|
||||
System.out.println("Received unknown protocol command: " + command + ", params: " + param);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,582 +0,0 @@
|
|||
package robot.mj.handler;
|
||||
|
||||
import com.robot.Util;
|
||||
import com.taurus.core.entity.ITArray;
|
||||
import com.taurus.core.entity.ITObject;
|
||||
import com.taurus.core.entity.TObject;
|
||||
import com.taurus.core.plugin.database.DataBase;
|
||||
import com.taurus.core.util.Logger;
|
||||
import taurus.client.Message;
|
||||
import taurus.client.TaurusClient;
|
||||
import taurus.util.CardUtil;
|
||||
import taurus.util.ChangShaSuanFaTest;
|
||||
import taurus.util.ChangshaWinSplitCard;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
public class HuNanChangShaHandler {
|
||||
|
||||
public static int changShaCard = 0;
|
||||
public static boolean isTinChi = false;
|
||||
public static boolean isTinPeng = false;
|
||||
|
||||
private static final Logger log = Logger.getLogger(HuNanChangShaHandler.class);
|
||||
|
||||
|
||||
//湖南长沙麻将手牌
|
||||
private final List<Integer> changShaCardInhand = new ArrayList<>();
|
||||
|
||||
private final List<Integer> changShaCardInhandgang = new ArrayList<>();
|
||||
|
||||
//长沙麻将出过的牌
|
||||
private final List<Integer> changShachuguopai = new ArrayList<>();
|
||||
|
||||
private final Map<Integer, Integer> chuGuoPainum = new HashMap<>();
|
||||
|
||||
//杠的牌
|
||||
private final List<Integer> gangdepai = new ArrayList<>();
|
||||
|
||||
//碰牌
|
||||
private final List<Integer> pongGroup = new ArrayList<>();
|
||||
|
||||
//吃牌
|
||||
private final List<Integer> chowGroup = new ArrayList<>();
|
||||
|
||||
//玩家座位号
|
||||
public static int seat = 0;
|
||||
|
||||
public static int playerId = 0;
|
||||
|
||||
public static int cardToOut1 = 0;
|
||||
|
||||
//会话标识
|
||||
public static String session = "";
|
||||
//访问令牌
|
||||
public static String token = "";
|
||||
|
||||
private static final ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest();
|
||||
|
||||
|
||||
//公共的getter和setter方法
|
||||
public List<Integer> getChangShaCardInhand() {
|
||||
return changShaCardInhand;
|
||||
}
|
||||
|
||||
public List<Integer> getGangdepai() {
|
||||
return gangdepai;
|
||||
}
|
||||
|
||||
public List<Integer> getChangShaCardInhandgang() {
|
||||
return changShaCardInhandgang;
|
||||
}
|
||||
|
||||
public List<Integer> getPongGroup() {
|
||||
return pongGroup;
|
||||
}
|
||||
|
||||
public List<Integer> getChowGroup() {
|
||||
return chowGroup;
|
||||
}
|
||||
|
||||
public List<Integer> getChuGuoCardInhand() {
|
||||
return changShachuguopai;
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getChuGuoPainum() {
|
||||
return chuGuoPainum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置会话和令牌
|
||||
*/
|
||||
public void setSessionAndToken(String session, String token) {
|
||||
HuNanChangShaHandler.session = session;
|
||||
HuNanChangShaHandler.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 出牌广播协议 812
|
||||
*
|
||||
* @param command 协议号
|
||||
* @param message 消息对象
|
||||
*/
|
||||
public static void drawCard(String command, Message message) {
|
||||
if (command.equalsIgnoreCase("812")) {
|
||||
ITObject param = message.param;
|
||||
if (param == null) {
|
||||
return;
|
||||
}
|
||||
changShaCard = param.getInt("card");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 摸牌协议 819
|
||||
*
|
||||
* @param command 协议号
|
||||
* @param message 消息对象
|
||||
*/
|
||||
public void getCard(String command, Message message) {
|
||||
if (command.equalsIgnoreCase("819")) {
|
||||
ITObject param = message.param;
|
||||
if (param == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (param.getInt("player") != null) {
|
||||
int drawnCard = param.getInt("card");
|
||||
changShaSuanFaTest.drawnCards = drawnCard;
|
||||
changShaCardInhand.add(drawnCard);
|
||||
changShaSuanFaTest.analyzeHand(changShaCardInhand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化手牌协议 811
|
||||
*
|
||||
* @param command 协议号
|
||||
* @param message 消息对象
|
||||
*/
|
||||
public void cardInHead(String command, Message message, TaurusClient client) {
|
||||
if (command.equalsIgnoreCase("811")) {
|
||||
ITObject param = message.param;
|
||||
if (param == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ITArray cardList = param.getTArray("card_list");
|
||||
changShaCardInhand.clear();
|
||||
for (int i = 0; i < cardList.size(); i++) {
|
||||
changShaCardInhand.add(cardList.getInt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 吃,碰,杠,补,胡
|
||||
*/
|
||||
public void actionCard(ITObject param, TaurusClient client) {
|
||||
ITArray tipList = param.getTArray("tip_list");
|
||||
log.info("tipList" +tipList);
|
||||
ITObject params = TObject.newInstance();
|
||||
int card = 0;
|
||||
|
||||
//循环
|
||||
List<Integer> yupanhandcard = new ArrayList<>(changShaCardInhand);
|
||||
//进行操作之前能否下听
|
||||
List<Integer> shifoutingpai = ChangShaSuanFaTest.handscardshifoutingpai(changShaCardInhand,chowGroup,pongGroup,gangdepai);
|
||||
//记录操作之前的下听状态
|
||||
boolean beforelisten = false;
|
||||
if (!shifoutingpai.isEmpty()) {
|
||||
beforelisten = true;
|
||||
}
|
||||
|
||||
//如果杠了之后还能继续听牌则杠
|
||||
//如果下听了,可以杠
|
||||
for (int i = 0; i < tipList.size(); i++) {
|
||||
TObject firstTip = (TObject) tipList.get(i).getObject();
|
||||
int type = firstTip.getInt("type");
|
||||
int id = firstTip.getInt("id");
|
||||
int weight = firstTip.getInt("weight");
|
||||
card = firstTip.getInt("card");
|
||||
|
||||
|
||||
if (type == 6){
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", id);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ((type == 5 || type == 3 || type==4)&&weight==4) {
|
||||
//判断开杠后是否能下听
|
||||
List<Integer> gangusecars = new ArrayList<>(changShaCardInhand);
|
||||
|
||||
if (type == 3) {
|
||||
Util.removeCard(gangusecars,card,3);
|
||||
List<Integer> shifoutingpai3 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai);
|
||||
log.info(shifoutingpai3);
|
||||
|
||||
if (!shifoutingpai3.isEmpty()) {
|
||||
//开杠
|
||||
//判断是否杠了之后没有牌可以听
|
||||
List<Integer> allList = new ArrayList<>();
|
||||
allList.addAll(changShaCardInhand);
|
||||
allList.addAll(changShachuguopai);
|
||||
int jutingnum = ChangShaSuanFaTest.getTingPainum(shifoutingpai3,allList);
|
||||
if (jutingnum==0){
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Util.removeCard(changShaCardInhand,card,3);
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", id);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}else{
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (type == 4) {
|
||||
Util.removeCard(gangusecars,card,4);
|
||||
List<Integer> shifoutingpai4 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai);
|
||||
if (!shifoutingpai4.isEmpty()) {
|
||||
//开杠
|
||||
//判断是否杠了之后没有牌可以听
|
||||
List<Integer> allList = new ArrayList<>();
|
||||
allList.addAll(changShaCardInhand);
|
||||
allList.addAll(changShachuguopai);
|
||||
int jutingnum = ChangShaSuanFaTest.getTingPainum(shifoutingpai4,allList);
|
||||
if (jutingnum==0){
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Util.removeCard(changShaCardInhand,card,4);
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", id);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}else{
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (type == 5) {
|
||||
|
||||
Util.removeCard(gangusecars,card,1);
|
||||
List<Integer> shifoutingpai5 = ChangShaSuanFaTest.handscardshifoutingpai(gangusecars,chowGroup,pongGroup,gangdepai);
|
||||
if (!shifoutingpai5.isEmpty()) {
|
||||
//开杠
|
||||
//判断是否杠了之后没有牌可以听
|
||||
List<Integer> allList = new ArrayList<>();
|
||||
allList.addAll(changShaCardInhand);
|
||||
allList.addAll(changShachuguopai);
|
||||
int jutingnum = ChangShaSuanFaTest.getTingPainum(shifoutingpai5,allList);
|
||||
if (jutingnum==0){
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Util.removeCard(changShaCardInhand,card,1);
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", id);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}else{
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//判断是否是大胡
|
||||
int beforeIsDahu = 0;
|
||||
beforeIsDahu = ChangShaSuanFaTest.checkDahu(yupanhandcard,chowGroup,pongGroup,gangdepai);
|
||||
//5、门清
|
||||
if (yupanhandcard.size()==13&&beforelisten){
|
||||
beforeIsDahu = 5;//门清
|
||||
}
|
||||
|
||||
//操作之前出现的牌
|
||||
List<Integer> allSeeCard = new ArrayList<>();
|
||||
|
||||
Map<Integer,ITObject> pingfenResult = new HashMap<>();
|
||||
Map<Integer,ITObject> idObject = new HashMap<>();
|
||||
for (int i = 0; i < tipList.size(); i++) {
|
||||
TObject firstTip = (TObject) tipList.get(i).getObject();
|
||||
int type = firstTip.getInt("type");
|
||||
int id = firstTip.getInt("id");
|
||||
int weight = firstTip.getInt("weight");
|
||||
card = firstTip.getInt("card");
|
||||
ITArray opcard = firstTip.getTArray("opcard");
|
||||
if (type == 6){
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", id);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
//对应的数据
|
||||
ITObject tmp = new TObject();
|
||||
//处理听的结果
|
||||
switch (type) {
|
||||
case 1:
|
||||
//吃
|
||||
tmp = ChangShaSuanFaTest.pingguChi( beforelisten,card,opcard,yupanhandcard,beforeIsDahu,allSeeCard,chowGroup,pongGroup,gangdepai,changShachuguopai);
|
||||
break;
|
||||
case 2:
|
||||
//碰
|
||||
tmp = ChangShaSuanFaTest.pingguPeng(beforelisten,card,opcard,yupanhandcard,beforeIsDahu,allSeeCard,chowGroup,pongGroup,gangdepai,changShachuguopai);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//记录下来事件
|
||||
if(tmp.size()>0){
|
||||
pingfenResult.put(id,tmp);
|
||||
}
|
||||
ITObject sj = new TObject();
|
||||
sj.putInt("weight", weight);
|
||||
sj.putTArray("opcard", opcard);
|
||||
idObject.put(id,sj);
|
||||
}
|
||||
//计算分数
|
||||
log.info(pingfenResult);
|
||||
if(!pingfenResult.isEmpty()){
|
||||
int changeid= ChangShaSuanFaTest.suanfen(pingfenResult);
|
||||
log.info("changeid:"+changeid);
|
||||
//选择最优的分数
|
||||
if (changeid==0){
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}else {
|
||||
//获取
|
||||
for(Map.Entry<Integer,ITObject> entry : idObject.entrySet()){
|
||||
if(entry.getKey()==changeid){
|
||||
ITObject tmp = entry.getValue();
|
||||
if (tmp.getInt("weight")==2){
|
||||
//碰
|
||||
ITArray outcards = tmp.getTArray("opcard");
|
||||
for (int i = 0; i < outcards.size(); i++) {
|
||||
Util.removeCard(changShaCardInhand,outcards.getInt(0),2);
|
||||
}
|
||||
}else if (tmp.getInt("weight")==1){
|
||||
//吃
|
||||
ITArray outcards = tmp.getTArray("opcard");
|
||||
for (int i = 0; i < outcards.size(); i++) {
|
||||
Util.removeCard(changShaCardInhand,outcards.getInt(i),1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", changeid);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//计算牌数
|
||||
|
||||
//如果 不吃,不碰,而且差三手牌情况,则补
|
||||
for (int i = 0; i < tipList.size(); i++) {
|
||||
TObject firstTip = (TObject) tipList.get(i).getObject();
|
||||
int type = firstTip.getInt("type");
|
||||
int id = firstTip.getInt("id");
|
||||
int weight = firstTip.getInt("weight");
|
||||
card = firstTip.getInt("card");
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
List<Integer> tmpChangSch = new ArrayList<>(yupanhandcard);
|
||||
ChangshaWinSplitCard.checkNormalHu(tmpChangSch, map);
|
||||
System.out.println("map:" + map);
|
||||
|
||||
if (!map.isEmpty() && weight==3) {
|
||||
|
||||
if (Integer.parseInt(map.get("remainingMelds").toString()) > 2) {
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", id);
|
||||
|
||||
if (type == 3) {
|
||||
Util.removeCard(changShaCardInhand,card,3);
|
||||
}
|
||||
if (type == 4) {
|
||||
Util.removeCard(changShaCardInhand,card,4);
|
||||
}
|
||||
if (type == 5) {
|
||||
Util.removeCard(changShaCardInhand,card,1);
|
||||
}
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
params.putString("session", session + "," + token);
|
||||
params.putInt("qi", 0);
|
||||
params.putInt("id", 0);
|
||||
client.send("612", params, response -> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 出牌方法
|
||||
*/
|
||||
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) {
|
||||
//整合所有其他玩家的出牌信息
|
||||
List<Integer> resultList = new ArrayList<>();
|
||||
for (List<Integer> cards : playerOutcardsMap.values()) {
|
||||
resultList.addAll(cards);
|
||||
}
|
||||
|
||||
for (List<Integer> chis : playerchisMap.values()) {
|
||||
resultList.addAll(chis);
|
||||
}
|
||||
|
||||
for (List<Integer> pengs : playerpengsMap.values()) {
|
||||
resultList.addAll(pengs);
|
||||
}
|
||||
|
||||
for (List<Integer> minggangs : playermingsMap.values()) {
|
||||
resultList.addAll(minggangs);
|
||||
}
|
||||
|
||||
for (List<Integer> zigang : playerzisMap.values()) {
|
||||
resultList.addAll(zigang);
|
||||
}
|
||||
|
||||
ITObject params = TObject.newInstance();
|
||||
int cardToOut;
|
||||
|
||||
if (!changShaCardInhand.isEmpty()) {
|
||||
//使用长沙麻将算法计算最优出牌
|
||||
ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest();
|
||||
String cardStr = changShaSuanFaTest.outCardSuanFa(changShaCardInhand, pongGroup, chowGroup, gangdepai, resultList);
|
||||
try {
|
||||
cardToOut = Integer.parseInt(cardStr);
|
||||
} catch (NumberFormatException e) {
|
||||
//如果算法出错,使用第一张牌
|
||||
cardToOut = changShaCardInhand.get(0);
|
||||
}
|
||||
} else {
|
||||
return; //没有牌可出
|
||||
}
|
||||
|
||||
params.putInt("card", cardToOut);
|
||||
|
||||
int outCountBefore = changShachuguopai.size(); //当前历史出牌数量
|
||||
|
||||
//第n次出牌时,发送前n-1张出牌
|
||||
if (outCountBefore >= 1) {
|
||||
//发送前n-1张(所有历史出牌)
|
||||
List<Integer> cardsToSend = changShachuguopai.subList(0, outCountBefore);
|
||||
params.putTArray("outcard_list", CardUtil.maJiangToTArray(cardsToSend));
|
||||
}
|
||||
params.putTArray("card_list", CardUtil.maJiangToTArray(changShaCardInhand));
|
||||
|
||||
|
||||
//将当前出的牌添加到历史出牌列表
|
||||
changShachuguopai.add(cardToOut);
|
||||
//从手牌中移除
|
||||
changShaCardInhand.remove(Integer.valueOf(cardToOut));
|
||||
|
||||
params.putString("session", session + "," + token);
|
||||
client.send("611", params, response -> {
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除出过的牌组
|
||||
*
|
||||
*/
|
||||
public void shanchuchuguopai(ITObject param) {
|
||||
if (param == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer card = param.getInt("card"); //操作牌值
|
||||
Integer type = param.getInt("type"); //操作类型
|
||||
Integer from_seat = param.getInt("from_seat"); //牌来源座位
|
||||
|
||||
|
||||
Integer playerid = param.getInt("playerid");
|
||||
|
||||
|
||||
String sql2 = "SELECT id FROM `account` WHERE jiqiren=9998";
|
||||
try {
|
||||
ITArray robotId2 = DataBase.use().executeQueryByTArray(sql2);
|
||||
List<Integer> robotIdsList = new ArrayList<>();
|
||||
|
||||
for (int j = 0; j < robotId2.size(); j++) {
|
||||
robotIdsList.add(robotId2.getTObject(j).getInt("id"));
|
||||
}
|
||||
|
||||
if (!robotIdsList.contains(playerid)) {
|
||||
if (type == 2 || type == 3 || type == 5 || type == 1) { //碰,杠
|
||||
getChuGuoCardInhand().remove(card);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 位置转换协议 820
|
||||
*
|
||||
* @param command 协议号
|
||||
* @param message 消息对象
|
||||
*/
|
||||
public static void changePlayer(String command, Message message) {
|
||||
if (command.equalsIgnoreCase("820")) {
|
||||
ITObject param = message.param;
|
||||
if (param == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
seat = param.getInt("seat");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package taurus.util;
|
||||
|
||||
import com.robot.Util;
|
||||
import robot.mj.handler.HuNanChangShaHandler;
|
||||
import robot.mj.handler.HuNanChangSha;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -627,7 +627,7 @@ public class TinHuChi {
|
|||
// 5. 检查打牌后是否能听牌
|
||||
boolean canTing = checkCanTing(afterDiscard, needs258, targetSizeAfterDiscard);
|
||||
if (canTing) {
|
||||
HuNanChangShaHandler.isTinChi = true;
|
||||
HuNanChangSha.isTinChi = true;
|
||||
ChangShaSuanFaTest.isChi=true;
|
||||
System.out.println(" ✓ 听牌!");
|
||||
foundTing = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,404 @@
|
|||
package taurus.util;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class TinHuGang {
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否能杠牌(检查杠牌后是否立即听牌)
|
||||
*/
|
||||
public static boolean canGang(List<Integer> handCards, int card, boolean isMingGang) {
|
||||
int type = card / 100;
|
||||
int value = card % 100;
|
||||
|
||||
System.out.println("\n检查" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":");
|
||||
|
||||
// 检查是否符合杠牌条件
|
||||
int count = Collections.frequency(handCards, card);
|
||||
|
||||
if (isMingGang) {
|
||||
// 明杠:手牌中需要有三张相同的牌
|
||||
if (count < 3) {
|
||||
System.out.println(" 手牌中没有三张相同的" + value + getTypeName(type) + ",不能明杠");
|
||||
return false;
|
||||
}
|
||||
System.out.println(" 发现三张" + value + getTypeName(type) + ",可以明杠");
|
||||
} else {
|
||||
// 暗杠:手牌中需要有四张相同的牌
|
||||
if (count < 4) {
|
||||
System.out.println(" 手牌中没有四张相同的" + value + getTypeName(type) + ",不能暗杠");
|
||||
return false;
|
||||
}
|
||||
System.out.println(" 发现四张" + value + getTypeName(type) + ",可以暗杠");
|
||||
}
|
||||
|
||||
// 检查杠牌后是否立即听牌(杠牌后手牌本身就是听牌状态)
|
||||
return canTingImmediatelyAfterGang(handCards, card, isMingGang);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查杠牌后是否能立即听牌
|
||||
*/
|
||||
private static boolean canTingImmediatelyAfterGang(List<Integer> handCards, int gangCard, boolean isMingGang) {
|
||||
int type = gangCard / 100;
|
||||
int value = gangCard % 100;
|
||||
|
||||
System.out.println("\n模拟" + (isMingGang ? "明" : "暗") + "杠" + value + getTypeName(type) + ":");
|
||||
|
||||
// 1. 模拟杠牌:移除手牌中的牌
|
||||
List<Integer> afterGang = new ArrayList<>(handCards);
|
||||
|
||||
if (isMingGang) {
|
||||
// 明杠:移除手牌中的三张牌
|
||||
for (int i = 0; i < 3; i++) {
|
||||
afterGang.remove(Integer.valueOf(gangCard));
|
||||
}
|
||||
// 杠牌后手牌数:13张 → 10张
|
||||
} else {
|
||||
// 暗杠:移除手牌中的四张牌
|
||||
for (int i = 0; i < 4; i++) {
|
||||
afterGang.remove(Integer.valueOf(gangCard));
|
||||
}
|
||||
// 暗杠后需要补牌,但这里先不处理
|
||||
}
|
||||
|
||||
Collections.sort(afterGang);
|
||||
|
||||
System.out.println(" 杠后手牌(" + afterGang.size() + "张): " + convertToReadable(afterGang));
|
||||
|
||||
// 2. 检查是否需要258将
|
||||
boolean needs258 = !checkSuitCount(afterGang);
|
||||
if (needs258) {
|
||||
System.out.println(" 花色牌数不足10张,需要258做将");
|
||||
}
|
||||
|
||||
// 3. 检查杠牌后手牌本身是否就是听牌状态
|
||||
// 注意:杠牌后手牌数是10张,这10张牌本身应该是听牌状态
|
||||
// 也就是说,随便摸一张牌(任何牌)都能胡牌
|
||||
|
||||
System.out.println("\n 检查杠后手牌是否听牌:");
|
||||
|
||||
if (afterGang.size() != 10) {
|
||||
System.out.println(" 手牌数不是10张,不符合听牌条件");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查这10张牌是否听牌
|
||||
boolean canTing = checkIfHandIsTingPai(afterGang, needs258);
|
||||
|
||||
if (canTing) {
|
||||
System.out.println(" ✓ 杠后手牌是听牌状态!");
|
||||
|
||||
// 显示听哪些牌
|
||||
Set<Integer> tingCards = getTingCards(afterGang, needs258);
|
||||
if (!tingCards.isEmpty()) {
|
||||
System.out.print(" 听" + tingCards.size() + "张牌: ");
|
||||
List<String> tingStrs = new ArrayList<>();
|
||||
for (int tingCard : tingCards) {
|
||||
tingStrs.add((tingCard%100) + getTypeName(tingCard/100));
|
||||
}
|
||||
System.out.println(String.join(", ", tingStrs));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
System.out.println(" ✗ 杠后手牌不是听牌状态");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手牌是否处于听牌状态
|
||||
*/
|
||||
private static boolean checkIfHandIsTingPai(List<Integer> hand, boolean needs258) {
|
||||
if (hand.size() != 10) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 听牌状态:再摸任何一张牌都能胡牌
|
||||
// 我们需要检查是否至少有一张牌能让这手牌胡牌
|
||||
Set<Integer> allCards = getAllCards();
|
||||
int tingCount = 0;
|
||||
|
||||
for (int testCard : allCards) {
|
||||
List<Integer> tempHand = new ArrayList<>(hand);
|
||||
tempHand.add(testCard);
|
||||
|
||||
if (canHu(tempHand, needs258)) {
|
||||
tingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(" 可胡" + tingCount + "张牌");
|
||||
return tingCount > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取听哪些牌
|
||||
*/
|
||||
private static Set<Integer> getTingCards(List<Integer> hand, boolean needs258) {
|
||||
Set<Integer> tingCards = new HashSet<>();
|
||||
|
||||
if (hand.size() != 10) {
|
||||
return tingCards;
|
||||
}
|
||||
|
||||
Set<Integer> allCards = getAllCards();
|
||||
|
||||
for (int testCard : allCards) {
|
||||
List<Integer> tempHand = new ArrayList<>(hand);
|
||||
tempHand.add(testCard);
|
||||
|
||||
if (canHu(tempHand, needs258)) {
|
||||
tingCards.add(testCard);
|
||||
}
|
||||
}
|
||||
|
||||
return tingCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手牌是否能胡牌
|
||||
*/
|
||||
private static boolean canHu(List<Integer> handCards, boolean needs258) {
|
||||
// 手牌排序
|
||||
List<Integer> sorted = new ArrayList<>(handCards);
|
||||
Collections.sort(sorted);
|
||||
|
||||
// 胡牌时手牌数必须是3n+2
|
||||
if (sorted.size() != 11 && sorted.size() != 14) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查七对子
|
||||
if (checkQiDuiZi(sorted)) {
|
||||
// 七对子不需要258将
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果需要258将,检查普通胡牌(必须有258将)
|
||||
if (needs258) {
|
||||
return checkNormalHuWith258(sorted);
|
||||
} else {
|
||||
// 不需要258将,检查普通胡牌
|
||||
return checkNormalHu(sorted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查七对子
|
||||
*/
|
||||
private static boolean checkQiDuiZi(List<Integer> handCards) {
|
||||
if (handCards.size() != 14) return false;
|
||||
|
||||
Map<Integer, Integer> countMap = new HashMap<>();
|
||||
for (int card : handCards) {
|
||||
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||
}
|
||||
|
||||
// 检查是否都是对子
|
||||
for (int count : countMap.values()) {
|
||||
if (count != 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查普通胡牌(必须有258将)
|
||||
*/
|
||||
private static boolean checkNormalHuWith258(List<Integer> handCards) {
|
||||
// 尝试每种258作为将牌
|
||||
for (int card : handCards) {
|
||||
int value = card % 100;
|
||||
// 检查是否是258
|
||||
if (value == 2 || value == 5 || value == 8) {
|
||||
// 检查是否有至少2张相同的牌做将
|
||||
int count = Collections.frequency(handCards, card);
|
||||
if (count >= 2) {
|
||||
// 移除将牌
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
remaining.remove(Integer.valueOf(card));
|
||||
}
|
||||
|
||||
// 检查剩余牌是否能组成顺子/刻子
|
||||
if (canGroup(remaining)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查普通胡牌(不需要258将)
|
||||
*/
|
||||
private static boolean checkNormalHu(List<Integer> handCards) {
|
||||
return checkNormalHuRecursive(new ArrayList<>(handCards), false);
|
||||
}
|
||||
|
||||
private static boolean checkNormalHuRecursive(List<Integer> handCards, boolean hasJiang) {
|
||||
if (handCards.isEmpty()) {
|
||||
return true; // 所有牌都分组成功
|
||||
}
|
||||
|
||||
Collections.sort(handCards);
|
||||
|
||||
// 统计第一张牌的数量
|
||||
int firstCard = handCards.get(0);
|
||||
int count = Collections.frequency(handCards, firstCard);
|
||||
|
||||
// 尝试作为刻子(三张相同)
|
||||
if (count >= 3) {
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
}
|
||||
if (checkNormalHuRecursive(remaining, hasJiang)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试作为顺子(三张连续)
|
||||
int type = firstCard / 100;
|
||||
int value = firstCard % 100;
|
||||
|
||||
if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子
|
||||
int second = type * 100 + (value + 1);
|
||||
int third = type * 100 + (value + 2);
|
||||
|
||||
if (handCards.contains(second) && handCards.contains(third)) {
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
remaining.remove(Integer.valueOf(second));
|
||||
remaining.remove(Integer.valueOf(third));
|
||||
if (checkNormalHuRecursive(remaining, hasJiang)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试作为将(对子)- 只能有一个将
|
||||
if (!hasJiang && count >= 2) {
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
if (checkNormalHuRecursive(remaining, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查牌是否能组成顺子或刻子
|
||||
*/
|
||||
private static boolean canGroup(List<Integer> cards) {
|
||||
if (cards.isEmpty()) {
|
||||
return true; // 所有牌都分组成功
|
||||
}
|
||||
|
||||
List<Integer> sorted = new ArrayList<>(cards);
|
||||
Collections.sort(sorted);
|
||||
|
||||
int firstCard = sorted.get(0);
|
||||
int count = Collections.frequency(sorted, firstCard);
|
||||
|
||||
// 尝试作为刻子(三张相同)
|
||||
if (count >= 3) {
|
||||
List<Integer> remaining = new ArrayList<>(sorted);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
}
|
||||
if (canGroup(remaining)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试作为顺子(三张连续)
|
||||
int type = firstCard / 100;
|
||||
int value = firstCard % 100;
|
||||
|
||||
if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子
|
||||
int second = type * 100 + (value + 1);
|
||||
int third = type * 100 + (value + 2);
|
||||
|
||||
if (sorted.contains(second) && sorted.contains(third)) {
|
||||
List<Integer> remaining = new ArrayList<>(sorted);
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
remaining.remove(Integer.valueOf(second));
|
||||
remaining.remove(Integer.valueOf(third));
|
||||
if (canGroup(remaining)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手牌花色牌数是否>=10张
|
||||
*/
|
||||
private static boolean checkSuitCount(List<Integer> hand) {
|
||||
Map<Integer, Integer> suitCount = new HashMap<>();
|
||||
for (int card : hand) {
|
||||
int type = card / 100;
|
||||
if (type < 4) {
|
||||
suitCount.put(type, suitCount.getOrDefault(type, 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int count : suitCount.values()) {
|
||||
if (count >= 10) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有麻将牌
|
||||
*/
|
||||
private static Set<Integer> getAllCards() {
|
||||
Set<Integer> allCards = new HashSet<>();
|
||||
for (int type = 1; type <= 3; type++) {
|
||||
for (int value = 1; value <= 9; value++) {
|
||||
allCards.add(type * 100 + value);
|
||||
}
|
||||
}
|
||||
return allCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为可读格式
|
||||
*/
|
||||
private static String convertToReadable(List<Integer> cards) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int card : cards) {
|
||||
int type = card / 100;
|
||||
int value = card % 100;
|
||||
sb.append(value).append(getTypeName(type)).append(" ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取牌型名称
|
||||
*/
|
||||
private static String getTypeName(int type) {
|
||||
switch(type) {
|
||||
case 1: return "万";
|
||||
case 2: return "筒";
|
||||
case 3: return "条";
|
||||
default: return "字";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,425 @@
|
|||
package taurus.util;
|
||||
|
||||
|
||||
import robot.mj.handler.HuNanChangSha;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class TinHuPeng {
|
||||
|
||||
/**
|
||||
* 判断是否能碰牌(包含碰牌后的听牌检查)
|
||||
*/
|
||||
public boolean canPeng(List<Integer> handCards, int card) {
|
||||
int type = card / 100;
|
||||
int value = card % 100;
|
||||
|
||||
if (type >= 4) return false; // 字牌不能碰(长沙麻将没有字牌)
|
||||
|
||||
System.out.println("\n要碰的牌: " + value + getTypeName(type));
|
||||
System.out.println("当前手牌数量: " + handCards.size() + "张");
|
||||
|
||||
// 检查基本碰牌条件:手牌至少有2张相同的牌
|
||||
if (!canPengBasic(handCards, card)) {
|
||||
System.out.println(" 基本碰牌条件不满足:手牌没有2张" + value + getTypeName(type));
|
||||
return false;
|
||||
}
|
||||
System.out.println(" ✓ 手牌有2张" + value + getTypeName(type) + ",可以碰");
|
||||
ChangShaSuanFaTest.isPeng = true;
|
||||
// 检查碰牌后是否能听牌
|
||||
return canTingAfterPengAndDiscard(handCards, card);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查基本碰牌条件
|
||||
*/
|
||||
private boolean canPengBasic(List<Integer> handCards, int card) {
|
||||
// 碰牌:手牌有2张相同的牌,别人打出第3张
|
||||
int count = 0;
|
||||
for (int c : handCards) {
|
||||
if (c == card) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count >= 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查碰牌并打出一张后是否能听牌
|
||||
*/
|
||||
private boolean canTingAfterPengAndDiscard(List<Integer> handCards, int pengCard) {
|
||||
// 1. 模拟碰牌:移除两张相同的牌,碰的牌不加入手牌
|
||||
List<Integer> afterPeng = new ArrayList<>(handCards);
|
||||
|
||||
// 移除2张相同的牌
|
||||
int removed = 0;
|
||||
for (int i = 0; i < handCards.size() && removed < 2; i++) {
|
||||
if (handCards.get(i) == pengCard) {
|
||||
afterPeng.remove(Integer.valueOf(pengCard));
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
Collections.sort(afterPeng);
|
||||
|
||||
System.out.println(" 碰后手牌(" + afterPeng.size() + "张): " + convertToReadable(afterPeng));
|
||||
|
||||
// 2. 检查是否需要258将(花色是否>=10张)
|
||||
boolean needs258 = !checkSuitCount(afterPeng);
|
||||
if (needs258) {
|
||||
System.out.println(" 花色牌数不足10张,需要258做将");
|
||||
}
|
||||
|
||||
// 3. 根据碰后手牌数确定目标手牌数
|
||||
// 碰牌前后手牌数变化:碰前N张 -> 碰后(N-2)张 -> 打后(N-3)张
|
||||
int targetSizeAfterDiscard = afterPeng.size() - 1;
|
||||
System.out.println(" 目标手牌数: " + targetSizeAfterDiscard);
|
||||
|
||||
// 4. 尝试打每一张不同的牌
|
||||
Set<Integer> uniqueCards = new HashSet<>(afterPeng);
|
||||
boolean foundTing = false;
|
||||
|
||||
for (int discardCard : uniqueCards) {
|
||||
List<Integer> afterDiscard = new ArrayList<>(afterPeng);
|
||||
afterDiscard.remove(Integer.valueOf(discardCard));
|
||||
Collections.sort(afterDiscard);
|
||||
|
||||
int discardType = discardCard / 100;
|
||||
int discardValue = discardCard % 100;
|
||||
|
||||
System.out.print("\n 打" + discardValue + getTypeName(discardType) +
|
||||
" → 剩余" + afterDiscard.size() + "张: " +
|
||||
convertToReadable(afterDiscard));
|
||||
|
||||
// 5. 检查打牌后是否能听牌
|
||||
boolean canTing = checkCanTing(afterDiscard, needs258, targetSizeAfterDiscard);
|
||||
|
||||
if (canTing) {
|
||||
HuNanChangSha.isTinPeng = true;
|
||||
System.out.println(" ✓ 听牌!");
|
||||
foundTing = true;
|
||||
|
||||
// 分析听牌详情
|
||||
analyzeTingDetails(afterDiscard, needs258);
|
||||
} else {
|
||||
System.out.println(" ✗ 不听");
|
||||
}
|
||||
}
|
||||
|
||||
return foundTing;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手牌是否能听牌
|
||||
*/
|
||||
private boolean checkCanTing(List<Integer> hand, boolean needs258, int targetSize) {
|
||||
if (hand.size() != targetSize) {
|
||||
System.out.print(" [手牌数" + hand.size() + "≠目标" + targetSize + "]");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取所有可能的牌
|
||||
Set<Integer> allCards = getAllCards();
|
||||
|
||||
for (int testCard : allCards) {
|
||||
List<Integer> tempHand = new ArrayList<>(hand);
|
||||
tempHand.add(testCard);
|
||||
Collections.sort(tempHand);
|
||||
|
||||
// 检查是否能胡牌
|
||||
if (canHu(tempHand, needs258)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手牌是否能胡牌(考虑258将要求)
|
||||
*/
|
||||
private boolean canHu(List<Integer> handCards, boolean needs258) {
|
||||
// 手牌排序
|
||||
List<Integer> sorted = new ArrayList<>(handCards);
|
||||
Collections.sort(sorted);
|
||||
|
||||
// 胡牌时手牌数必须是3n+2
|
||||
if (sorted.size() % 3 != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查七对子(特殊胡牌)
|
||||
if (checkQiDuiZi(sorted)) {
|
||||
// 七对子不需要258将
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果需要258将,检查普通胡牌(必须有258将)
|
||||
if (needs258) {
|
||||
return checkNormalHuWith258(sorted);
|
||||
} else {
|
||||
// 不需要258将,检查普通胡牌
|
||||
return checkNormalHu(sorted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析听牌详情
|
||||
*/
|
||||
private void analyzeTingDetails(List<Integer> hand, boolean needs258) {
|
||||
Set<Integer> tingCards = new HashSet<>();
|
||||
Set<Integer> allCards = getAllCards();
|
||||
|
||||
for (int testCard : allCards) {
|
||||
List<Integer> tempHand = new ArrayList<>(hand);
|
||||
tempHand.add(testCard);
|
||||
|
||||
if (canHu(tempHand, needs258)) {
|
||||
tingCards.add(testCard);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tingCards.isEmpty()) {
|
||||
System.out.print(" 听" + tingCards.size() + "张牌: ");
|
||||
List<String> tingCardStrs = new ArrayList<>();
|
||||
for (int card : tingCards) {
|
||||
int type = card / 100;
|
||||
int value = card % 100;
|
||||
tingCardStrs.add(value + getTypeName(type));
|
||||
}
|
||||
System.out.println(String.join(", ", tingCardStrs));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手牌花色牌数是否>=10张
|
||||
*/
|
||||
private boolean checkSuitCount(List<Integer> hand) {
|
||||
// 统计万、筒、条各自的数量
|
||||
Map<Integer, Integer> suitCount = new HashMap<>();
|
||||
for (int card : hand) {
|
||||
int type = card / 100;
|
||||
if (type < 4) { // 只统计万筒条
|
||||
suitCount.put(type, suitCount.getOrDefault(type, 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有某种花色>=10张
|
||||
for (int count : suitCount.values()) {
|
||||
if (count >= 10) {
|
||||
return true; // 有花色>=10张,不需要258将
|
||||
}
|
||||
}
|
||||
|
||||
return false; // 没有花色>=10张,需要258将
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查七对子
|
||||
*/
|
||||
private boolean checkQiDuiZi(List<Integer> handCards) {
|
||||
if (handCards.size() != 14) return false;
|
||||
|
||||
Map<Integer, Integer> countMap = new HashMap<>();
|
||||
for (int card : handCards) {
|
||||
countMap.put(card, countMap.getOrDefault(card, 0) + 1);
|
||||
}
|
||||
|
||||
// 检查是否都是对子
|
||||
for (int count : countMap.values()) {
|
||||
if (count != 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查普通胡牌(必须有258将)
|
||||
*/
|
||||
private boolean checkNormalHuWith258(List<Integer> handCards) {
|
||||
// 尝试每种258作为将牌
|
||||
for (int card : handCards) {
|
||||
int value = card % 100;
|
||||
// 检查是否是258
|
||||
if (value == 2 || value == 5 || value == 8) {
|
||||
// 检查是否有至少2张相同的牌做将
|
||||
int count = Collections.frequency(handCards, card);
|
||||
if (count >= 2) {
|
||||
// 移除将牌
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
remaining.remove(Integer.valueOf(card));
|
||||
}
|
||||
|
||||
// 检查剩余牌是否能组成顺子/刻子
|
||||
if (canGroup(remaining)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查普通胡牌(不需要258将)
|
||||
*/
|
||||
private boolean checkNormalHu(List<Integer> handCards) {
|
||||
return checkNormalHuRecursive(new ArrayList<>(handCards), false);
|
||||
}
|
||||
|
||||
private boolean checkNormalHuRecursive(List<Integer> handCards, boolean hasJiang) {
|
||||
if (handCards.isEmpty()) {
|
||||
return true; // 所有牌都分组成功
|
||||
}
|
||||
|
||||
Collections.sort(handCards);
|
||||
|
||||
// 统计第一张牌的数量
|
||||
int firstCard = handCards.get(0);
|
||||
int count = Collections.frequency(handCards, firstCard);
|
||||
|
||||
// 尝试作为刻子(三张相同)
|
||||
if (count >= 3) {
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
}
|
||||
if (checkNormalHuRecursive(remaining, hasJiang)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试作为顺子(三张连续)
|
||||
int type = firstCard / 100;
|
||||
int value = firstCard % 100;
|
||||
|
||||
if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子
|
||||
int second = type * 100 + (value + 1);
|
||||
int third = type * 100 + (value + 2);
|
||||
|
||||
if (handCards.contains(second) && handCards.contains(third)) {
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
remaining.remove(Integer.valueOf(second));
|
||||
remaining.remove(Integer.valueOf(third));
|
||||
if (checkNormalHuRecursive(remaining, hasJiang)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试作为将(对子)- 只能有一个将
|
||||
if (!hasJiang && count >= 2) {
|
||||
List<Integer> remaining = new ArrayList<>(handCards);
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
if (checkNormalHuRecursive(remaining, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查牌是否能组成顺子或刻子
|
||||
*/
|
||||
private boolean canGroup(List<Integer> cards) {
|
||||
if (cards.isEmpty()) {
|
||||
return true; // 所有牌都分组成功
|
||||
}
|
||||
|
||||
List<Integer> sorted = new ArrayList<>(cards);
|
||||
Collections.sort(sorted);
|
||||
|
||||
int firstCard = sorted.get(0);
|
||||
int count = Collections.frequency(sorted, firstCard);
|
||||
|
||||
// 尝试作为刻子(三张相同)
|
||||
if (count >= 3) {
|
||||
List<Integer> remaining = new ArrayList<>(sorted);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
}
|
||||
if (canGroup(remaining)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试作为顺子(三张连续)
|
||||
int type = firstCard / 100;
|
||||
int value = firstCard % 100;
|
||||
|
||||
if (type < 4 && value <= 7) { // 字牌和8、9不能组成顺子
|
||||
int second = type * 100 + (value + 1);
|
||||
int third = type * 100 + (value + 2);
|
||||
|
||||
if (sorted.contains(second) && sorted.contains(third)) {
|
||||
List<Integer> remaining = new ArrayList<>(sorted);
|
||||
remaining.remove(Integer.valueOf(firstCard));
|
||||
remaining.remove(Integer.valueOf(second));
|
||||
remaining.remove(Integer.valueOf(third));
|
||||
if (canGroup(remaining)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有麻将牌
|
||||
*/
|
||||
private Set<Integer> getAllCards() {
|
||||
Set<Integer> allCards = new HashSet<>();
|
||||
// 万条筒 1-9
|
||||
for (int type = 1; type <= 3; type++) {
|
||||
for (int value = 1; value <= 9; value++) {
|
||||
allCards.add(type * 100 + value);
|
||||
}
|
||||
}
|
||||
return allCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为可读格式
|
||||
*/
|
||||
private String convertToReadable(List<Integer> cards) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int card : cards) {
|
||||
int type = card / 100;
|
||||
int value = card % 100;
|
||||
sb.append(value).append(getTypeName(type)).append(" ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取牌型名称
|
||||
*/
|
||||
private String getTypeName(int type) {
|
||||
switch(type) {
|
||||
case 1: return "万";
|
||||
case 2: return "筒";
|
||||
case 3: return "条";
|
||||
default: return "字";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手牌分析类
|
||||
*/
|
||||
class HandAnalysis {
|
||||
int keziCount = 0; // 刻子数量
|
||||
int pairCount = 0; // 对子数量
|
||||
int shunziCount = 0; // 顺子数量
|
||||
int singleCount = 0; // 单张数量
|
||||
List<Integer> singles = new ArrayList<>(); // 单张列表
|
||||
boolean has258Jiang = false; // 是否有258将
|
||||
List<Integer> pairs = new ArrayList<>(); // 对子列表(value)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue