141 lines
8.9 KiB
Go
141 lines
8.9 KiB
Go
package routers
|
||
|
||
import (
|
||
"context"
|
||
"sync"
|
||
)
|
||
|
||
var _ = sync.Mutex{}
|
||
var _ = context.Background
|
||
|
||
//
|
||
// 五子棋**单机**(人机 / 残局)—— EnterGomokuZoneDoubleAI(67614971) 与
|
||
// EnterGomokuZoneEndGame(67341006)。
|
||
// <para>★ 现象与根因链(四刀,每一刀都有本机取证)</para>
|
||
// <list type="number">
|
||
// <item>R153 入口未登记:这两条从未进白名单 ⇒ 走 DefaultReturnCatalog4229938
|
||
// 兜底(:1611 / :1710)回 8 个 0x00 = **裸 u64 = 0**。客户端
|
||
// [warning]StartGomoke AI: TryGetLogicAgent fail, uid=0 /
|
||
// StartGomoke EndGame: TryGetLogicAgent fail, uid=0。
|
||
// ⇒ 必须登记并回一个**真实存在的**实体 uid。</item>
|
||
// <item>R155 只有 uid 装不下身份:SyncLogicAgentEnter 的 interop 签名只有一个 ulong,
|
||
// 而 LogicAgent.AgentConfigId 是 uint ⇒ 只注册 uid 会让客户端报
|
||
// 创建AgentUnit失败!AgentConfigId为0!。
|
||
// 唯一能送 AgentConfig 的通道是 **Aether 静态 NPC**(NpcFormworkId 就是 AgentConfig id),
|
||
// 而客户端在请求体第 2 个字段 agentid 里**自己把该填哪个告诉我们了**。</item>
|
||
// <item>R156 缺棋局状态推送(本轮真正卡住落子的那一刀):
|
||
// 客户端点子链是 TryRayIntersectBoard → PlaceGomokuPiece →
|
||
// AskPlaceGomokuPiece,而闸门是 GomokuManager.get_NowMyTurn();
|
||
// 唯一能置起"我的回合"的输入就是 SyncGameGroundZoneInfo(68004609)。
|
||
// 实测 AskPlaceGomokuPiece / PlaceGomokuPiece / SyncGameGroundZoneInfo
|
||
// 在全部历史日志里 **0 命中** ⇒ 客户端从没收到过 ⇒ 闸门恒关 ⇒ **一条落子包都不发**。</item>
|
||
// <item>R185 参与方列表不能为空:客户端靠 ipairs(zoneInfo.ParticipantInfos) 拿到
|
||
// pendingOpponentAgentId。推空列表 ⇒ 它永远是 0 ⇒ 闸门还是不开。
|
||
// 且列表元素是**多态**的,每个元素自带 1B tag = GomokuWire.ParticipantTagGomoku(12);
|
||
// 若用裸 [UxContract](默认 0xFF)会被 Dispatch 判成 undefined 分支全部丢弃。</item>
|
||
// </list>
|
||
// <para>★ 三步法与主线自己的 GameRouter.ChineseChess.cs(SpawnChineseChessAiAsync)
|
||
// **完全同形**(那里注释原话:"与五子棋 R153/R155/R157 三步完全同形"):
|
||
// ① SyncLogicAgentEnter 注册 uid → ② SendCivilianAsync 送 AgentConfig + 站位
|
||
// → ③ 把同一个 uid 回给客户端 / 写进 ParticipantInfos[].AgentInstanceId。</para>
|
||
// <para>⛑⛑ 不要加"等 NPC 落地"的延迟:象棋的实测证据是客户端
|
||
// TryGetOrConvertToUnit 查不到时**会等待**(10 秒后才打"等待服务器对手 NPC 单位超时"),
|
||
// 不是立刻失败 ⇒ 时序不是问题,缺的是状态推送。曾按"时序"假设加过延迟,方向是错的。</para>
|
||
// <para>契约 DTO 见 Drmk.RpcTypes/Client4229938/GomokuWireContracts.cs(文件头有逐字段的本机复核)。</para>
|
||
//
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 两条单机入口
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
//
|
||
// EnterGomokuZoneDoubleAI(67614971) 人机对战。
|
||
// C2S = u64 gadgetuid + u32 agentid + bool useblackpiece + byte difficulty + bool canuseskill。
|
||
// RET = **裸 u64**(对手实体的真实 uid)。
|
||
//
|
||
func GameRouter_GomokuSolo_EnterGomokuZoneDoubleAI4229938(ctx context.Context, conn any, msg any) any {
|
||
// Router implementation for GameRouter_GomokuSolo_EnterGomokuZoneDoubleAI4229938
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// EnterGomokuZoneEndGame(67341006) 残局。
|
||
// C2S = u64 gadgetuid + u32 agentid + u32 endgameid。RET = **裸 u64**(同上)。
|
||
// ⛑ 与 DoubleAI 的差别只有:请求里**没有** useBlack 字段 ⇒ 按残局约定「玩家先手 = 黑」。
|
||
//
|
||
func GameRouter_GomokuSolo_EnterGomokuZoneEndGame4229938(ctx context.Context, conn any, msg any) any {
|
||
// Router implementation for GameRouter_GomokuSolo_EnterGomokuZoneEndGame4229938
|
||
var zero any; return zero
|
||
}
|
||
|
||
// 把对手 uid 按「裸 u64」拼成 8 字节小端回给客户端。
|
||
func GameRouter_GomokuSolo_ReturnGomokuUidAsync(ctx context.Context, conn any, msg any, uid uint64) any {
|
||
// Router implementation for GameRouter_GomokuSolo_ReturnGomokuUidAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// ★★★ R156 核心:把「棋局已开始、现在轮到谁、对手是谁」推给客户端
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
//
|
||
// 推 SyncGameGroundZoneInfo(68004609) + GomokuZoneInfo(多态 tag 13)。
|
||
// 这是「点棋盘没反应」与「能落子」之间缺的那一刀 —— 客户端 get_NowMyTurn()
|
||
// 的置位只有这一个输入来源。
|
||
// <para>★ 座位/先手(别与象棋的「颜色」混淆):五子棋 CurrentTurn 是 **SeatIndex 座位号**。
|
||
// GomokuWire.SeatIndexBlack = 0;客户端两个单机入口都 useBlack=true
|
||
// ⇒ 玩家 mySeatIndex = 0、黑棋先手 ⇒ CurrentTurn 必须是 0。
|
||
// 玩家 SeatIndex=0 / AI SeatIndex=1 / CurrentTurn=玩家座位 —— **三者必须自洽**,
|
||
// 写错任一个都是「点不动」。</para>
|
||
// <para>回滚 = 删两个 handler 里对本方法的调用(两行)。</para>
|
||
//
|
||
func GameRouter_GomokuSolo_PushGomokuZoneInfoAsync(ctx context.Context, conn any, gadgetUid uint64, gameType byte, aiUid uint64, playerUsesBlack bool) any {
|
||
// Router implementation for GameRouter_GomokuSolo_PushGomokuZoneInfoAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 造对手 —— 三步法,与 GameRouter.ChineseChess.cs 的 SpawnChineseChessAiAsync 同形
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
//
|
||
// 建一个「客户端查得到的 AI 对手」,返回它的**真实 uid**。三步:
|
||
// ① SyncLogicAgentEnter 注册 uid(客户端 TryGetLogicAgent 查的就是这张表);
|
||
// ② 用**客户端给的 agentId** 走 Aether 静态 NPC 通道(NpcFormworkId = AgentConfig id,
|
||
// 不送的话客户端建 Unit 时报 创建AgentUnit失败!AgentConfigId为0!);
|
||
// ③ 把同一个 uid 交回调用方(回包 + 写进 ParticipantInfos)。
|
||
// <para>★ R157 复用:**同一会话同一张桌只 spawn 一次**。理由(象棋同源注释原话):
|
||
// 「旧的那个删不掉(本版客户端无可靠删除通道)⇒ 场上越来越多」。
|
||
// 实测本仓 MethodId 里没有 SyncAetherAIStaticNpcRemove*;
|
||
// SyncRemoveNpc(68446556) 的 reader 是 Int32 ⇒ 装不下 830… 段的 u64。
|
||
// ⇒ 与其猜"怎么删",不如**根本不重复建**。</para>
|
||
// <para>缓存键带 gadgetUid(庙会区有多张 WuZiQi 桌,要各有一个对手,不能互相顶掉)。
|
||
// 作用域用 TcpSession.Items —— 断线即弃,不会泄漏到别的会话。</para>
|
||
//
|
||
func GameRouter_GomokuSolo_SpawnGomokuOpponentAsync(ctx context.Context, conn any, gadgetUid uint64, agentId uint32, playerUsesBlack bool, tag string, endGameId uint32) any {
|
||
// Router implementation for GameRouter_GomokuSolo_SpawnGomokuOpponentAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// 挑对手模板:优先用客户端请求里带来的 agentId(实测 40131483「廖永强」),
|
||
// 它不在 NpcCatalog 里时才退回通用市民(骰子 PickDiceOpponentItem 同款挑法)。
|
||
//
|
||
func GameRouter_GomokuSolo_PickGomokuOpponentItem(ctx context.Context, agentId uint32) any {
|
||
// Router implementation for GameRouter_GomokuSolo_PickGomokuOpponentItem
|
||
var zero any; return zero
|
||
}
|
||
|
||
// 摊位表里离玩家最近的那个五子棋机关;表为空时返回 null。
|
||
func GameRouter_GomokuSolo_FindGomokuStall(ctx context.Context, px float32, pz float32) any {
|
||
// Router implementation for GameRouter_GomokuSolo_FindGomokuStall
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// 造 NPC 失败时的兜底 uid(骰子 FallbackUid 同款)。刻意不是 0 ——
|
||
// 0 会被客户端当成「进场失败」(套圈注释的判据),至少让它走到
|
||
// 「等待 Agent 实体同步」而不是当场判死,日志里也好区分。
|
||
//
|
||
func GameRouter_GomokuSolo_GomokuFallbackUid(ctx context.Context, ) uint64 {
|
||
// Router implementation for GameRouter_GomokuSolo_GomokuFallbackUid
|
||
return 0
|
||
}
|
||
|