357 lines
12 KiB
Go
357 lines
12 KiB
Go
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
|
||
}
|
||
|