package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // One piece of gym equipment the server places itself: treadmill, bench press, squat rack, // sit-up board. Read from Configs/GymEquipment.json. // // // Why this file exists at all — the four prefabs are NOT in GadgetCatalog.json (2935 pathIds) // and NOT in SceneitemConfig.json, so the server has never known they exist. Their true home is // the client's own LX6.Item.GadgetBlob (uid -> pathId + placement), which only gets // populated after OnEnterScene("WorldMap_Release") + ReadHeadInfo + // InitSimpleGrid + OnAoiAddBlobCfgByGridIndex — a pipeline AnantaGymLoader.exe // drives by frida injection because our grid-AOI push (68956797) alone leaves it idle. // pathIds (build 4229938) — ⛔ the loader's agent source hard-codes a DIFFERENT set // (79302023/79302022/79302021/79000586) that matches nothing in the live blob; do not use them. // The values below were verified 2026-09-23 by injecting the client, letting the loader spawn the // props, and pairing each instance name (`wodegym_treadmill_`) with its blob record by uid — // 19/19 instances matched, pathId -> prefab is therefore certain: // 1371296286 = wodegym_treadmill 跑步机 // 1443522735 = wodegym_benchpress 卧推架 // 434521152 = wodegym_squatnew 深蹲架 // 344334257 = wodegym_situp 仰卧板 // 684457731 = Bench_chairs_p_map01_indoor_prop_gym_dengzi01a 健身房凳子 // The minigame itself (squat / bench / run) is client-local lua: the server only has to make the // prop exist, then the F key does the rest. Capturing coordinates is what _gym_dump.py/_gym_final.py // are for (they read the blob directly — no need to run the loader at all). // type GymEquipmentItem4229938 struct { PathId int32 `json:"pathId"` Name string `json:"name"` X float32 `json:"x"` Y float32 `json:"y"` Z float32 `json:"z"` Yaw float32 `json:"yaw"` UniqueId uint64 `json:"uniqueId"` } // One gym venue: an anchor, a trigger radius, and the equipment inside it. type GymEquipmentSpot4229938 struct { Name string `json:"name"` Raid uint32 `json:"raid"` X float32 `json:"x"` Z float32 `json:"z"` Radius float32 `json:"radius"` Items []GymEquipmentItem4229938 `json:"items"` } // // Reads Configs/GymEquipment.json. Tiny file, parsed once behind a Lazy. // A missing or malformed file is not fatal: the catalog goes empty and // LoadError explains why, so the panel can say so instead of throwing on load. // var ( GymEquipmentCatalog4229938_cacheLock sync.RWMutex GymEquipmentCatalog4229938_cached = false GymEquipmentCatalog4229938_entries []any ) // LoadGymEquipmentCatalog4229938 loads catalog records from client config directory func LoadGymEquipmentCatalog4229938(configDir string) ([]any, error) { GymEquipmentCatalog4229938_cacheLock.RLock() if GymEquipmentCatalog4229938_cached { defer GymEquipmentCatalog4229938_cacheLock.RUnlock() return GymEquipmentCatalog4229938_entries, nil } GymEquipmentCatalog4229938_cacheLock.RUnlock() GymEquipmentCatalog4229938_cacheLock.Lock() defer GymEquipmentCatalog4229938_cacheLock.Unlock() if GymEquipmentCatalog4229938_cached { return GymEquipmentCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "GymEquipmentCatalog.json.json") data, err := os.ReadFile(filePath) if err != nil { return nil, err } var raw struct { Records []json.RawMessage `json:"records"` } if err := json.Unmarshal(data, &raw); err != nil { return nil, err } var result []any for _, item := range raw.Records { var m map[string]any if err := json.Unmarshal(item, &m); err == nil { result = append(result, m) } } GymEquipmentCatalog4229938_entries = result GymEquipmentCatalog4229938_cached = true return result, nil } // Spots whose anchor is within <radius> metres of (x, z), nearest first. func GymEquipmentCatalog4229938_WithinRadius(x float32, z float32, radius float32) []GymEquipmentSpot4229938 { // Catalog helper GymEquipmentCatalog4229938_WithinRadius return nil } func GymEquipmentCatalog4229938_Dist2(spot GymEquipmentSpot4229938, x float32, z float32) float32 { // Catalog helper GymEquipmentCatalog4229938_Dist2 return 0 } // // u64 that the capture script emits as a JSON string (no double round-trip). System.Text.Json // throws InvalidOperationException ("requires an element of type 'Number'") if you call // TryGetUInt64 on a string element, so accept both shapes — one bad uid must not kill the file. // func GymEquipmentCatalog4229938_U64(source any, name string) uint64 { // Catalog helper GymEquipmentCatalog4229938_U64 return 0 } func GymEquipmentCatalog4229938_Str(source any, name string) string { // Catalog helper GymEquipmentCatalog4229938_Str return "" } func GymEquipmentCatalog4229938_Floats(source any, name string, take int32) []float32 { // Catalog helper GymEquipmentCatalog4229938_Floats return nil }