package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // 巡卫官(里希 / 15021021)执勤终端里的三条派遣,C2S 都是 AskPoliceDispatch(63500791), // 只靠 dispatchid 区分: // // PoliceDispatchCatalog.json // Id=2 NCCA Dog 巡卫犬 SkillId 51938184 / SwitchSkillId 51938186 // Id=3 Helicopter Search 直升机搜查 SkillId 0 → 走 S2C SyncPoliceDispatchHelicopter(68359668) // Id=4 Armed Support 战术支援 SkillId 0 Des「冲锋枪2人」,DialogId 100025472 // // 2026-09-24 抓包:按钮一直在发,服务端一直在回空 OK。 // logs/packets-20260924-233242.log:209939 23:46:42 C2S AskPoliceDispatch id=3, // 服务端 DefaultUnknownInvoke4229938 回 error=0 body=0b ⇒ 客户端不卡死, // 但世界上什么也没发生。unknown-methods.log 里 09-21 还有 id=2/3/4 各一次。 // 本文件只接管 dispatchid=4(战术支援)。2 走技能链(GameRouter.HackerSummon.cs), // 3(直升机)契约只有两个参数但目前没有实体模板可刷,先留日志位。 // ⛑ 硬规则 94:光写 [Handler] 不算注册,GameRouter.cs 的 // Enabled4229938MethodIds 必须同步加 MethodId.AskPoliceDispatch, // 否则编译通过、功能零生效,而这是个 INVOKE —— 客户端会 await 到超时。 // // ================================================================================= // ① C2S AskPoliceDispatch(63500791) // ================================================================================= // // 执勤终端 / 轮盘的派遣按钮。INVOKE —— 客户端 gReliableRpcManager 等回执, // 无论 dispatchid 认不认都得先回 empty OK,否则按钮 await 永不落地。 // func GameRouter_PoliceDispatch_AskPoliceDispatch(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_PoliceDispatch_AskPoliceDispatch var zero any; return zero } func GameRouter_PoliceDispatch_OnPoliceDispatch(ctx context.Context, rpcCtx *RpcContext, dispatchId uint32) any { // Router implementation for GameRouter_PoliceDispatch_OnPoliceDispatch var zero any; return zero } // ================================================================================= // ② 战术支援:刷两个友方冲锋枪巡卫官 // ================================================================================= func GameRouter_PoliceDispatch_DispatchArmedSupportAsync(ctx context.Context, rpcCtx *RpcContext) any { // Router implementation for GameRouter_PoliceDispatch_DispatchArmedSupportAsync var zero any; return zero } // // 收掉当前在场的支援单位。SpawnAgentAsync 出来的单位配对的移除通道是 // SyncLogicAgentLeave(68281244)(第一句话是 SyncLogicAgentEnter)—— 与 // GameRouter.HackerSummon.cs 里回收召唤物用的是同一条,别用 SyncAetherAINpcRemove(那是 // ambient 行人 / functional NPC 的通道,对这类单位发了等于没发)。 // func GameRouter_PoliceDispatch_DismissArmedSupportAsync(ctx context.Context, rpcCtx *RpcContext) any { // Router implementation for GameRouter_PoliceDispatch_DismissArmedSupportAsync var zero any; return zero } // ================================================================================= // ②b 直升机载具:让场景里真的出现一架直升机 // // ⛑ 这是替代方案,不是官方实现的复刻 —— 官方直升机搜查面板在 build 4229938 里是半成品 // (硬规则 134:无 Open 入口 / SyncPoliceDispatchHelicopter 空实现 / dump.cs 无 C# 类), // 服务端再怎么接也只是在后台记账。刷这台载具是唯一能"看见直升机"的手段。 // ================================================================================= // // 刷一台搜查直升机载具(81007007 Helicopter01a_Rope)。 // ⛑ SpawnDirectAsync 内部自带 replacement:刷之前会把 runtime.SummonedEntity // 上那台(可能是玩家自己召唤的车)DestroyVehicleAsync 掉。这里只提前打一行 // warn,不拦 —— 玩家点「直升机搜查」时通常没在开车,而且顶掉比堆车好。 // 落点是玩家右侧 5.5 m、+0.15 m(SpawnDirectAsync 里的 proven offset)。对直升机来说 // 就是停在旁边地面上,不需要抬高度(抬了反而会卡在半空下不来)。 // func GameRouter_PoliceDispatch_SpawnHelicopterVehicleAsync(ctx context.Context, rpcCtx *RpcContext) bool { // Router implementation for GameRouter_PoliceDispatch_SpawnHelicopterVehicleAsync return true } // // 收掉这架搜查直升机(一轮搜查结束时调)。 // 走 DestroyVehicleAsync ⇒ S2C SyncDestroyVehicle,与玩家召唤车 / 交通车 // 用的是同一条通道,不是 SyncLogicAgentLeave(那是 agent 的)。 // func GameRouter_PoliceDispatch_DismissHelicopterVehicleAsync(ctx context.Context, rpcCtx *RpcContext) any { // Router implementation for GameRouter_PoliceDispatch_DismissHelicopterVehicleAsync var zero any; return zero } // // C2S AskHelicopterNpc(67113443),INVOKE —— 玩家在直升机视角里框住一个 NPC 并下判定。 // ⛑⛑ 这条以前不在 Enabled4229938MethodIds 里,等于走中性兜底;而它是 INVOKE, // 客户端四个调用点(PoliceHelicopterPanelStore.lua:162 / :378 / :470 / :558)全在用 // gReliableRpcManager:RegisterRPC 等回调 ⇒ 面板会卡在"点了没反应"。 // 硬规则 94 的老坑,别再踩。 // func GameRouter_PoliceDispatch_AskHelicopterNpc(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_PoliceDispatch_AskHelicopterNpc var zero any; return zero } // // C2S AskHelicopterScanChange(67898923),NOTIFY —— 直升机扫描开关。 // 客户端用的是 self.Notify(ClientToGameSceneDelegate.lua:5198),没有回执; // 这里登记 Both 兜住,将来客户端改成 Invoke 也不用改代码(AskControlAgent 那次就是这么栽的)。 // func GameRouter_PoliceDispatch_AskHelicopterScanChange(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_PoliceDispatch_AskHelicopterScanChange var zero any; return zero } // // C2S AskPoliceStopHelicopterDispatch(63057829),INVOKE,无参 —— 一轮搜查结束 // (客户端在"扫描成功"后延迟 1 s 发它,见 PoliceHelicopterPanelStore.lua:483)。 // 收到后清账,并推一次 S2C SynExitHelicopterView(68309986) 收视角。客户端那个 // handler 就是 Close(gPanelId.S_POLICE_HELICOPTER),幂等 —— 面板已经关了再 Close 一次无害。 // func GameRouter_PoliceDispatch_AskPoliceStopHelicopterDispatch(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_PoliceDispatch_AskPoliceStopHelicopterDispatch var zero any; return zero } func GameRouter_PoliceDispatch_GetOrCreateHelicopterScan(ctx context.Context, rpcCtx *RpcContext) any { // Router implementation for GameRouter_PoliceDispatch_GetOrCreateHelicopterScan var zero any; return zero } // ================================================================================= // ③ 直升机搜查:AskHelicopterNpc / AskHelicopterScanChange / AskPoliceStopHelicopterDispatch // ================================================================================= // // 一轮直升机搜查的服务端记账。官方玩法里这些标记会喂给"盘查 / 通缉 / 案件"那条链, // 我们那条链是空的,所以先只把玩家框过的单位记下来 —— 至少让 RPC 有回执、有日志可查。 // type HelicopterScanState struct { // 扫描开关(AskHelicopterScanChange)。 ScanOpen bool `json:"scanOpen"` // instanceid → 最后一次 optype。 Marks map[uint64]byte `json:"marks"` }