package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // One RailLineConfig row (a physical light-rail / cruise line, and its direction). type RailLine4229938 struct { Id uint32 `json:"id"` LineName string `json:"lineName"` TrainTemplateId uint32 `json:"trainTemplateId"` PrefabPath string `json:"prefabPath"` BlobName string `json:"blobName"` RingLine bool `json:"ringLine"` IsTestPath bool `json:"isTestPath"` DepartureInterval int32 `json:"departureInterval"` MaxTrainAmount int32 `json:"maxTrainAmount"` TotalTravelDuration float64 `json:"totalTravelDuration"` DestinationArrivalTime float64 `json:"destinationArrivalTime"` AvgVelocity int32 `json:"avgVelocity"` FirstStopDumb bool `json:"firstStopDumb"` LastStopDumb bool `json:"lastStopDumb"` FirstTrainHour int32 `json:"firstTrainHour"` FirstTrainMinute int32 `json:"firstTrainMinute"` LastTrainHour int32 `json:"lastTrainHour"` LastTrainMinute int32 `json:"lastTrainMinute"` // // How many carriages the client should keep alive on this line. The timetable repeats every // TotalTravelDuration seconds and a train leaves every DepartureInterval, so the steady-state // fleet size is the ratio of the two - capped by the config's own MaxTrainAmount // (Line 1: 1123.088 / 90 = 12 -> 12, Line 3: 655.295 / 90 = 7 -> 7, Line 4: 608.478 / 90 = 6 -> 6). // EffectiveTrainCount int32 `json:"effectiveTrainCount"` } // // One RailStopConfig row (a scheduled call of one line). Note that the JSON field literally named // "LineName" holds the stop name (e.g. 白帆湾车库 for the depot stop), not the line name. // type RailStop4229938 struct { Id uint32 `json:"id"` LineId uint32 `json:"lineId"` StopName string `json:"stopName"` NextStopId uint32 `json:"nextStopId"` ArrivalTime float64 `json:"arrivalTime"` InsertTime float64 `json:"insertTime"` GateOpenSide uint32 `json:"gateOpenSide"` DefaultExitStop bool `json:"defaultExitStop"` StationId uint32 `json:"stationId"` } // One RailStationConfig row (a platform shared by one or more lines). type RailStation4229938 struct { Id uint32 `json:"id"` Name string `json:"name"` ChineseName string `json:"chineseName"` EnglishName string `json:"englishName"` X float64 `json:"x"` Y float64 `json:"y"` Z float64 `json:"z"` Lines []uint32 `json:"lines"` } // // A line with its stops already ordered along the travel direction, so the nth entry of // Stops is the nth station of RailLine4229938's own Stations array. // type RailRoute4229938 struct { Line RailLine4229938 `json:"line"` Stops []RailStop4229938 `json:"stops"` } // Where a train is right now, derived from ElapsedTime + the baked timetable. type RailTrainProgress struct { StopIndex int32 `json:"stopIndex"` NextStopIndex int32 `json:"nextStopIndex"` CurrentStop RailStop4229938 `json:"currentStop"` NextStop *RailStop4229938 `json:"nextStop"` CurrentStation *RailStation4229938 `json:"currentStation"` NextStation *RailStation4229938 `json:"nextStation"` SecondsToNext float64 `json:"secondsToNext"` MetresToNext float64 `json:"metresToNext"` AtStation bool `json:"atStation"` } // // Build-4229938 light-rail / cruise catalog, read from the Rail*Config.json files in the configured // client-data directory. Field names verified against ConfigDump_v3 and the Lua // Auto.WriteMetroClientInfo writer in RPCSerializeAuto.lua. // var ( RailCatalog4229938_cacheLock sync.RWMutex RailCatalog4229938_cached = false RailCatalog4229938_entries []any ) // LoadRailCatalog4229938 loads catalog records from client config directory func LoadRailCatalog4229938(configDir string) ([]any, error) { RailCatalog4229938_cacheLock.RLock() if RailCatalog4229938_cached { defer RailCatalog4229938_cacheLock.RUnlock() return RailCatalog4229938_entries, nil } RailCatalog4229938_cacheLock.RUnlock() RailCatalog4229938_cacheLock.Lock() defer RailCatalog4229938_cacheLock.Unlock() if RailCatalog4229938_cached { return RailCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "RailCatalog.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) } } RailCatalog4229938_entries = result RailCatalog4229938_cached = true return result, nil } // // RaidConfig.json 的 LightRailLineIds:这个 raid 的场景里**实际存在**的地铁/轻轨线路。 // 客户端只能为「已加载场景里烘焙了 MetroPath 的线」建出车厢(见 // GameRouter.Metro.cs 顶部说明:MetroClientInfo 只有 Id / LineId / ElapsedTime / IsFinalTrain, // 走行路径是客户端本地资源)。推了不属于当前场景的线,客户端找不到路径 ⇒ 车要么不出、 // 要么沿别的路径跑(表现为"车不在铁轨上")。所以推线要以这张表为准,不能全局固定。 // 全表只有 4 个 raid 配了这项(RaidConfig 共 212 行): // 23300888 新启 = 1–4;23300999 凌云/重霄 = 9–14 + 15 画舫; // 23301227 轻轨测试场景 = 5–8;23301271 香肠人镜像副本 = 1–4。 // 其余(以及 raid 未知 / 未进世界时)返回空数组,调用方自行兜底。 // func RailCatalog4229938_LinesForRaid(raidId uint32) []uint32 { // Catalog helper RailCatalog4229938_LinesForRaid return nil } func RailCatalog4229938_TryGetLine(id uint32, line RailLine4229938) bool { // Catalog helper RailCatalog4229938_TryGetLine return false } func RailCatalog4229938_TryGetStation(id uint32, station RailStation4229938) bool { // Catalog helper RailCatalog4229938_TryGetStation return false } func RailCatalog4229938_TryGetRoute(lineId uint32, route RailRoute4229938) bool { // Catalog helper RailCatalog4229938_TryGetRoute return false } // // Position of train of at // seconds into the day's service. Trains are evenly spaced by // DepartureInterval and wrap around TotalTravelDuration, which is what the stops' ArrivalTime // values are measured against (the last stop's arrival equals the total travel duration). // func RailCatalog4229938_ElapsedForTrain(line RailLine4229938, trainIndex int32, elapsedSeconds float64) float64 { // Catalog helper RailCatalog4229938_ElapsedForTrain return 0 } // Resolves "where is the train" from its ElapsedTime and the baked stop timetable. func RailCatalog4229938_ProgressAt(line RailLine4229938, elapsed float64) *RailTrainProgress { // Catalog helper RailCatalog4229938_ProgressAt return nil } func RailCatalog4229938_LoadRaidLines() map[uint32][]uint32 { // Catalog helper RailCatalog4229938_LoadRaidLines return nil } func RailCatalog4229938_LoadLines() map[uint32]RailLine4229938 { // Catalog helper RailCatalog4229938_LoadLines return nil } func RailCatalog4229938_LoadStations() map[uint32]RailStation4229938 { // Catalog helper RailCatalog4229938_LoadStations return nil } func RailCatalog4229938_LoadRoutes() map[uint32]RailRoute4229938 { // Catalog helper RailCatalog4229938_LoadRoutes return nil } func RailCatalog4229938_UIntArray(row any, name string) []uint32 { // Catalog helper RailCatalog4229938_UIntArray return nil } func RailCatalog4229938_ReadRecords(fileName string) []any { // Catalog helper RailCatalog4229938_ReadRecords return nil } func RailCatalog4229938_Str(row any, name string) string { // Catalog helper RailCatalog4229938_Str return "" } func RailCatalog4229938_UInt(row any, name string) uint32 { // Catalog helper RailCatalog4229938_UInt return 0 } func RailCatalog4229938_Int(row any, name string) int32 { // Catalog helper RailCatalog4229938_Int return 0 } func RailCatalog4229938_Double(row any, name string) float64 { // Catalog helper RailCatalog4229938_Double return 0 } func RailCatalog4229938_Bool(row any, name string) bool { // Catalog helper RailCatalog4229938_Bool return false } func RailCatalog4229938_Clock(row any, name string, fallbackHour int32, fallbackMinute int32) any { // Catalog helper RailCatalog4229938_Clock var zero any; return zero }