package models import ( "encoding/json" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} // 一件落地的物品。ConfigId 是配置行 id(9993xxxx),下发时才换成 ConsumableId。 type ExtractionShooterItem struct { ConfigId uint32 `json:"configId"` StackCount uint32 `json:"stackCount"` CellX int32 `json:"cellX"` CellY int32 `json:"cellY"` IsRotated bool `json:"isRotated"` IsLocked bool `json:"isLocked"` BindPid uint64 `json:"bindPid"` } type ExtractionShooterGrid struct { } // 一个 bag 的服务端镜像:格数 + 占位图 + 物品清单。 type ExtractionShooterBagState struct { BagId uint32 `json:"bagId"` Meta ExtractionShooterBagMeta `json:"meta"` Items []ExtractionShooterItem `json:"items"` Grid ExtractionGrid4229938 `json:"grid"` IsSlotGroup bool `json:"isSlotGroup"` } // 一个容器实例。实例 id 由客户端给出(8 字节),服务端只认这一份。 type ExtractionShooterContainerState struct { InstanceId uint64 `json:"instanceId"` CfgId uint32 `json:"cfgId"` SizeX int32 `json:"sizeX"` SizeY int32 `json:"sizeY"` Items []ExtractionShooterItem `json:"items"` Grid ExtractionGrid4229938 `json:"grid"` // pid → 该玩家搜索完成的服务器秒数。空 = 未搜索 = 客户端隐藏图标。 SearchFinish map[uint64]uint32 `json:"searchFinish"` // // 已经把 SearchFinish 翻成过去的 pid。 // ⛑️ 这一位是「能不能看见东西」的开关:搜索刚发起时 SearchFinish 落在未来 // (客户端据此播读条),只有等时间到了、把它翻回过去,物品才会真正显示。 // 捐赠端 v1.7 的实机日志(09-24 17:07:04 → 17:07:05 → 17:07:06)逐帧证明了这个顺序: // 推「未来」帧 → 隔 N 秒推「已揭晓」帧 → 玩家才取得到东西。 // Revealed []uint64 `json:"revealed"` // 整箱揭晓的基准延时:箱内最长的那件 SearchTime。 RevealDelaySeconds int32 `json:"revealDelaySeconds"` } // // 每条连接的搜打撤状态,存 session.Items(与 GameRouter.SceneItemGrid 同套路)。 // type ExtractionShooterSessionState struct { SyncRoot any `json:"syncRoot"` Bags map[uint32]ExtractionShooterBagState `json:"bags"` Containers map[uint64]ExtractionShooterContainerState `json:"containers"` UnlockedExpansions []uint32 `json:"unlockedExpansions"` UnlockedBags []uint32 `json:"unlockedBags"` TokenCount uint32 `json:"tokenCount"` Seeded bool `json:"seeded"` // // 本局开始的时间戳(秒)—— 结算帧 SyncRaidSettlement 里的 // EnterRaidTime / BattleTime 就取它。 // Seed 每次重跑都会刷新(换局),Reset 不清它, // 因为退图那一刀要用「退图时间 - 进图时间」算这一局打了多久。 // EnterRaidSeconds uint32 `json:"enterRaidSeconds"` } // // 搜打撤运行时:编目 → per-session 状态 → wire payload。 // 本轮覆盖:登录帧注入、进图、容器刷物与搜索、背包搬运 / 丢弃 / 锁定 / 排序 / 入仓、容器↔背包搬运。 // 不含武器实体化(轮盘 Tab 切枪)—— 那需要 CombatCatalogRepository 的实例 id // 与 SyncSpiritWeaponDetail 整表,是第二轮的事。 // 三条容易漏的: // 1. 下发 id 用 ConsumableId(ExtractionShooterCatalog4229938.WireIdFor), // 配置行 id 只在服务端内部记。 // 2. 搜索时间戳必须落在未来:客户端 wait 秒数被钳负为 0 ⇒ 过去时间戳 = wait(0) 空转, // 动画一帧不播。 // 3. 堆叠数量按服务端规则生成(StackCountForLocked)。写 1 会让客户端把角标 // 整个藏掉(showCountCtrl = StackCount <= 1),看起来像"子弹只有 1 发"。 // type ExtractionShooterRuntime4229938 struct { // ------------------------------------------------------------------ seeding // 本局开放的包。取自 GamePlayTypeConfig 的 StorageSet / EquipSlotSet。 DefaultBagIds []uint32 `json:"defaultBagIds"` // // 轮盘里能放武器的格数(CellX 0..N-1)。 // WheelWeaponCfg(99952004)SlotCapacity{initial:8, max:8} —— 8 个槽位里有 1 个是私有武器槽 // (客户端 GetCellIndexBySlotIndex 把它扣掉了)⇒ 真正能放轮盘武器的是 7 格。 // WheelSlotCells4229938 int32 `json:"wheelSlotCells4229938"` // // 轮盘物品要不要带 WeaponData 的运行时值。 // 面板改这个比改配置省事(不用改 json、不用重启),GM 面板那条 checkbox 走的就是这里的 setter。 // 没动过时 null ⇒ 回落到 world.content.extraction.wheelWeaponData,重启就恢复配置值。 // 关掉它专门用于回退验证:枪的 tooltip 会退化成普通物品描述,能一眼看出 tooltip 那条链路 // 到底走的是 ItemInfo.WeaponData 还是虚构出来的东西。 // S_wheelWeaponDataOverride *bool `json:"s_wheelWeaponDataOverride"` WheelWeaponDataEnabled bool `json:"wheelWeaponDataEnabled"` } const ( ExtractionShooterRuntime4229938_SessionKey string = "client4229938-extraction-shooter" ExtractionShooterRuntime4229938_WheelHomeOffset4229938 int32 = 2 ) // 轮盘里的一件武器:格子 + 线上物品 id + SceneitemConfig 模板 + 账号军械实例 id。 type WheelWeapon struct { CellX uint32 `json:"cellX"` WireItemId uint32 `json:"wireItemId"` SceneItemTemplateId uint32 `json:"sceneItemTemplateId"` InstanceId uint64 `json:"instanceId"` }