This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Ananta-ps/internal/gameserver/routers/game_router_scene_item_grid.go

166 lines
9.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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"`
}