package common import ( "encoding/json" "fmt" ) // Scene metadata is exported from the installed Client's RuntimeFloor tables. // A complete group list allows us to exclude deleted/client-owned groups // without maintaining special cases for individual maps or group IDs. type sceneMetadata struct { ClientVersion string `json:"client_version"` Floors map[uint32]struct { MapTeleportEntryID uint32 `json:"map_teleport_entry_id"` DimensionID *uint32 `json:"dimension_id"` DimensionGroups map[uint32][]uint32 `json:"dimension_groups"` GroupProperties map[uint32]map[string]GroupPropertyRule `json:"group_properties"` SavedValues map[string]int32 `json:"saved_values"` SavedValueRules map[string]GroupPropertyRule `json:"saved_value_rules"` EntrySavedValues map[uint32]map[string]int32 `json:"entry_saved_values"` Props map[uint32][]ScenePropInfo `json:"props"` StartAnchor *AnchorItem `json:"start_anchor"` FloorDefaultAnchor *AnchorItem `json:"floor_default_anchor"` Monsters map[uint32]map[uint32]TeleportInfo `json:"monsters"` GroupCount int `json:"group_count"` Groups map[uint32]struct { LoadSide string `json:"load_side"` Deleted bool `json:"is_deleted"` } `json:"groups"` } `json:"floors"` } func applySceneMetadata(res *JsonConfig) { raw, err := ReadResourceFile("resources/SceneMetadata.json") if err != nil { return } var metadata sceneMetadata if err := json.Unmarshal(raw, &metadata); err != nil { LogInfo("[SCENE] Invalid SceneMetadata: %v", err) return } config, err := LoadRuntimeConfig() if err != nil || metadata.ClientVersion != config.ClientVersion { LogInfo("[SCENE] SceneMetadata Client revision does not match runtime config; not applied") return } for entryID, configs := range res.LevelOutputConfigs { for name, scene := range configs { var planeID, floorID uint32 if n, _ := fmt.Sscanf(name, "P%d_F%d", &planeID, &floorID); n != 2 { continue } floor, ok := metadata.Floors[floorID] if !ok { continue } if floor.GroupCount == 0 || len(floor.Groups) != floor.GroupCount { LogInfo("[SCENE] Incomplete group metadata for floor %d; not applied", floorID) continue } if target := floor.MapTeleportEntryID; target != 0 { entrance, exists := res.MapEntrances[target] canonical := fmt.Sprintf("P%d_F%d", planeID, floorID) if exists && entrance.PlaneID == planeID && entrance.FloorID == floorID && entrance.EntranceType == "Town" && res.LevelOutputConfigs[target][canonical] != nil { if res.MapTeleportEntrances == nil { res.MapTeleportEntrances = map[uint32]uint32{} } res.MapTeleportEntrances[floorID] = target res.MapDefaultEntranceMap[floorID] = target } else { LogInfo("[SCENE] Map teleport entrance %d is not a configured Town on floor %d; routing override not applied", target, floorID) } } if floor.StartAnchor != nil { res.SceneAnchors[entryID] = []AnchorItem{*floor.StartAnchor} } if floor.FloorDefaultAnchor != nil && floor.FloorDefaultAnchor.ID != 0 { if res.SceneDefaultAnchors == nil { res.SceneDefaultAnchors = map[uint32]AnchorItem{} } res.SceneDefaultAnchors[floorID] = *floor.FloorDefaultAnchor } if floor.GroupProperties != nil { scene.GroupProperties = floor.GroupProperties } if floor.DimensionID != nil { scene.DimensionID = *floor.DimensionID } var dimensionGroups map[uint32]bool if floor.DimensionGroups != nil { ids, exists := floor.DimensionGroups[scene.DimensionID] if !exists || len(ids) == 0 { LogInfo("[SCENE] Missing group membership for floor %d dimension %d; scene override not applied", floorID, scene.DimensionID) continue } dimensionGroups = make(map[uint32]bool, len(ids)) for _, id := range ids { dimensionGroups[id] = true } } if floor.SavedValues != nil { // RuntimeDimension exports the actual DefaultValue, not MaxValue. // This applies only to floors with a complete captured override. scene.SavedValues = floor.SavedValues } if overrides, exists := floor.EntrySavedValues[entryID]; exists { entrance, known := res.MapEntrances[entryID] valid := known && entrance.FloorID == floorID && entrance.PlaneID == planeID values := make(map[string]int32, len(floor.SavedValues)) for key, value := range floor.SavedValues { values[key] = value } for key, value := range overrides { rule, known := floor.SavedValueRules[key] if !known || value < rule.MinValue || value > rule.MaxValue { valid = false break } values[key] = value } if valid { // Never mutate the shared floor defaults: entrances sharing a // floor can intentionally represent different story states. scene.SavedValues = values } else { LogInfo("[SCENE] Invalid saved-value profile entry=%d floor=%d; override not applied", entryID, floorID) } } for groupID := range scene.Scenes { group, exists := floor.Groups[groupID] if !exists || group.Deleted || group.LoadSide == "Client" || (dimensionGroups != nil && !dimensionGroups[groupID]) { delete(scene.Scenes, groupID) LogInfo("[SCENE] Excluded group entry=%d floor=%d group=%d present=%t deleted=%t side=%s", entryID, floorID, groupID, exists, group.Deleted, group.LoadSide) continue } data := scene.Scenes[groupID] if floor.Props != nil { data.Props = floor.Props[groupID] } for index := range data.Monsters { monster := &data.Monsters[index] if placement, ok := floor.Monsters[groupID][monster.InstID]; ok { monster.Pos, monster.Rot = placement.Pos, placement.Rot } } scene.Scenes[groupID] = data } // Resource updates may introduce server props in previously empty groups. for groupID, props := range floor.Props { group, exists := floor.Groups[groupID] if !exists || group.Deleted || group.LoadSide == "Client" || (dimensionGroups != nil && !dimensionGroups[groupID]) { continue } data := scene.Scenes[groupID] data.Props = props scene.Scenes[groupID] = data } } } }