TermiNexus
|
8964ea28cd
|
feat(client): M6 车库最小界面(浏览器只读),实测显示 15/28/515/992
browser 分支改为单页界面:连接区(URL 可改)→ 仓库物品(15 件,标注已挂载/未挂载) / 挂载位(28 槽位 + 515 设备,当前装备全 null) / 市场(992 件,只渲染前 20) / 日志区(最近 50 条,人类可读名)。状态更新用「状态容器 + 手动重渲染根节点」(未用 hooks)——全页只有一份全局会话状态。新增 GarageSession.kt(浏览器侧驱动,复用 AutoResponder 与 ClientState 的靶推导);M5 GarageDriver 的 node-only 逻辑(读抓包/日志/NodeRequire)没有拖进浏览器路径。验收:三构建成功(注意 dev 与 prod 系列要分开跑,否则 Gradle 报隐式依赖);浏览器实测已进空间 100073070、面板显示 15/28/515/992、日志含 G.q1wd/LoadObjectsData/ls.xbl/ba.lcr/devicesLoaded;服务端 session_069 佐证浏览器的上行被逐条收到;浏览器 Origin(http://127.0.0.1:8123) 被接受——服务端只记录不校验 Origin(index.js:484-489),当前无需改动。
|
2026-09-18 11:50:19 +08:00 |
|
TermiNexus
|
c0360810b7
|
feat(client): 协议层 M5 —— 车库因果闭环成立,ClientState 语义状态可 dump
闭环证据(自检 session_063,与服务端日志逐条对照):客户端 48 条上行换来服务端 5 条重放帧(s2c_0000..0004,按整帧字节数逐一匹配)+ 28 条 ba.lcr 的查库定向应答(与服务端 28 行「查库应答」吻合)。靶 id 从客户端语义状态推导、不是照抄抓包:G.q1wd 靶=空间 id;ls.xbl 靶=pt 里 ObjectId 为负的对象(与抓包 5/5 命中);ba.lcr 靶=pt 里带语义标记 modelId 2494628067134376039(自指 + modificationCount=7)与 7391892068905080676(devicesAvailable=true)的对象(与抓包 28/28 命中),且从 s2c_0001 推导出的 28 个挂载位与后到的 s2c_0004 里 28 条 devicesLoaded 记录头 ObjectId 28/28 一致(独立预测成功)。ClientState:itemsOnDepot 15 / itemsOnMarket 992 / mountedItems 5 / 挂载位 28 / 设备 515 / mountedDevice 全 null,全部与期望一致。修复自身缺陷:原先用「最新一帧 pt」推导 URL 会被 s2c_0004 的无标记版本覆盖导致靶集从 28 掉到 0,改为按帧各存对象→模型快照并扫描所有 pt 帧。无跳帧(仅 3 行正常的「重放等待」);三构建全绿;浏览器目标不回归。
|
2026-09-18 11:37:32 +08:00 |
|
TermiNexus
|
78ce1bacea
|
feat(client): 协议层 M4 —— 上行帧构造与自动应答,形成问答闭环
新增 Uplink.kt:c2s 空间帧构造 [nullmap][靶 objectId i64][methodId i64][负载];ba.lcr(19B)/ls.xbl(19B)/G.q1wd(23B) 的构造结果与抓包逐字节相同(4 条帧全字节比对通过)。新增 AutoResponder.kt:收 LoadDependencies(zt) 自动回 G.q1wd(X),X 原样回传;防重放用「(X, 来源帧序号) 只回一次」而非「X 只回一次」——X 是回调号会被复用(3DTank/tools/server/schema.sql 头注 8),按 X 去重会吞掉合法应答;另加单连接 64 次上限兜底。Connection 加 SendBaLcr/SendLsXbl/SendGQ1wd 手动触发与 LastObjectsDataObjectIds()。口径修正:G.q1wd 的靶是「客户端进入的空间 id」,不是 s2c 记录头上的 objectId(证据:s2c_0000 记录 objectId=390801,而同会话 c2s_0001 靶=100045260)。端到端(服务端 session_055 佐证):客户端自动发出的 c2s_0002.bin 23B 与服务端落盘逐字节一致;注:100045260 的 _loadObjectsData 是模板第 2 帧(进空间即排期 +455ms),不是本次 G.q1wd 因果换来的,且查库定向应答默认 Scope.GQ1wd=false,服务端记为 Disabled/G.q1wd=1,故因果闭环需在有会话回放的 100073070 验证。
|
2026-09-18 11:13:33 +08:00 |
|
TermiNexus
|
2a0296d0bb
|
feat(client): 协议层 M3 —— 压缩帧解压两端统一走 fflate.unzlibSync
真实抓包里的关键帧是 zlib 压缩的(space-100045260/s2c_0000.bin 整帧 915B → 解压后帧体 1799B;space-100073070/s2c_0004.bin 整帧 41590B → 解压后 255844B),M2 只在 Node 侧用 zlib 解、浏览器目标直接抛错。改为两端同一份纯 JS 实现 fflate 0.8.3,DecodeFrame 保持同步签名。关键修正:fflate 的 inflateSync 是「无包装 DEFLATE」,喂 zlib 流会报 unexpected EOF,zlib 包装要用 unzlibSync —— 官方客户端也正是用它(3DTank/src/module_24635.js:20 g = n.unzlibSync,来自 module_29518.js:1172),与我们同路。验收:Node 侧两个压缩帧的解压结果与 link.js 的 zlib.inflateSync SHA256 完全相同,且解压后完整解出记录(1 条 zt / 1 条 pt + 28 条 fields),M2 对照脚本仍 5 帧、一致叶子 26950 / 不一致 0;浏览器侧页面加载即自动跑自检、console 全项通过(已实际用浏览器打开确认);三个构建任务全绿;production 产物 524579 → 564689 字节(+40110,fflate 的 CJS 入口无法 tree-shake,后续可换 ESM 瘦身)。
|
2026-09-18 10:31:37 +08:00 |
|
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 |
|
TermiNexus
|
2deb628bf6
|
feat(client): 协议层 M1 —— 入口层握手 + 空间帧记录级解码 + Node 自检
在 client/ 里实现 3DTank 协议层(Kotlin/JS),并给同一工程加 nodejs() 目标做无头自检。自检跑通:Hash Request(571B) → Hash Response(启用链路加密=0)→ Auth/EnterSpace(100045260) → Open Space → 3 条初始化帧(LoadDependencies/zt、LoadObjectsData/pt、gn)→ 保活 ping。字节级核对:Hash Request 与真实抓包 server-logs/store-02/session_040/c2s_0000.bin 逐字节相同;EnterSpace 与 packets/plain/space-100045260/c2s_0000.bin 逐字节相同。M1 明确不做:容器三布局定界、短命令形态、压缩帧解压(遇到即报错,不静默)。协议表由 client/tools/ExportProtocolTables.mjs 从 3DTank/names/ 生成(下行 31 / 上行 7);本机拉不到 Kotlin 插件自带的 yarn,改用系统 npm,故 kotlin-js-store 由 yarn.lock 换成 package-lock.json。
|
2026-09-18 09:51:00 +08:00 |
|
TermiNexus
|
5644692c97
|
feat(client): Kotlin/JS + React 客户端脚手架(对齐游戏技术栈)
游戏本体是 Kotlin/JS + kotlin-react + kotlinx.serialization + coroutines,逆向客户端要用同一套栈重写,所以脚手架与它对齐:Kotlin 2.1.21 / kotlin-wrappers 2025.5.11-19.1.0(React 19.1.0)/ kotlinx-serialization-json 1.8.1 / kotlinx-coroutines-core 1.10.2 / kotlinx-html-js 0.12.0;Gradle 8.10.2 走腾讯镜像(services.gradle.org 与 raw.githubusercontent.com 本机不可达)。构建已验证:BUILD SUCCESSFUL,产物 build/dist/js/productionExecutable/TankIcClient.js(261KB)。注:kotlin-wrappers 自 2025 年起只发 React 19.x 的 wrapper,故 React 锁 19.1.0,与游戏的 18.3.1 不同。
|
2026-09-18 09:30:22 +08:00 |
|