package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // 搜打撤物品。下发用的是 ConsumableId(8 位线上 id),不是配置行 id(9993xxxx)。 type ExtractionShooterItemMeta struct { Id uint32 `json:"id"` Name string `json:"name"` ConsumableId uint32 `json:"consumableId"` RealSceneItemModelId uint32 `json:"realSceneItemModelId"` Type int32 `json:"type"` VolumeX int32 `json:"volumeX"` VolumeY int32 `json:"volumeY"` MaxStackNum int32 `json:"maxStackNum"` SearchTime int32 `json:"searchTime"` Quality int32 `json:"quality"` BasicValue int32 `json:"basicValue"` ReadInfoType int32 `json:"readInfoType"` CanTakeOn bool `json:"canTakeOn"` Durability int32 `json:"durability"` IfSafetyBox bool `json:"ifSafetyBox"` IfDiscard bool `json:"ifDiscard"` } // 容器(箱子 / 抽屉 / 子容器)。只有洗过规则的行才有刷物能力。 type ExtractionShooterContainerMeta struct { Id uint32 `json:"id"` Name string `json:"name"` Type int32 `json:"type"` CapacityX int32 `json:"capacityX"` CapacityY int32 `json:"capacityY"` QuantityMin int32 `json:"quantityMin"` QuantityMax int32 `json:"quantityMax"` CertainItemList []uint32 `json:"certainItemList"` RandomItemGroups []any `json:"randomItemGroups"` } // 随机物品组。按 Weight 抽一件。 type ExtractionShooterGroupMeta struct { Id uint32 `json:"id"` Items []any `json:"items"` } // // 背包。Type 决定它走 Bags 还是 SlotGroups // (0 背包 / 1 安全箱 / 2 仓库 → Bags;4 轮盘 / 5 战备 / 6 扩容 → SlotGroups)。 // type ExtractionShooterBagMeta struct { Id uint32 `json:"id"` Name string `json:"name"` Type int32 `json:"type"` CapacityX int32 `json:"capacityX"` CapacityY int32 `json:"capacityY"` SlotInitial int32 `json:"slotInitial"` SlotMax int32 `json:"slotMax"` AllowedItemTypes []uint32 `json:"allowedItemTypes"` } // 扩容条目。Type=1 给 BelongBagId 加一行;Type=2 解锁 BagAddId 仓库。 type ExtractionShooterExpansionMeta struct { Id uint32 `json:"id"` Type int32 `json:"type"` BelongBagId uint32 `json:"belongBagId"` BagAddId uint32 `json:"bagAddId"` ExpandX int32 `json:"expandX"` ExpandY int32 `json:"expandY"` FrontExpansionIds []uint32 `json:"frontExpansionIds"` } type ExtractionShooterBirthPoint struct { Id uint32 `json:"id"` Name string `json:"name"` X float32 `json:"x"` Y float32 `json:"y"` Z float32 `json:"z"` Weight int32 `json:"weight"` EvacuationPoints []uint32 `json:"evacuationPoints"` } type ExtractionShooterEvacuationPoint struct { Id uint32 `json:"id"` Name string `json:"name"` X float32 `json:"x"` Y float32 `json:"y"` Z float32 `json:"z"` } // 图。BirthPointIds 指向 BirthPlaceConfig;表内没有 raid↔图外键。 type ExtractionShooterMapMeta struct { Id uint32 `json:"id"` Name string `json:"name"` GlobalCountTime int32 `json:"globalCountTime"` BirthPointIds []uint32 `json:"birthPointIds"` } // // 搜打撤配置表读取层。11 张表全部来自官方 ConfigDump(ClientData/4229938/Configs), // 与本服其它 catalog 一样用 Lazy 一次性解析,坏文件只记 LoadError 不抛。 // 两张表是派生性质的,不在这里: // - ExtractionMonsterCatalog.json —— 捐赠端从 AgentConfig/RandomEnemyConfig 派生的自建表, // 官方 dump 里没有(我们这轮的 S1 不做铺怪,见硬规则:点位在关卡静态刷怪点)。 // - DropConfig.ExtractionShooterItem —— 那是掉落表,不是容器刷物池,别拿它刷箱子。 // const ( // 轮盘 bag。物品进这里只是"存进格子",要让 Tab 能切还要走实体化(本轮不做)。 ExtractionShooterCatalog4229938_WheelBagId uint32 = 99952004 ExtractionShooterCatalog4229938_BackpackBagId uint32 = 99952001 ExtractionShooterCatalog4229938_SafeBoxBagId uint32 = 99952002 ExtractionShooterCatalog4229938_FirstStashBagId uint32 = 99952003 ) var ( ExtractionShooterCatalog4229938_cacheLock sync.RWMutex ExtractionShooterCatalog4229938_cached = false ExtractionShooterCatalog4229938_entries []any ) // LoadExtractionShooterCatalog4229938 loads catalog records from client config directory func LoadExtractionShooterCatalog4229938(configDir string) ([]any, error) { ExtractionShooterCatalog4229938_cacheLock.RLock() if ExtractionShooterCatalog4229938_cached { defer ExtractionShooterCatalog4229938_cacheLock.RUnlock() return ExtractionShooterCatalog4229938_entries, nil } ExtractionShooterCatalog4229938_cacheLock.RUnlock() ExtractionShooterCatalog4229938_cacheLock.Lock() defer ExtractionShooterCatalog4229938_cacheLock.Unlock() if ExtractionShooterCatalog4229938_cached { return ExtractionShooterCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "ExtractionShooterCatalog.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) } } ExtractionShooterCatalog4229938_entries = result ExtractionShooterCatalog4229938_cached = true return result, nil } // 换局 / 重开时调用,让同一批容器实例 id 刷出新内容。 func ExtractionShooterCatalog4229938_ReseedSessionSalt() { // Catalog helper ExtractionShooterCatalog4229938_ReseedSessionSalt } func ExtractionShooterCatalog4229938_Item(configId uint32) *ExtractionShooterItemMeta { // Catalog helper ExtractionShooterCatalog4229938_Item return nil } // // 配置行 id → 下发给客户端的物品 id。3 条测试行没有 ConsumableId,退回配置行 id // (客户端那三条本来就换不出去,退成 0 会让整件物品显示成空白)。 // func ExtractionShooterCatalog4229938_WireIdFor(configId uint32) uint32 { // Catalog helper ExtractionShooterCatalog4229938_WireIdFor return 0 } // // 线上物品 id(客户端发回来的那个)→ 配置行。是 WireIdFor 的反函数。 // 客户端回传的一律是 ConsumableId(8 位),服务端内部记的是配置行 id(9993xxxx), // 所以「客户端说它把哪件东西放进了轮盘」必须走这里翻译,不能拿去查 Item。 // 3 条测试行没有 ConsumableId ⇒ 退回配置行 id,与 WireIdFor 对称。 // func ExtractionShooterCatalog4229938_ItemByWire(wireId uint32) *ExtractionShooterItemMeta { // Catalog helper ExtractionShooterCatalog4229938_ItemByWire return nil } // 物品类型是否带「详情读条」(ItemTypeConfig.ReadInfoType == 1)—— 实测就是武器与医疗道具。 func ExtractionShooterCatalog4229938_CarriesInfo(meta ExtractionShooterItemMeta) bool { // Catalog helper ExtractionShooterCatalog4229938_CarriesInfo return false } // // 这件物品对应的军械武器实例 id(0 = 不是可实体化的武器)。 // 链路:线上物品 id → 物品表 RealSceneItemModelId → SceneitemConfig 模板 → // 账号军械实例 id(CombatCatalogRepository.AccountInstanceId)。 // 轮盘里的枪要靠它下发 WeaponDetail,牛奶 / 药剂在这一步返回 0 ⇒ 不实体化。 // func ExtractionShooterCatalog4229938_WeaponInstanceOf(wireId uint32) uint64 { // Catalog helper ExtractionShooterCatalog4229938_WeaponInstanceOf return 0 } // 该 bag 是否属于 SlotGroups 一侧。文档判据:Type ∈ {4,5,6} ⟺ SlotCapacity.max > 0。 func ExtractionShooterCatalog4229938_IsSlotGroup(bag ExtractionShooterBagMeta) bool { // Catalog helper ExtractionShooterCatalog4229938_IsSlotGroup return false } // // 按容器实例 id 确定性选一个「有刷物规则」的容器。 // 客户端搜容器只发 8 字节实例 id、不带 config id,所以选哪张表只能服务端自己定。 // 用的是捐赠端那套 hash:mix = (id ^ (id>>32)) * 0x9E3779B97F4A7C15。 // ⓿ **容量不加盐** —— 同一个箱子进图两次应该还是那个格子数。 // func ExtractionShooterCatalog4229938_PickContainer(instanceId uint64) *ExtractionShooterContainerMeta { // Catalog helper ExtractionShooterCatalog4229938_PickContainer return nil } // 随机物品组。 func ExtractionShooterCatalog4229938_Group(groupId uint32) *ExtractionShooterGroupMeta { // Catalog helper ExtractionShooterCatalog4229938_Group return nil } // // 玩家落点落在哪张图里。表里没有 raid↔图外键 ⇒ 只能用出生点的包围盒反查。 // func ExtractionShooterCatalog4229938_MapAt(x float32, z float32) *ExtractionShooterMapMeta { // Catalog helper ExtractionShooterCatalog4229938_MapAt return nil } func ExtractionShooterCatalog4229938_TryBirthPoint(id uint32, point ExtractionShooterBirthPoint) bool { // Catalog helper ExtractionShooterCatalog4229938_TryBirthPoint return false } func ExtractionShooterCatalog4229938_BasePath() string { // Catalog helper ExtractionShooterCatalog4229938_BasePath return "" } func ExtractionShooterCatalog4229938_Rows(tableName string) []any { // Catalog helper ExtractionShooterCatalog4229938_Rows return nil } // ⛑️⛑️ JsonElement.TryGetInt32 / TryGetUInt32 **不是**「取不到就返回 false」—— // 元素不是 Number 时它们直接**抛** InvalidOperationException // ("The requested operation requires an element of type 'Number', but the target element has type 'False'")。 // 实测:TryGetInt32(False) / TryGetInt32(True) / TryGetUInt32(False) 全抛,只有 ValueKind 先判才安全。 // 官方配置里布尔字段是**真 true/false**(不是 0/1):ItemTypeConfig.CanTakeOn 13 条里 5 条 false、 // ItemConfig.CanBringOut 314 条里 113 条 false ⇒ 不加守卫的表一读到就整块 catch 掉。 // 所以下面三个取值器一律先判 ValueKind 再 Try。 func ExtractionShooterCatalog4229938_U32(e any, name string) uint32 { // Catalog helper ExtractionShooterCatalog4229938_U32 return 0 } func ExtractionShooterCatalog4229938_I32(e any, name string) int32 { // Catalog helper ExtractionShooterCatalog4229938_I32 return 0 } func ExtractionShooterCatalog4229938_Bool(e any, name string) bool { // Catalog helper ExtractionShooterCatalog4229938_Bool return false } func ExtractionShooterCatalog4229938_Str(e any, name string) string { // Catalog helper ExtractionShooterCatalog4229938_Str return "" } func ExtractionShooterCatalog4229938_U32List(e any, name string) []uint32 { // Catalog helper ExtractionShooterCatalog4229938_U32List return nil } func ExtractionShooterCatalog4229938_Int2(e any, name string) any { // Catalog helper ExtractionShooterCatalog4229938_Int2 var zero any; return zero } func ExtractionShooterCatalog4229938_Int2Initial(e any, name string) any { // Catalog helper ExtractionShooterCatalog4229938_Int2Initial var zero any; return zero } // ---------------------------------------------------------------- load var ( Store_cacheLock sync.RWMutex Store_cached = false Store_entries []any ) // LoadStore loads catalog records from client config directory func LoadStore(configDir string) ([]any, error) { Store_cacheLock.RLock() if Store_cached { defer Store_cacheLock.RUnlock() return Store_entries, nil } Store_cacheLock.RUnlock() Store_cacheLock.Lock() defer Store_cacheLock.Unlock() if Store_cached { return Store_entries, nil } filePath := filepath.Join(configDir, "Store.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) } } Store_entries = result Store_cached = true return result, nil } func Store_NewSalt() { // Catalog helper Store_NewSalt } func Store_TryBounds(mapId uint32, box any) bool { // Catalog helper Store_TryBounds return false }