Files
TankIC/client/src/jsMain/kotlin/Protocol/ModelCodec.kt
T
TermiNexus 830d6408b8 feat(client): 协议层 M2 —— 容器三布局 lt/pt/zt 落定,多记录帧不再丢记录
客户端实现三种容器布局的读,与 JS 实现(3DTank/tools/server/container.js)逐字段对照:5/5 帧、一致叶子 26950 / 不一致 0,两份规范 JSON SHA256 完全相同(各 795139 B)。覆盖 space-100073070/s2c_0004.bin 的 pt(7210 项,含 515 个 Id=0 字面量与 6695 个模型展开) 与其后 28 条 fields 记录——M1 会把这 28 条全丢掉。协议表由 codec_fields.json 的 1143497 B 裁到 ProtocolTables.json 的 124270 B(削 89.1%),做法:只留解码必需属性 + 闭包裁剪(根=所有带 ModelId 的 codec ∪ 命令/容器方法,沿 TypeCodec 与模型真名取闭包,保留 559/643 个 codec),闭包外如实报「无 codec」。计划外但必需:space-100045260 的 s2c_0000/s2c_0004 是 zlib 压缩帧,M1 直接抛错,现用 Node zlib 解压(browser 侧仍显式抛错);并新增正式产物 client/tools/CompareContainerDecode.mjs。
2026-09-18 10:11:10 +08:00

42 lines
2.4 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ModelCodec.kt —— 模型 codec 的字段展开(口径逐条对照 3DTank/tools/lib_space_codec.js 的
// ReadValue 尾部分支与 ReadFields)
//
// 什么时候会走到这里:容器 pt 的每一项(container.js 的 ReadLtItem:id 是 modelId →
// 就地展开该模型的字段),以及普通字段里类型是「模型类」的字段(字段自带 TypeCodec)。
//
// 解析顺序(必须与 lib_space_codec.js 一致,否则同一个字节流会解出不同的边界):
// ① 字段的 TypeCodec 命中导出的 codec 表 → 用该 codec
// ② 类型名命中「模型真名 → codec」(CodecByModelName)→ 用该 codec
// ③ 命中「协议核心类 / 资源类 → 线上基础类型」直译(FieldReader 里做)
// 三条都不命中 → FieldReader 如实报「无 codec」,绝不用长度或字节数去猜。
package Protocol
internal object ModelCodec {
// 命中 ①② 就返回 codec 键;都不命中返回 null(③ 与报错交给 FieldReader)
fun ResolveCodec(Type: String, TypeCodec: String): String? {
if (TypeCodec.isNotEmpty() && ProtocolTables.Codec(TypeCodec) != null) return TypeCodec
return ProtocolTables.CodecByModelName(Type)
}
// 按 codec 键就地展开字段(无长度前缀)。字段名口径同 lib_space_codec.js:306:
// 匿名字段(表里没有名字)用 `(匿名#序)` 区分,否则同名会互相覆盖。
// 中途解不动:返回已解出的部分 + Bad + 带字段名/类型/相对偏移的原因串(调用方据此停下并如实标注)。
fun ReadFields(Buf: ByteArray, Off: Int, CodecKey: String, Depth: Int, Ctx: ReadCtx?): FieldReadResult {
val Fields = ProtocolTables.Codec(CodecKey)
?: return FieldReadResult(null, Off, true, "codec 键 $CodecKey 不在导出表里(闭包裁剪掉了,不猜)")
val Obj = LinkedHashMap<String, Any?>()
var At = Off
for (F in Fields) {
val R = FieldReader.ReadValue(Buf, At, F.Type, F.TypeCodec, Depth, Ctx)
Obj[F.Name] = R.Value
if (R.Bad) {
val Why = "字段 ${F.Name}(${F.Type})在 +${At - Off} 处解不动" + (if (R.Why.isNotEmpty()) ":${R.Why}" else "")
return FieldReadResult(Obj, At, true, Why)
}
At = R.Next
}
return FieldReadResult(Obj, At, false)
}
}