package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // 场景物品(路灯 / 街边道具 / 可破坏物 / 念动力目标)的服务端流式下发 —— AnantaWorldMod // 的等价实现,不需要 frida 注入器。 // 原理与 GameRouter.GadgetGrid.cs 完全同构:客户端自己有烘焙好的 SceneItemBlob,服务端 // 只要告诉它「哪几个 40m 网格在视野内」,客户端就从 blob 里把物品铺出来。 // 注入器每 700ms 调 SceneItemMgr.SyncSceneItemGridAOIAddByIndex(cx, cz, exceptIds, sectorIdList, // playerStandardIndex);服务端等价物 = 推 SyncDestructibleGridAOIDecrease(68969593), // 接收端 GameSceneToClientImpl 三连展开正是 remove → increase → AddDecrease(→AddByIndex)。 // ⛑️⛑️ 必须用 68969593,不是 68934309:68934309 的 args 只装得下 aoiInfo 一个字段 // (dump.cs TypeDefIndex 35754),它的接收端是记账用的;网格下发只能走 Decrease 孪生 // (与 gadget 的 68956797 同律)。用错 mid 的表现是「推了、日志干净、一个都不出」。 // 网格→sector 表直接复用 GadgetCellsCatalog4229938(Configs/GadgetCells.json): // 那份文件本身就是 AnantaWorldMod 运行时导出的 cell→sector(5116 格,meta 写明 // "runtime export from 4229938 client"),两个家族用的是同一张烘焙表。 // func GameRouter_SceneItemGrid_SceneItemGridStateOf(ctx context.Context, session *GameSession) any { // Router implementation for GameRouter_SceneItemGrid_SceneItemGridStateOf var zero any; return zero } // // ★★★ R93(自 donor v2.1「打气球」移植)—— 把「脚本自建的可破坏物」登记为受保护, // 直到显式撤下。心跳 / 刷新重声明 AOI 窗口时,这些 id 会进 ExceptIds 并随 // addInfos 一起重发,从而不被客户端按烘焙 blob 覆盖掉。 // func GameRouter_SceneItemGrid_ProtectDestructibles(ctx context.Context, session *GameSession, items []any) any { // Router implementation for GameRouter_SceneItemGrid_ProtectDestructibles var zero any; return zero } // ★ R93 — 撤下保护(离开气球区 / 小游戏结束时用)。 func GameRouter_SceneItemGrid_UnprotectDestructibles(ctx context.Context, session *GameSession, ids []any) any { // Router implementation for GameRouter_SceneItemGrid_UnprotectDestructibles var zero any; return zero } // 世界 ready 钩子:启动场景物品流。幂等;配置关闭时 no-op。 func GameRouter_SceneItemGrid_EnsureSceneItemGrid(ctx context.Context, session *GameSession) any { // Router implementation for GameRouter_SceneItemGrid_EnsureSceneItemGrid var zero any; return zero } // 断线 / 切场景:停循环。已经铺出来的物品留在原地。 func GameRouter_SceneItemGrid_StopSceneItemGrid(ctx context.Context, session *GameSession) any { // Router implementation for GameRouter_SceneItemGrid_StopSceneItemGrid var zero any; return zero } // 已下发网格数(面板 / 调试用)。 func GameRouter_SceneItemGrid_SceneItemGridSentCount(ctx context.Context, session *GameSession) int32 { // Router implementation for GameRouter_SceneItemGrid_SceneItemGridSentCount return 0 } func GameRouter_SceneItemGrid_RunSceneItemGridLoopAsync(ctx context.Context, session *GameSession, owner any, token context.Context) any { // Router implementation for GameRouter_SceneItemGrid_RunSceneItemGridLoopAsync var zero any; return zero } func GameRouter_SceneItemGrid_TickSceneItemGridAsync(ctx context.Context, session *GameSession, owner any) any { // Router implementation for GameRouter_SceneItemGrid_TickSceneItemGridAsync var zero any; return zero } // // 把 (cx,cz) 周围还没发过的网格按 sector 列表分组下发,一组一条消息。返回发出的格子数。 // func GameRouter_SceneItemGrid_PushSceneItemGridAsync(ctx context.Context, session *GameSession, owner any, cx int32, cz int32, reason byte, fullResend bool, spawns []any) int32 { // Router implementation for GameRouter_SceneItemGrid_PushSceneItemGridAsync return 0 } // // ★ donor《content-streaming-implementation》§4 —— 显式的「请卸载这批格子」。 // 单独一条 68969593:addGridInfo 空表、removeGridInfo 装出界的格子。 // ⛑ donor §4 提醒 family 差异:68969593 没有 extraInfo / activeIds / inactiveIds // 三个字段(那是 gadget 侧才有的),别照 GadgetGrid 那份填。 // ⛑ BuildingId 必须写 int32(值 0),漏写会让整条 payload 位移。 // func GameRouter_SceneItemGrid_PushSceneItemGridRemovalAsync(ctx context.Context, session *GameSession, removed []any, cx int32, cz int32, reason byte) any { // Router implementation for GameRouter_SceneItemGrid_PushSceneItemGridRemovalAsync var zero any; return zero } // // 念动力探针(硬规则 28):DestructibleGridAOIIncrease.addInfos 可以凭空造一个可抓的 // SceneItem —— 给 CfgId(SceneitemConfig 里 CanQInteraction=true 的条目)+ // Position 就够,客户端会实例化成真物件并被 MindPowerMgr 认成 Q 目标。 // 这是 T1-22 里唯一能直接验证「服务端显式摆放」这条路的手段:箱子出现 ⇒ 路通,家具不出 // 只是缺坐标;箱子都不出 ⇒ 先修通道。 // ⛑️ 只在本会话第一次推送时摆:心跳 / 刷新 / 跨格重声明都会重发整窗,摆第二次会叠箱子。 // ⛑️ 序列化的坑:DestructibleInfo 的客户端 reader 是多态的 // (ReadStaticDestructibleInfo 跳转表:0=null / 1=BASE / 2=Dynamic / 3=Static / 4=Task)。 // 这里先走 UxSerializer 的常规写法;若客户端日志出现 // deserialize failed ... parameter=DestructibleInfo,就说明缺那个 1=BASE 的标签字节, // 需要改成手写裸字节(见 SceneItemGridMethods4229938.cs 里 DestructibleInfo4229938 的注释)。 // 失败的包只会被客户端丢弃,不会污染场景,所以这个探针可以放心试。 // // // ★ R93 —— 把登记进 Protected 的可破坏物 DTO 归一到本管线使用的 // SceneMethods.DestructibleInfo4229938。 // 气球(GameRouter.Balloon.cs)持有的是 BowlingWire 那份 DestructibleInfo // —— 它是**真机验证过能建出 hold** 的标注(Marker=1 / BASE),而网格通道这份是同构双胞胎 // (SceneMethods,字段一致但 UXVector3 用的是 Auto 版)。两份绝不能混用(会静默串位), // 所以登记时存 object、心跳时在这里逐字段映射。认不出的类型返回 null(该件不摆)。 // func GameRouter_SceneItemGrid_ToGridDestructibleInfo(ctx context.Context, info any) any { // Router implementation for GameRouter_SceneItemGrid_ToGridDestructibleInfo var zero any; return zero } func GameRouter_SceneItemGrid_BuildTestBoxSpawns(ctx context.Context, session *GameSession, owner any, px float32, py float32, pz float32) []any { // Router implementation for GameRouter_SceneItemGrid_BuildTestBoxSpawns return nil } type SceneItemGridSessionState struct { SyncRoot any `json:"syncRoot"` Cts *context.CancelFunc `json:"cts"` Loop *any `json:"loop"` // 已经下发过的网格;只在刷新 / 心跳 / 跨格重声明时重发。 Sent []any `json:"sent"` LastCellX int32 `json:"lastCellX"` LastCellZ int32 `json:"lastCellZ"` HasCell bool `json:"hasCell"` LoadedSent bool `json:"loadedSent"` // 可破坏物总开关 SyncDestructibleSceneAOIActive(68418115) 本会话是否已推。 ActiveSwitchSent bool `json:"activeSwitchSent"` // 念动力测试盒本会话是否已摆过(只摆一次,见 SpawnTestBox)。 TestBoxSpawned bool `json:"testBoxSpawned"` // // ★★★ R93(自 donor v2.1「打气球」移植)—— 本会话「脚本自建、但不在客户端烘焙 // SceneItemBlob 里」的可破坏物保护表(气球的 12/16 颗球就属于这一类)。 // 病因(donor 真机实测):心跳 / 刷新每 5 s 重发一次 SyncDestructibleGridAOIDecrease // 声明 AOI 窗口;该报文 aoiInfo.addInfos 为空 + addGridInfo.ExceptIds 为空 ⇒ // 客户端语义是「这些格子改为从你自己的 SceneItemBlob 重新解析」⇒ 服务端合成的球 // (blob 里没有)每次心跳都被整片抹掉。 // 修法:把这类 id 记在这里,心跳时 ① 塞进 addGridInfo.ExceptIds(明确告诉客户端 // 「别删这些」),② 并把其 DestructibleInfo 重新塞回 aoiInfo.addInfos(保证还在)。 // ⛑ Info 是 object:登记方可能持有 BowlingWire 那份 DestructibleInfo(真机验证过 // 能建出 hold 的标注),由 ToGridDestructibleInfo 归一。 // 🆕 2026-09-30:本通道改**严格增量**后,这张表实际上**退化为空转** —— 因为「重推整窗 // ⇒ addInfos 空 ⇒ 客户端按 blob 重建 ⇒ 抹掉服务端合成物」这条链路的**前半段不再发生** // (已推过的格子根本不会再发)。气球本体本来就是 GameRouter.Balloon.cs 用 // SyncDestructibleObjectAddList(68963429) + 自带的 68969593 报文建的,不依赖这里。 // 表**保留不删**:一旦把 heartbeatMs / refreshMs / resendWindowOnMove 回滚成整窗重推, // 它立刻重新生效(否则气球会被抹掉,R93 的病会原样复发)。 // Protected map[uint64]any `json:"protected"` LastPushMs int64 `json:"lastPushMs"` ReadyAtMs int64 `json:"readyAtMs"` Pushes int32 `json:"pushes"` CellsSent int32 `json:"cellsSent"` }