141 lines
4.9 KiB
Go
141 lines
4.9 KiB
Go
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_<uid>`) 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
|
|
}
|
|
|