134 lines
6.5 KiB
Go
134 lines
6.5 KiB
Go
package routers
|
||
|
||
import (
|
||
"context"
|
||
"sync"
|
||
)
|
||
|
||
var _ = sync.Mutex{}
|
||
var _ = context.Background
|
||
|
||
//
|
||
// Interactive props: Gadget placement and DynamicGo activation.
|
||
// Two different systems, both previously untouched by this server:
|
||
// Gadget — the game's placed interactive prop (chairs, benches, arcade cabinets,
|
||
// computers, drill shelves). The client owns the prefabs; the server only says *which prefab and
|
||
// where* via SyncGadgetAdd(68843189) and the client loads and spawns it. We have the full prefab
|
||
// list (2935 pathIds) from the world dump, so this is the one channel where the server can put a
|
||
// brand-new interactive object into the world without any scene data of its own.
|
||
// DynamicGo — scene-authored props that the client already has (doors, shutters, kiosks).
|
||
// Their ids live inside the client's scene resources, which is why the server has never been able
|
||
// to enumerate them. The client tells us what it activated through AskActiveDynamicGo(67339530),
|
||
// so the reachable half is: accept the report, remember it, and echo it back as
|
||
// SyncDynamicGoChange(68106491) so the state survives a relog instead of resetting.
|
||
//
|
||
//
|
||
// Client invoke: AskActiveDynamicGo(67339530) — "I activated/deactivated prop id N".
|
||
// The wire payload is 6 bytes and reads as int32 + bool + byte, which pins
|
||
// DynamicGoChangeReason to a single byte even though the dump types it as an int enum. The
|
||
// two sightings in unknown-methods.log are both reason 5 (Shop) on the same id with
|
||
// active 0 then 1 — a shop purchase toggling a prop, not a player pressing a switch.
|
||
//
|
||
func GameRouter_Gadget_OnActiveDynamicGo(ctx context.Context, conn any, msg any) any {
|
||
// Router implementation for GameRouter_Gadget_OnActiveDynamicGo
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// Builds the mandatory part of GadgetEntityInfo and books the instance in
|
||
// WorldEntryState.SpawnedGadgets. Shared by the single and batch channels.
|
||
//
|
||
func GameRouter_Gadget_BuildGadgetEntityInfo(ctx context.Context, session *GameSession, state any, pathId int32, x float32, y float32, z float32, facing float32, uniqueId uint64, iScale int32, spoonParams []any, houseFurniture bool) any {
|
||
// Router implementation for GameRouter_Gadget_BuildGadgetEntityInfo
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// Per-prefab spoon parameters (PackedGadgetInfo.spoonSpecialList). Most props ship it
|
||
// null — a frida dump of 161 live instances found only the SpoonSlot doors and
|
||
// MiniGameMachine_Bee carrying anything (markId 31511937 = "True", which is what arms the
|
||
// mini-game). Explicit params win; otherwise we look the prefab up in
|
||
// world.content.gadgetSpoonParams.byPathId. Null came a full stop before this existed,
|
||
// so nothing else can regress by adding it.
|
||
//
|
||
func GameRouter_Gadget_BuildSpoonSpecialList(ctx context.Context, session *GameSession, pathId int32, explicitParams []any) []any {
|
||
// Router implementation for GameRouter_Gadget_BuildSpoonSpecialList
|
||
return nil
|
||
}
|
||
|
||
//
|
||
// Places a whole batch with SyncGadgetAddList(68925813) — one push, N entities. This is the
|
||
// channel AnantaGymLoader.exe drives by injection; it exists here so the server can try it
|
||
// without the loader (see GadgetSendChannel.AddList).
|
||
//
|
||
func GameRouter_Gadget_SpawnGadgetListAsync(ctx context.Context, session *GameSession, state any, specs []any, iScale int32, houseFurniture bool) any {
|
||
// Router implementation for GameRouter_Gadget_SpawnGadgetListAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// Places one Gadget prefab into the world in front of the player and returns its instance id.
|
||
// Only the mandatory part of GadgetEntityInfo is filled: instance id, transform, and the
|
||
// packed placement (pathId + same transform + a fresh uniqueId). Every authored sub-module —
|
||
// doors, drill shelves, lifts, occupants — is left null, which the client reads as "this prop
|
||
// has none". ForceLod0 keeps a freshly spawned prop from being culled to a billboard.
|
||
//
|
||
//
|
||
// Non-zero = reuse that instance id instead of minting one. Gym equipment passes the real blob
|
||
// uid captured from the client, so the entity the server places is the same instance the client
|
||
// would have built itself (its prefab lookup and any blob-side state key off that uid).
|
||
//
|
||
func GameRouter_Gadget_SpawnGadgetAsync(ctx context.Context, session *GameSession, state any, pathId int32, x float32, y float32, z float32, facing float32, channel any, uniqueId uint64, iScale int32) any {
|
||
// Router implementation for GameRouter_Gadget_SpawnGadgetAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
// Removes every Gadget this panel placed, or just one when an id is given.
|
||
//
|
||
// 只撤某一种物件(按 pathId)。**这是撤离探针残留的正确姿势** —— 全清会把健身房器材 /
|
||
// PropRestock / 摊位补给一起抹掉,而它们各自管线有自己的 Sent 记录,不一定会立刻补回来。
|
||
// (2026-10-03 随赛车移植:赛车门「过点即撤」要按 pathId 批量撤。)
|
||
//
|
||
//
|
||
// ★ 只撤这一批 id(比 更细)。
|
||
// 赛车门一局 52 个门**同一个 pathId**,按 pathId 撤会把还没跑到的一起撤掉,
|
||
// 所以「过点即撤」必须用它。
|
||
//
|
||
func GameRouter_Gadget_RemoveGadgetsAsync(ctx context.Context, session *GameSession, state any, onlyId any, pathIdFilter int32, onlyIds []any) any {
|
||
// Router implementation for GameRouter_Gadget_RemoveGadgetsAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// Which S2C channel places the prop. SyncGadgetAdd(68843189) is the direct create call and was
|
||
// proven to reach the client; SyncGadgetAOIAddAndRemove(68169646) is the world-streaming call
|
||
// the client uses for props that come and go with the AOI. Both are offered because which one
|
||
// the client honours for a prop that no grid ever streamed is still unknown.
|
||
//
|
||
type GadgetSendChannel int32
|
||
|
||
const (
|
||
GadgetSendChannel_Add GadgetSendChannel = 0
|
||
GadgetSendChannel_Aoi GadgetSendChannel = 1
|
||
GadgetSendChannel_Both GadgetSendChannel = 2
|
||
//
|
||
// SyncGadgetAddList(68925813) — the channel AnantaGymLoader.exe calls. Same payload as
|
||
// Add but batched in one push. Worth trying when Add builds the entity (name, position,
|
||
// interact point all correct) yet the prefab never appears: the two client handlers are
|
||
// different methods (0x12787ED0 vs 0x1276CF20) and may not share the resource path.
|
||
//
|
||
GadgetSendChannel_AddList GadgetSendChannel = 3
|
||
)
|
||
|
||
// One prop the caller wants placed. Used by the batch channel.
|
||
type GadgetSpawnSpec struct {
|
||
PathId int32 `json:"pathId"`
|
||
X float32 `json:"x"`
|
||
Y float32 `json:"y"`
|
||
Z float32 `json:"z"`
|
||
Yaw float32 `json:"yaw"`
|
||
UniqueId uint64 `json:"uniqueId"`
|
||
Name string `json:"name"`
|
||
}
|
||
|