package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // 巡卫官(里希 / 15021021)盘查链路的最后一步 —— 开罚单出结果。 // // 客户端 PoliceExamineManager.lua:1163 gPoliceJobManager.cs:AskPoliceFine(pid, fineIdList) // ⇒ C2S AskPoliceFineNpc(63548238) // ⇒ 服务端必须回 S2C ShowReceiveRewardDetail(64145515) 且 Reason == ItemReason.PoliceFine(180001) // ⇒ GameToClientImpl.lua:266 gPoliceJobManager:OnDropPoliceJobExp(msg.Reward) // ⇒ PoliceExamineManager.lua:618 填 fineMoney / JobExpInfo // ⇒ PoliceExamineManager.lua:601 ShowFineResult(罚款结果面板) // // ⛑⛑ 为什么之前"开罚单没结果":AskPoliceFineNpc 既没进 // Enabled4229938MethodIds(硬规则 94)也没 handler ⇒ 客户端 await 卡死, // 服务端从来没回过掉落帧 ⇒ needShowFineResult=true 但 OnDropPoliceJobExp // 永远不触发 ⇒ ShowFineResult 一次都没被调用。 // ⛑ 反编译陷阱(又是同一条,见已作废结论): // GameToClientImpl.lua:266 里写的是 if msg.Reason ~= ItemReason.PoliceFine then, // 字面读是"非罚款才回调",但同一函数里 if msg.Reason ~= Mall or msg.Reason ~= MallBundle // 恒为真会无条件 return ⇒ 字面读整段逻辑自相矛盾 ⇒ 反编译器把 == 翻成了 ~=。 // 真意:Reason == PoliceFine 时才走 OnDropPoliceJobExp。 // ItemReason.PoliceFine = 180001(dump.cs:1737137,enum ItemReason TypeDefIndex:32206)。 // 回滚 = 删本文件 + 从 GameRouter.cs 白名单删掉 MethodId.AskPoliceFineNpc。 // // ================================================================================= // ① C2S AskPoliceFineNpc(63548238) // ================================================================================= // // 玩家在盘查面板里选完罪名、点「开罚单」时由 C# 侧发出来的那条。 // 形状:AskPoliceFine(ulong pid, object data)(dump.cs:673124),data 是 int[]。 // 极性 CSharpOnly ⇒ lua 没有 writer 可抄、count 编码不确定 ⇒ 不硬解析, // 只把前 8 字节当 u64 pid 记日志,其余原样打 hex 留作取证。 // ⛑ 用 HandlerPacketKind.Both:INVOKE / NOTIFY 都不能漏。 // INVOKE 不回执 ⇒ 客户端 await 到超时(硬规则 94 的同一类坑)。 // func GameRouter_PoliceFine_AskPoliceFineNpc(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_PoliceFine_AskPoliceFineNpc var zero any; return zero } // ================================================================================= // ② S2C ShowReceiveRewardDetail(64145515) —— 手搓字节 // ================================================================================= // // 推一帧 ShowReceiveRewardDetail(64145515, LuaOnly),Reason = PoliceFine(180001)。 // 载荷(照 Auto.Reader[308] / Auto.Reader[500],RPCDeserializeAuto.lua:37641 / 37521): // // int32 Reason = 180001 // uint32 RewardTemplate = 0 // list FirstItemInfo = FF + int32 count + uint32* (空) // cplx ExtraInfo = 00(null —— 必须 null!DropManager.lua:126 见 ExtraInfo 非空 // 就会拿 ExtraInfo.Pos 判"掉落物在地上"并 return,罚款结果就没了) // dict Reward = FF + int32 count + { byte key, cplx RewardDetail } // // RewardDetail(Reader[500])28 个字段全按声明顺序写,空的集合一律 FF + int32 0 // (Serializer.ReadList / ReadDict = peek byte FF + 原始 int32 count,不带 +1 偏置; // 只有 ReadList7Bit / ReadDict7Bit 才走 Int7 +1 偏置)。 // SpiritTalentExpInfo 是 ReadComplex ⇒ 写 00。 // ⛑⛑ 关键判据(PoliceExamineManager.lua:618): // // fineMoney = Σ RewardDetail.Money // if RewardDetail.JobExpInfo[Police] then JobExpInfo = RewardDetail.JobExpInfo end // if JobExpInfo or fineMoney <= 0 then ShowFineResult() end // // ⇒ JobExpInfo 里必须命中 Police(11300003),否则 money>0 时永远不弹。 // 所以这里 money 与 exp 都给,双保险。 // func GameRouter_PoliceFine_PushFineRewardAsync(ctx context.Context, session *GameSession, money float64, jobExp uint32, token context.Context) any { // Router implementation for GameRouter_PoliceFine_PushFineRewardAsync var zero any; return zero } // 照 Auto.Reader[308] / Auto.Reader[500] 手搓 ShowReceiveRewardDetail。 func GameRouter_PoliceFine_BuildFineRewardWire(ctx context.Context, money float64, jobExp uint32) []byte { // Router implementation for GameRouter_PoliceFine_BuildFineRewardWire return nil } // Serializer.ReadList 的空集合:peek FF + 原始 int32 count = 0。 func GameRouter_PoliceFine_EmptyList(ctx context.Context, w any) any { // Router implementation for GameRouter_PoliceFine_EmptyList var zero any; return zero } // Serializer.ReadDict 的空字典:peek FF + 原始 int32 count = 0。 func GameRouter_PoliceFine_EmptyDict(ctx context.Context, w any) any { // Router implementation for GameRouter_PoliceFine_EmptyDict var zero any; return zero }