125 lines
6.2 KiB
Go
125 lines
6.2 KiB
Go
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
|
||
)
|
||
|