package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // Client economy tables (mall commodities, drops, gacha pools/contents/tier rules) imported from the // 4229938 config dump into paths.clientConfigs. // Unlike ClientConfigRepository these tables are optional: a missing or malformed file only disables // the matching feature (mall purchase / gacha draw) instead of refusing to start, because the rest of // the server must keep working when a dump is incomplete. // var ( EconomyConfigRepository_cacheLock sync.RWMutex EconomyConfigRepository_cached = false EconomyConfigRepository_entries []any ) // LoadEconomyConfigRepository loads catalog records from client config directory func LoadEconomyConfigRepository(configDir string) ([]any, error) { EconomyConfigRepository_cacheLock.RLock() if EconomyConfigRepository_cached { defer EconomyConfigRepository_cacheLock.RUnlock() return EconomyConfigRepository_entries, nil } EconomyConfigRepository_cacheLock.RUnlock() EconomyConfigRepository_cacheLock.Lock() defer EconomyConfigRepository_cacheLock.Unlock() if EconomyConfigRepository_cached { return EconomyConfigRepository_entries, nil } filePath := filepath.Join(configDir, "EconomyConfigRepository.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) } } EconomyConfigRepository_entries = result EconomyConfigRepository_cached = true return result, nil } // Forces the (optional) tables to load so the startup log reports what was imported. func EconomyConfigRepository_Warmup() { // Catalog helper EconomyConfigRepository_Warmup } func EconomyConfigRepository_TryMallCommodity(id uint32, commodity MallCommodity) bool { // Catalog helper EconomyConfigRepository_TryMallCommodity return false } func EconomyConfigRepository_TryDrop(id uint32, drop DropRow) bool { // Catalog helper EconomyConfigRepository_TryDrop return false } func EconomyConfigRepository_TryPool(id uint32, pool GachaPool) bool { // Catalog helper EconomyConfigRepository_TryPool return false } func EconomyConfigRepository_TryTierRule(id uint32, rule GachaTierRule) bool { // Catalog helper EconomyConfigRepository_TryTierRule return false } func EconomyConfigRepository_TryTradeItem(id uint32, item TradeItem) bool { // Catalog helper EconomyConfigRepository_TryTradeItem return false } func EconomyConfigRepository_PoolContents(poolId uint32) []GachaContent { // Catalog helper EconomyConfigRepository_PoolContents return nil } func EconomyConfigRepository_ReadRecords(path string, warnings []string) []any { // Catalog helper EconomyConfigRepository_ReadRecords return nil } // Dump records may carry fully qualified keys ("LT.ConfigGen.IFoo.Id"), so match on the leaf name. func EconomyConfigRepository_TryProperty(row any, name string, value any) bool { // Catalog helper EconomyConfigRepository_TryProperty return false } func EconomyConfigRepository_TryUInt32(row any, name string, result uint32) bool { // Catalog helper EconomyConfigRepository_TryUInt32 return false } func EconomyConfigRepository_TryDouble(row any, name string, result float64) bool { // Catalog helper EconomyConfigRepository_TryDouble return false } func EconomyConfigRepository_ReadBool(row any, name string) bool { // Catalog helper EconomyConfigRepository_ReadBool return false } func EconomyConfigRepository_ReadUInt32Array(row any, name string) []uint32 { // Catalog helper EconomyConfigRepository_ReadUInt32Array return nil } // Item1: [{ "id1": 36211010, "count": 10 }, ...] - every entry is granted. func EconomyConfigRepository_ReadItemList(row any, name string) []any { // Catalog helper EconomyConfigRepository_ReadItemList return nil } // Item2: [{ "id2": .., "min": .., "max": .. }] - one entry is chosen and rolled inside [min, max]. func EconomyConfigRepository_ReadItemRangeList(row any, name string) []any { // Catalog helper EconomyConfigRepository_ReadItemRangeList return nil } // Item3: [{ "id3": .., "count": 0.00075 }] - 'count' is a relative probability weight. func EconomyConfigRepository_ReadItemWeightList(row any, name string) []any { // Catalog helper EconomyConfigRepository_ReadItemWeightList return nil } // CostCount: [{ "drawCount": 1, "cost": 100 }, ...] func EconomyConfigRepository_ReadCostList(row any, name string) []any { // Catalog helper EconomyConfigRepository_ReadCostList return nil } // One mall shelf row. Price is the list price, DiscountPrice the current charge (0 = none). type MallCommodity struct { Id uint32 `json:"id"` MallId uint32 `json:"mallId"` Type uint32 `json:"type"` BindId uint32 `json:"bindId"` ConsumeItemId uint32 `json:"consumeItemId"` Price float64 `json:"price"` DiscountPrice float64 `json:"discountPrice"` DropId uint32 `json:"dropId"` LimitNum uint32 `json:"limitNum"` CommodityBindIds []uint32 `json:"commodityBindIds"` UnitPrice float64 `json:"unitPrice"` } // // One drop row. items are always granted, // picks one entry and a random count in [Min, Max], picks one entry by // relative weight (Item3 carries fractional weights, so they are doubles). // type DropRow struct { Id uint32 `json:"id"` Money float64 `json:"money"` BindingGold float64 `json:"bindingGold"` Guaranteed []any `json:"guaranteed"` Ranged []any `json:"ranged"` Weighted []any `json:"weighted"` IsEmpty bool `json:"isEmpty"` } type GachaPool struct { Id uint32 `json:"id"` IsDrop bool `json:"isDrop"` MoneyId uint32 `json:"moneyId"` CommodityId uint32 `json:"commodityId"` GrandTierRuleId uint32 `json:"grandTierRuleId"` Costs []any `json:"costs"` } type GachaContent struct { Id uint32 `json:"id"` PoolId uint32 `json:"poolId"` TierRarity uint32 `json:"tierRarity"` DropId uint32 `json:"dropId"` Weight float64 `json:"weight"` Quantity uint32 `json:"quantity"` Quality uint32 `json:"quality"` DuplicateReturnDropId uint32 `json:"duplicateReturnDropId"` DuplicateReturnCount uint32 `json:"duplicateReturnCount"` // SS-tier row: the UI plays the extra grand-prize video for these draws. IsGrandPrize bool `json:"isGrandPrize"` } // // Pity ramp for one tier rule. 1 pools ramp their grand-prize chance // from by every draw // once is reached and guarantee it at . // 0 pools instead pick a tier from the cumulative // table (scale 100000) with no pity at all. // type GachaTierRule struct { Id uint32 `json:"id"` RuleType uint32 `json:"ruleType"` PityThreshold uint32 `json:"pityThreshold"` RampStartDraw uint32 `json:"rampStartDraw"` BaseProbability uint32 `json:"baseProbability"` ProbabilityIncrement uint32 `json:"probabilityIncrement"` GrandPrizeProbability []uint32 `json:"grandPrizeProbability"` } // // One trading-post row. 0 hands out as an // ordinary backpack item, 1 unlocks the wardrobe piece , 3 is a // household collectible (also ) and 2 is a fashion suit that retail only // trades through the order book. // type TradeItem struct { Id uint32 `json:"id"` ItemType uint32 `json:"itemType"` ItemId uint32 `json:"itemId"` Tab uint32 `json:"tab"` FashionId uint32 `json:"fashionId"` FashionSuitId uint32 `json:"fashionSuitId"` MinPrice uint32 `json:"minPrice"` MaxPrice uint32 `json:"maxPrice"` BucketRanges []uint32 `json:"bucketRanges"` // Cheapest price the row is offered at - the floor of its authored bucket ranges. MarketPrice uint32 `json:"marketPrice"` } type EconomyConfigRepository_Data struct { Mall map[uint32]MallCommodity `json:"mall"` Drops map[uint32]DropRow `json:"drops"` Pools map[uint32]GachaPool `json:"pools"` TierRules map[uint32]GachaTierRule `json:"tierRules"` ContentsByPool map[uint32][]GachaContent `json:"contentsByPool"` TradeItems map[uint32]TradeItem `json:"tradeItems"` }