147 lines
7.4 KiB
Go
147 lines
7.4 KiB
Go
package models
|
||
|
||
import (
|
||
"encoding/json"
|
||
"sync"
|
||
)
|
||
|
||
var _ = json.RawMessage{}
|
||
var _ = sync.Mutex{}
|
||
|
||
//
|
||
// AskLoadHouseData(63109556) 的返回体 —— 2026-09-30「自带家具」。
|
||
// <para>⛔ 先纠正一个编号坑:客户端有两套互不相通的 Auto.Reader 编号,
|
||
// 千万别混用:</para>
|
||
// <list type="bullet">
|
||
// <item>Lua 侧(S2C 推送):LuaGen/AutoGen/RPCDeserializeAuto.lua,定义
|
||
// Auto.Reader[N] + midToReader[mid] + midToName[mid](833 条)。
|
||
// AskLoadHouseData 不在这张表里(它是 C# 侧请求/应答链路)⇒
|
||
// 拿 Lua 的编号去指这条返回体是错的。</item>
|
||
// <item>C# 侧(请求/应答):在 Assembly-CSharp 里,本仓库读不到。</item>
|
||
// </list>
|
||
// <para>本 DTO 的字段顺序/编码不是靠编号推的,是逐字节验出来的(一次性探针
|
||
// %TEMP%\houseprobe,反射调 UxSerializer.Serialize 再和权威兜底表比):</para>
|
||
// <list type="bullet">
|
||
// <item>空 payload(OwnerPid = 0)与 DefaultReturnCatalog4229938 里
|
||
// case 63109556u 的 57 字节模板 MATCH ⇒ 字段顺序 + 三个计数编码全对。</item>
|
||
// <item>四套房的非空 payload 被按 Lua Auto.Reader[321]/[1010]/[390] 手写的解析器
|
||
// 完整吃掉(remain = 0),家具件数与模板一致。</item>
|
||
// </list>
|
||
// <para>下面这套 Lua 编号是亲自读过、可信的(用于对照字段顺序):</para>
|
||
// <code>
|
||
// Auto.Reader[321] = HouseInfo { HouseId, ParkingSpaceVehicleIdDict, FloorBuildInfoDict,
|
||
// CurPlacedFurnitureInstanceId, WallInfo, Configuration,
|
||
// FashionShowcaseDict, PlacementStatistics }
|
||
// Auto.Reader[1010] = IndoorBuildInfo { Root = ReadComplex(390) }
|
||
// Auto.Reader[390] = PlacedFurnitureInfo
|
||
// </code>
|
||
// <para>本返回体(HouseBuildingClientData,dump TypeDefIndex 33347,7 个 FieldIndex)逐字:</para>
|
||
// <code>
|
||
// OwnerPid u64
|
||
// FloorBuildInfoDict ReadDict7Bit(u32 → complex) ← ★ 家具树宿主;键 = 楼层号(1 基,见下)
|
||
// WallInfo complex HouseWallInfo
|
||
// FashionShowcaseDict ReadDict7Bit(u64 → complex)
|
||
// Configuration complex PlayerSingleHouseConfiguration
|
||
// FurnitureGadgetDataDict ReadDict7Bit(u64 → complex)
|
||
// DisabledFurniturePlacedIds ReadList7Bit(u64) (带 UXRPCParameterLengthLimit(1024))
|
||
// </code>
|
||
// <para>⚠️ FloorBuildInfoDict 的键是 楼层号,1 基,地面层 = 1,取值 1..Floors
|
||
// —— 不是 indoorId。取证链见 PrivateServerConfig.HouseFurnitureSettings.KeyMode
|
||
// 与 GameRouter.House.cs 的 ResolveFurnitureFloorKeys。</para>
|
||
// <para>⛔⛔ 三个 ReadDict7Bit 不能用 Auto.Reader[321](HouseInfo)那套 Int32 计数。
|
||
// Base.ReadDict7Bit(LX6/Service/RPCDeserializeBase.lua)是
|
||
// 「0xFF 标记 + 偏置 varint(线上写 count+1)」,而 Base.ReadDict 是
|
||
// 「0xFF + Int32 计数」。UxSerializer.WriteDictionary 的默认计数编码是
|
||
// Int32 ⇒ 这里每个 7bit 字段都必须显式写
|
||
// [UxCollection(Count = UxCountEncoding.Int7)],否则空表会写成
|
||
// FF 00 00 00 00(客户端读到 count=0 但多吞 3 个字节 ⇒ 整包错位)。
|
||
// 判据:权威兜底表 DefaultReturnCatalog4229938 里这条 57 字节的
|
||
// FloorBuildInfoDict 段就是 FF 01,不是 FF 00 00 00 00。</para>
|
||
// <para>⛔ Root.ChildrenDict 反过来是 Base.ReadDict
|
||
// (Auto.Reader[390] 里 ChildrenDict = Base.ReadDict(r:ReadUInt64, ReadComplex(390)))
|
||
// ⇒ 那里必须用 Int32 计数(空表 FF 00 00 00 00)。两个字段编码不一样,
|
||
// 别顺手统一。</para>
|
||
//
|
||
type HouseBuildingClientData4229938 struct {
|
||
OwnerPid uint64 `json:"ownerPid"`
|
||
FloorBuildInfoDict map[uint32]IndoorBuildInfo `json:"floorBuildInfoDict"`
|
||
WallInfo HouseWallInfo `json:"wallInfo"`
|
||
FashionShowcaseDict map[uint64]HouseFashionShowcaseData `json:"fashionShowcaseDict"`
|
||
Configuration PlayerSingleHouseConfiguration `json:"configuration"`
|
||
FurnitureGadgetDataDict map[uint64]HouseFurnitureGadgetData4229938 `json:"furnitureGadgetDataDict"`
|
||
DisabledFurniturePlacedIds []uint64 `json:"disabledFurniturePlacedIds"`
|
||
}
|
||
|
||
//
|
||
// HouseBuildingClientData.FurnitureGadgetDataDict 的 value 类型(FurnitureGadgetData)。
|
||
// <para>⛔ 下面这一族 Auto.Reader[N] 编号(1088 / 1073 / 932 / 1133 / 289 / 1089)
|
||
// 属于 C# 侧编号空间,本仓库读不到那张表 ⇒ 只当形状备忘,别拿它去 Lua 的
|
||
// RPCDeserializeAuto.lua 里查(那边的编号完全不同)。已亲自验证过的 Lua 编号只有
|
||
// [321] HouseInfo / [1010] IndoorBuildInfo / [390] PlacedFurnitureInfo。</para>
|
||
// <para>形状逐字抄自客户端 reader,但本轮永远不下发(FurnitureGadgetDataDict 恒为空字典)——
|
||
// 鱼缸 / 收藏品是「家具运行时数据」,不在「房子自带家具」范围内。
|
||
// 保留完整字段只是为了让这个类型将来真被填上时字节是对的,而不是个只会写 0xFF 的空壳。</para>
|
||
//
|
||
type HouseFurnitureGadgetData4229938 struct {
|
||
FishTank *HouseFishTankGadgetData4229938 `json:"fishTank"`
|
||
Collectible *HouseCollectibleGadgetData4229938 `json:"collectible"`
|
||
}
|
||
|
||
// Auto.Reader[1073]:{ SlotFish: Dict7Bit<u32, complex(1133)> }。
|
||
type HouseFishTankGadgetData4229938 struct {
|
||
SlotFish map[uint32]HouseFishInfo4229938 `json:"slotFish"`
|
||
}
|
||
|
||
// Auto.Reader[932]。
|
||
type HouseCollectibleGadgetData4229938 struct {
|
||
LootConfigId uint32 `json:"lootConfigId"`
|
||
ItemUniqueId uint64 `json:"itemUniqueId"`
|
||
}
|
||
|
||
// Auto.Reader[1133](HouseFishInfo)。
|
||
type HouseFishInfo4229938 struct {
|
||
UniqueId uint32 `json:"uniqueId"`
|
||
TemplateId uint32 `json:"templateId"`
|
||
Weight float32 `json:"weight"`
|
||
Length float32 `json:"length"`
|
||
PlacedInfo *HouseFishPlacedInfo4229938 `json:"placedInfo"`
|
||
}
|
||
|
||
// Auto.Reader[289]:{ Furniture: complex(1089) }。
|
||
type HouseFishPlacedInfo4229938 struct {
|
||
Furniture *HouseFishPlacedFurnitureInfo4229938 `json:"furniture"`
|
||
}
|
||
|
||
// Auto.Reader[1089]。
|
||
type HouseFishPlacedFurnitureInfo4229938 struct {
|
||
HouseId uint32 `json:"houseId"`
|
||
FurniturePlacedId uint64 `json:"furniturePlacedId"`
|
||
FurnitureSlotId uint32 `json:"furnitureSlotId"`
|
||
}
|
||
|
||
//
|
||
// 把 ClientData.Client4229938.HouseFurnitureTemplateNode4229938 模板树
|
||
// 翻译成客户端认识的 PlacedFurnitureInfo 树。
|
||
// <para>关键点:</para>
|
||
// <list type="bullet">
|
||
// <item>Position/Rotation 直接搬模板里的**绝对世界坐标**,不做任何变换
|
||
// —— 模板叶子本来就落在房子所在的真实坐标上(绿屿公馆 14001 → 约 (2254..2294, 160.72, 1394..1404))。</item>
|
||
// <item>PlacedInstanceId 必须**全局唯一且非 0**:客户端的
|
||
// gFurnitureUIDManager.uid2FurnitureGoDict 是**跨房子**的全局表,
|
||
// 四套房共用一套小 id 会让「先去 A 再去 B」互相踩。这里用
|
||
// houseId * 100000 + 模板节点号(节点号 ≤ 14150 < 100000 ⇒ 房内不撞;
|
||
// houseId ≥ 11200003 ⇒ 跨房不撞;最大值 ≈ 1.12e12 < 2^53 ⇒ Lua number 也能精确表示)。</item>
|
||
// <item>GadgetInstanceId 恒 0 —— 家具本体是客户端按 FurnitureId 自己实例化的,
|
||
// 机关(鱼缸/电视…)才需要 gadget,那是另一条链路。</item>
|
||
// <item>父子关系照抄模板树(绿屿公馆的模板有 9 个「桌上的小物件」是挂在桌子下面的),
|
||
// 客户端用 ChildrenDict 表达父子,没有 ParentPlacedInstanceId 字段。</item>
|
||
// </list>
|
||
//
|
||
type HouseFurnitureTree4229938 struct {
|
||
}
|
||
|
||
const (
|
||
HouseFurnitureTree4229938_PlacedIdStride uint64 = 100000
|
||
)
|
||
|