package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // T2-32 路人骇入(Seymour 骇入路人的手机看信息)—— 2026-09-24 接入。 // 整条链路客户端早就写好了,服务端只缺两样东西(取证见 // modules/黑客大停电与广告牌坐标.md §7.8): // 1. 从来没推过 SyncUnitHackableState(68941159) ⇒ C# 的 HackManager.hackUnitTarget // 永远是 nil ⇒ GadgetManager.RefreshHackInteract 不会把路人装进 // HackInteractList ⇒ 路人身上根本没有骇入按钮; // 2. 三个 C2S(AskHackingNpc 67070057 / AskHackingNpcPress 67747341 / // ReportBeHacked 67093669)没进白名单 ⇒ 就算玩家按了也是静默落兜底, // 而前两个是 Invoke,不 ack 会让客户端 await 卡死。 // 两者完全自洽:logs/unknown-methods.log 里 AskHackingNpc 命中 0 次。 // ⛑️ 本文件不实现"看信息"的内容:面板(HackerInfoPanelStore.lua:267 / // HintInfosHudStore.lua:2132)读的是客户端手里的 NPC 数据,服务端只负责放行。 // // // 把一个 NPC 标记为可骇 / 取消标记。 // 与载具那套(SyncVehicleHackableState + SyncVehicleBrokenCollision)对齐: // 只在状态翻转时发,重复发同一个值没有意义(C# 侧就是个集合/标志位)。 // func GameRouter_HackNpc_PublishNpcHackableAsync(ctx context.Context, session *GameSession, unitId uint64, hackable bool, token context.Context) any { // Router implementation for GameRouter_HackNpc_PublishNpcHackableAsync var zero any; return zero } // // NPC 出场时调一次。 gate:Gameplay.HackNpc.MarkSpawnedNpcs。 // 放在 SendCivilianAsync / SendAmbientCrowdPedAsync 的尾部 —— // 必须先有单位(StaticNpcAddData / CrowdAdd + ManagedLogicAgent), // C# 才能在 SceneDataMgr.GetUnit(unitId) 里找到它。 // func GameRouter_HackNpc_MarkNpcHackableOnSpawnAsync(ctx context.Context, session *GameSession, unitId uint64, channel any, token context.Context, skipPhone bool) any { // Router implementation for GameRouter_HackNpc_MarkNpcHackableOnSpawnAsync var zero any; return zero } // // ⭐ 给 NPC 发一部手机并让他正在玩/正在打 —— 骇入面板下面那排按钮就是从这儿来的。 // 证据链(2026-09-24,用户回报"骇入路人后只能看名字,不像汽车/售货机有选项"): // 1. HackerInfoPanelStore.lua:119(反编译,比较符极性反转)路人分支走 // self.textIds = HackManager:GetUnitHackBtns(data.unit), // btnNum = min(#textIds,3) ⇒ 列表为空就是"只剩标题(名字)"; // 2. GetUnitHackBtns 遍历六个 NpcHackSkill,逐个问 CanUse(unit, tracker, debug); // 3. PhoneUsageSkillBase.CanUseInternal 看的是 // BelongingBelongingUsageConfig.HackType,全三条非 0 的 usage 都要求持有手机 // BelongingSceneItem = 80003481; // 4. 我们下发的 crowd 行人没有任何 belongings,而 static NPC 用的是 POI 的 usage // (18/86/93/237/238/241 —— HackType 全 0)⇒ 一条都对不上 ⇒ 零按钮。 // 与 GameRouter.Civilians.cs / GameRouter.DonorNpc.cs 给 NPC 递道具的路径完全同包同结构, // 只是把"POI 决定拿什么"换成"一律给手机"。 // func GameRouter_HackNpc_PublishNpcPhoneAsync(ctx context.Context, session *GameSession, unitId uint64, channel any, token context.Context) any { // Router implementation for GameRouter_HackNpc_PublishNpcPhoneAsync var zero any; return zero } // // 敌人版本的手持道具 —— 与 PublishNpcPhoneAsync 同一套 wire // (SyncAgentBelongings → SyncAgentUseBelongingChanged),只是配置源换成 // gameplay.combat.enemies,调敌人手里的东西时不会动到全城路人。 // 为什么单独一份:两者的生命周期完全不同。路人的手机要在每次 SendCivilianAsync // / crowd ped 出场时按 MaxPhonedNpcs 计数限量推;敌人是 GM 面板一次性指令, // 数量本来就很少,再套那层保险丝只会让第 N 个敌人之后突然不带东西,很难解释。 // ⛑ 调用时机必须在单位已经在客户端落地之后(SyncManagedLogicAgent + // SyncRaidBattleUnitAgent 都已发出),否则 SceneDataMgr.GetUnit(uid) // 查不到这个单位,两个包都会被静默丢掉。 // func GameRouter_HackNpc_PublishEnemyPropAsync(ctx context.Context, session *GameSession, unitId uint64, templateId uint32, token context.Context) any { // Router implementation for GameRouter_HackNpc_PublishEnemyPropAsync var zero any; return zero } // // AskHackingNpc(67070057) — 玩家点下了路人骇入按钮。 // ⛑️ 必须 HandlerPacketKind.Both:客户端走的是 Invoke // (ClientToGameSceneDelegate.lua:489),但按 AskControlAgent 那次的教训, // 同族 RPC 会有 NOTIFY 变体,只注册 Invoke 就是静默丢包。 // 返回体是空 reader ⇒ ReturnEmptyOkAsync(对 NOTIFY 是 no-op)。 // func GameRouter_HackNpc_OnAskHackingNpc(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_HackNpc_OnAskHackingNpc var zero any; return zero } // // AskHackingNpcPress(67747341) — 长按骇入的那一下(GadgetManager.lua:1851)。 // 同样是 Invoke + 空 reader。 // func GameRouter_HackNpc_OnAskHackingNpcPress(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_HackNpc_OnAskHackingNpcPress var zero any; return zero } // // ReportBeHacked(67093669) — NOTIFY,客户端告知"这些单位被骇了"。 // 纯记账:没有 ack,也没有需要回推的包(面板内容在客户端)。 // func GameRouter_HackNpc_OnReportBeHacked(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_HackNpc_OnReportBeHacked var zero any; return zero } // // NPC 的来路。手机要不要发,两条通道的政策不同(HackNpcSettings 里各有一个开关): // crowd 行人本来一件 belongings 都没有,static NPC 则由 POI 决定拿什么在做什么。 // type HackNpcChannel int32 const ( // crowd 行人(SendAmbientCrowdPedAsync,SyncAetherAICrowdAdd)。 HackNpcChannel_Crowd HackNpcChannel = iota // 服务端自己生成的 static NPC(SendCivilianAsync,SyncAetherAIStaticNpcAddData)。 HackNpcChannel_Static )