TermiNexus
|
356647937e
|
feat: M9 枚举线形序数 enum#N 解析成成员名(category 显示 ARMOR/WEAPON/PAINT…)
数据:evidence.json 的 byModule.<模块>.enums = { 枚举短名: [{field, member, value}] },全局 348 类 / 2633 成员。查表键定为 (字段所属模块, 枚举短名, 序数):同模块内短名天然唯一;跨模块同名 11 个短名 / 9 个同名异义,键含模块号即可隔离。全表 102 条 enum<X> 引用的定义模块均由字段的 TypeCodec 唯一确定(无冲突、未解 0),导出后落盘 77 类 / 1343 成员(33879 字符,整表增量 33894)。两层分离(本任务核心约束):协议层原始值仍是 enum#N,一字不改,因此 CompareContainerDecode.mjs 的逐字段对照口径不变;EnumNames 只在 UI/日志/dump 里翻译,查不到返回 null 并由调用方标「未解」,不猜。category 实测(15 件仓库物品全部解出):ARMOR#1 / WEAPON#0 / PAINT#2 / BAZOOKA#13 / DRONE#8 / INVENTORY#3 / INVISIBLE#9,真实 5 件挂载物品类别互不相同 —— M8 结论不变。UI 与日志显示「成员名(enum#N)」。顺带修正 GarageDriver 接线:上一轮把 URL 硬编码成 9092 且日志 label 写死 store-02,改为环境变量 TANKIC_WS_URL(默认 9091)与 TANKIC_LOG_LABEL(默认 store-03),否则「配对表 ⇔ 服务端日志」会找错目录。验收:CompareContainerDecode 5 帧 / 一致叶子 26950 / 不一致 0(硬门槛);server selftest 逐字节一致 1108 / 不一致 9 / 解码中断 90;三构建全绿;浏览器实测 15/15 类别显示成员名。未解:枚举成员名未覆盖 list<enum<X>>/opt<enum<X>> 的元素级。
|
2026-09-18 12:59:45 +08:00 |
|
TermiNexus
|
4277e0ae08
|
feat: M8 查明物品类别来源并实现同类互斥(切武器时旧件自动卸下)
类别来源(完整链):挂载 reducer IB.yr9 用物品的 qr3_1 查类别表;qr3_1 = oB 构造第 4 参 = n.yr6_1.ici_1;yr6_1 来自 pt 里 model 类 Ij 那一项 = modelId 6117306895090150422(module_85196::Lo,字段名就叫 category)。交叉验证:真实 5 件 mountedItems 的类别 {enum#1,enum#0,enum#13,enum#2,enum#8} 互不相同 → 规则成立;并确认 M7 留下的第 6 件 920009631018 与 920009631039 同为 enum#1(都是底盘),正是 M7 缺口的体现。排查并排除:不是本地静态表;GarageSelectItem 的 Yy/Xy/ek/Wy 是另一套枚举(模型 ox/2114179554921095892 的 pr3_1),与挂载互斥无关。实现:schema 加 Items.Category + 索引;新增可重跑的 backfill_category.js(从 ObjectModels 回填,支持 --dry),992 件全部回填;store.SetMounted 改为「先卸同类、再置新件」且幂等,日志打印「同时卸下 N 件同类」;客户端 ClientState.MarkMounted 同步互斥(与 IB.yr9 同顺序),UI 每行显示类别。验收:连装两件 enum#1 → 库中只剩后者(日志「同时卸下 1 件同类」);浏览器 a11y 快照前后对比正确;回归 selftest 1108/9/90、CompareContainerDecode 5 帧 0 不一致、三构建全绿。未解:enum#N 只是线形序数,枚举成员名未解出,库里一律存原始令牌 enum#N。
|
2026-09-18 12:26:01 +08:00 |
|
TermiNexus
|
ab1a905d28
|
feat: M7 点装备即落库,重启服务端后仍回显(闭合「能切换但没持久化」)
客户端:Uplink 加 M_.fcn 构造(6 条抓包样本逐字节相同)、仓库每行加「装备」按钮、Equip() 入队 + 本地乐观标记(对齐真实客户端的 jU reducer)。服务端:store.js 加 LoadMountedFlags/SetMounted(UPDATE ... WHERE Mounted<>1,幂等)/OverrideMountedRecord;session.js 加可选 RecordOverride 钩子;index.js 进车库时刷新快照、收 M_.fcn 落库。回显只在库与模板有差异时覆盖并打日志(装备前帧仍与抓包逐字节一致 775441B;装备后 775449B,+8B = 多一个 i64)。验收:浏览器点装备 → 服务端落盘 c2s_0064.bin 与抓包 c2s_0141.bin 逐字节相同;MySQL Items.Mounted=1 共 6 行;重启 9092 后启动日志显示 Items.Mounted=1 共 6 件、重进车库 UI 6 个装备按钮置灰与库一致。回归:selftest 1108/9/90、CompareContainerDecode 5 帧 0 不一致、三构建全绿。未实现并如实记录:类别互斥(物品类别来源未解出,不硬猜)、挂载位设备 mountedDevice 未处理。
|
2026-09-18 12:01:30 +08:00 |
|
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
|
61b57a3d11
|
docs(3dtank): 架构逆向:模块依赖图、6 条运行链路、TOState↔reducer 映射
names/module-graph.json:126 模块的 Kind/职责/依赖/导出/锚点,533 条依赖边,入口 13431(出度 46,依赖链深 17 层)。docs/ArchitectureModules.md:依赖图摘要 + 40 个游戏模块逐条 + 依赖分层(由数据生成)。docs/ArchitectureRuntime.md:启动引导/网络层/Redux/UI 系统/资源与渲染/空间与对象模型 6 条链路,64 条关键函数([证据] 77 / [推断] 15)。names/tostate-map.json + docs/StateMap.md:TOState 实为 89 字段(非估计的 60),89/89 由「toString 字段→槽位」与「根 reducer v8 的实参顺序→ntb 形参→w8 字段顺序」两条独立链路交叉验证,唯一未映射的是只读的 launchParams。
|
2026-09-18 09:30:30 +08:00 |
|
TermiNexus
|
4eff6bb315
|
chore(3dtank): 模块职责表规范化并修正两处误标
dir.file 改写为规范表格(126 模块 + 2 个资源文件),补「分类」「需反混淆」两列;分类计数与 module-graph.json 的 Stats.ByKind 逐项对齐(游戏本体 40 / Kotlin生态库 21 / 第三方库 65)。修正两处被标反的职责:module_96480 实为 inline-style-prefixer 的 transition 前缀插件(原标 Underscore.js 1.13.6);module_94965 实为 Underscore.js 1.13.6(原标 ReactHTML 标签构建器),分类随之由 Kotlin生态库 改为 第三方库。
|
2026-09-18 09:30:29 +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 |
|
TermiNexus
|
812d3702a5
|
chore(3dtank): 清理工作区可重建产物,360MB -> 199MB
删除的都是能重新生成的:cache/(124MB,serve.js 代理缓存)、src_named/(18MB,apply_names.js 重建)、旧 server-logs(19MB,保留当前在用的 store-02)、tools/symbol_map.json(8.9MB,map_symbols.js 重建)、names 下的报告产物(flow.json / commands.json / commands_decrypted.json / decoded_frames.txt,各自工具可重跑)、运行产物 missing.txt / sendlog.json,以及根目录杂项 1 / curlcmd / i.html。
保留:packets/(真实抓包证据,不可再生)、src/(权威解包)、web/(跑本地客户端要用)、names/ 协议表(工具输入,注意 model_table.json 是 probe_containers.js 的输入,没删)。
|
2026-09-17 20:05:12 +08:00 |
|
TermiNexus
|
b6f2cb71ee
|
feat(3dtank): 协议逆向工程与自研服务器入库(工具链 + 协议表 + 空间回放 + MySQL)
工具链(3DTank/tools/):extract(模块提取)、mine_evidence/apply_names(命名证据与别名回填)、extract_codec_fields(字段表 + 全库槽位类型 + PrimClassWire/ResourceClasses)、mine_downlink(下行字段表)、extract_space_init/rebase_space_init/audit_space_init(空间模板与记录切分审计)、extract_space_session(把真实会话切成按上行触发的回放模板)、pair_flow(请求→应答配对)、probe_containers、ingest/decrypt_frames(抓包入库与解密)、serve/fetch_assets(本地站点镜像)、import_db(抓包对象仓库导入 MySQL)。
服务器(3DTank/tools/server/):link/entry/codec/container/dispatch/world/space/semantic(协议层与 lt/pt/zt 三个容器布局)、session(会话回放,顺序推进)、db + schema.sql(MySQL 连接层与对象仓库表结构)、store/responder(查库组帧与定向应答;接管范围默认只开 ba.lcr)。
同时修 pair_flow 的时间轴偏移:timeline_c2s 与 timeline_s2c 的 0 点相差 144~4453ms,改前配对整体偏约 2 秒(实测 space-100073070 偏 2084ms),改后 22.4% 的触发者被纠正。
src/、src_named/、packets/、3DTank/local/(含数据库口令)不进库,可用上述工具重新生成。
|
2026-09-17 20:00:41 +08:00 |
|
TermiNexus
|
90c377ad79
|
feat(3dtank): 本地镜像与 hornet 工具链迁到新版 bundle main.6469b966.js
旧 main.b7114b43.js 已被新构建替换(10,011,039 字节 / 1335 模块),本地镜像整体重切到新版并清掉旧产物;hornet 工具链按 tools/map_symbols.js 生成的新旧符号映射改名后重新打包(3dtank_bundle.js 与 unpacked/hornet_bundle.js 同步)。tools/symbol_map.json(8.9MB 映射产物)与切割解包目录不进库,可用 map_symbols.js / split.js 重新生成。
|
2026-09-17 19:59:40 +08:00 |
|
TermiNexus
|
fd32881c3d
|
chore(3dtank): 补 .gitignore,隔离依赖/凭据/抓包/反编译产物
无 .gitignore 时 3DTank/local/db.json(含 MySQL 明文口令)、node_modules、web/(70MB 资源)、packets/、src/ 都会被 git add 扫进来。
|
2026-09-17 19:59:19 +08:00 |
|
TermiNexus
|
de3dbbc097
|
fix(3dtank): dump_plain 静默丢帧:明文尾部被巧合密钥当成加密段
- Solve 只按第一个可用 Start 提前返回,实测 mine-01/session_001 的 59 条 c2s 全是明文整帧,
却被巧合的 K=31 判成「加密段只切出 1 帧」,只 dump 出 13 条(还多出一个解不开的假帧)
- 加两条护栏:① 剩余消息自身都是完整帧时不再尝试解密;② 多个候选取 Start 最大的那个
- 加覆盖度自检:解出的帧没覆盖到每条 WS 消息就在 summary 里显式报 ⚠
重跑 mine-01 入库:回放库 7 个目录 → 18 个,帧数 13621 全量为整帧、
33 个压缩帧全部解压成功(原先 session_001 的假帧已消失)
|
2026-09-16 17:35:55 +08:00 |
|
TermiNexus
|
8d2aa5566d
|
feat(3dtank): 解出容器「模式表」头部并约束内层扫描
- ContainerSchema:容器负载头部 = [i32 a][i32 b][i64 objId][i32 N][N × i64 modelId],
这 N 个 modelId 就是容器里出现的模型(登录批次/聊天批次/商店各自对得上)
- InnerScan 支持模式表:只认表里的模型且按表内顺序出现,内层「吃满边界」60% → 70%,假命中基本清掉
- 容器负载打印模式表模型名;README/注释补已核实结论与未闭合环节
|
2026-09-16 16:38:24 +08:00 |
|
TermiNexus
|
2e5e8bd7b0
|
feat(3dtank): 协议工具链入库:上行命令接入 fake server + 字段表/WireClasses
- wss_server:识别 c2s 空间命令([nullmap][objectId i64][methodId i64][负载])并逐字段解码
- 应答签名改用 S:<methodId>(pair_flow.js 同步),fake 模式能按具体上行命令触发对应下行回放
- extract_codec_fields:新增「协议核心类 → 线上基础类型」挖掘(WireClasses,xr/R → i64)
- lib_space_codec:按模型真名/核心类换算兜底(ji → i64、xr → i64),抽成上下行共享库
- decode_space_frames:记录容器(_t/lt)已核实结构与未闭合环节
- README:补「协议逆向与离线模拟」章节(帧格式/字段编码/命令表/fake 回放/容器现状)
实测:297 条上行记录 100% 完整解出;fake 模式按 G.q1wd、ls.xbl 等命令精确触发应答
|
2026-09-16 16:30:38 +08:00 |
|
TermiNexus
|
f1c9d903fa
|
Test Version
|
2026-07-28 01:16:15 +08:00 |
|