diff --git a/3DTank/docs/KtRebuild_Plan.md b/3DTank/docs/KtRebuild_Plan.md new file mode 100644 index 0000000..3fa189d --- /dev/null +++ b/3DTank/docs/KtRebuild_Plan.md @@ -0,0 +1,261 @@ +# 3DTank Kotlin 重构计划(KtRebuild Plan) + +> **目标**:用 Kotlin 复刻游戏本体,最终产出**职责等价、可启动**的 JS 客户端。 +> **基线**:`main.7cfa4058.js`(10,080,299 字节)/ `client_version = e98d02161a563e043eabb96c6e4db5fedcf165e7` +> **文档定位**:计划表 —— 有哪些工具、怎么用、按什么顺序做、怎么验收。 + +--- + +## 0. 先纠正三个前提 + +1. **`dir.file` 不是当前版本的职责表。** + 它是旧版 126 模块的职责划分;当前版本的模块 id、字节数、内部字段都有漂移。 + 但**职责划分大体一致**,仍可作为"要做哪些子系统"的规划依据;正式开工前必须用当前包重建一份职责表(见 S0)。 + +2. **`e98d0216` 不是文件名。** + `e98d0216…` 是 `client_version`(git release hash,写死在 `module_61860.js`): + ```js + i.g3("client_version", "e98d02161a563e043eabb96c6e4db5fedcf165e7"); + ``` + 而 `main.7cfa4058.js` 的 `7cfa4058` 是 webpack **contenthash**。两者无对应关系,不能互相推导文件名。 + +3. **真正要复刻的只有 40 个模块。** + `dir.file` 的分类计数:**游戏本体 40 / Kotlin 生态库 21 / 第三方库 65**(另有 index.json、resources.json)。 + 后 86 个是已知开源依赖,直接引上游即可,不需要反编译重写。 + +--- + +## 1. 验收标准("可启动 JS"的定义) + +| 级别 | 判定 | 手段 | +|---|---|---| +| **L1 可加载** | 126 模块 + 资源全部注册成功,无 404 / 无语法错误 | `tools/test_loader.js` 全绿 | +| **L2 可进战局** | `serve.js` 起站 → 登录 → 车库 → 进战斗 → 控制台无未捕获异常 | 人工 + 控制台 | +| **L3 行为等价** | 同场景关键状态(位置/血量/状态机/事件序列)与原包对比一致 | 双跑 golden trace | +| **L4 工具兼容** | HORNET 全部开关可用(雷达/透视/后视镜/穿坦克/拒绝击退…) | `build_bundle.py` 产物实测 | + +> L1/L2 是最低门槛;L3 才是"职责等价"的真正证据。 + +--- + +## 2. 模块分层(依据 `dir.file`) + +| 分类 | 数量 | 处理方式 | 是否重写 | +|---|---|---|---| +| 游戏本体 | 40 | 反混淆 → Kotlin 复刻 | ✅ | +| Kotlin 生态库 | 21 | 换官方依赖(stdlib / serialization / coroutines / atomicfu / kvbuffer…) | ❌ | +| 第三方库 | 65 | 换 npm 依赖(React 18.3.1 / Redux / Sentry 5.30.0 / qrcode / Underscore 1.13.6 / Emotion / fflate…) | ❌ | +| 资源/索引 | 2 | index.json、resources.json 原样托管 | ❌ | + +**游戏本体的体积分布(决定推进顺序)**: + +| 模块 | 大小 | 职责 | 阶段 | +|---|---|---|---| +| 82347 | 5.50 MB | 大厅/车库/商店/部落/结算 UI | S6(最后) | +| 87072 | 3.78 MB | 战斗系统:底盘物理、模式、物品池、特效、空投、地图渲染 | S5 | +| 10032 | 1.54 MB | 核心 Redux 状态 | S3 | +| 85183 | 1.18 MB | 全量协议 Codec 注册 | S2 | +| 92980 | 0.83 MB | WebGL 渲染核心 | S4 | +| 其余 35 个 | < 5 MB | 协议/工具/音频/输入/资源/ECS/物理… | S2–S5 | + +前 5 个约占 12.8 MB,是工作量的绝对主体。 + +--- + +## 3. 工具清单与用法 + +### 3.1 基线与拆分 + +| 工具 | 作用 | 用法 | +|---|---|---| +| `tools/split.js` | 把 `main..js` 切成 126 个模块 + `resources.json`,并注入 bundle 加载 | `node tools/split.js [bundle] [输出目录] --clean`(不传参自动取根目录最新 `main.*.js`) | +| `3DTank/tools/extract.js` | bundle → prettier 美化源码(`3DTank/src/`) | `cd 3DTank/tools && node extract.js [bundle] [输出目录]` | +| `tools/pack_unpacked.js` | 把 `unpacked/` 打成 `unpacked.pack.json`(一次请求加载全部模块) | `node tools/pack_unpacked.js` | +| `tools/test_loader.js` | 模拟浏览器加载流程,验证切割产物完整 | `node tools/test_loader.js` | + +### 3.2 命名还原 + +| 工具 | 作用 | 用法 | +|---|---|---| +| `3DTank/tools/mine_evidence.js` | 从源码挖命名证据(组件名/类名/枚举/闭包原名/导出表/字符串) | `cd 3DTank/tools && node mine_evidence.js` | +| `3DTank/tools/apply_names.js` | 把证据回填成 `src_named/`(三道校验) | `node apply_names.js --aliases` | +| `tools/map_symbols.js` | 新旧 bundle 符号映射(K 元块锚点 + 投票) | `node tools/map_symbols.js <旧unpacked> <新bundle> all tools/symbol_map.json` | +| `tools/apply_symbol_map.js` | 按映射批量改名(五道安全规则 + 双向校验) | 见文件头注释;默认读 `3DTank/tmp_symbol_map.json` | +| `3DTank/names/module-graph.json` | 模块依赖图(`DependsOn` / `Exports` / `Anchors` / `Kind`) | 数据文件,作为 Kotlin 模块切分依据 | + +### 3.3 字段抽象(当前版本适配的主入口) + +| 工具 | 作用 | 用法 | +|---|---|---| +| `tools/hornet_schema.js` | 匿名字段 → 语义字段总表(88 项)+ 导出键表 | 控制台 `__hornetSchema__.SelfCheck()` 一次列出失配项 | +| `build_bundle.py` | 把 `tools/hornet_*.js` 拼成 `3dtank_bundle.js` 并同步到 `unpacked/` | `python build_bundle.py` | + +换版本时**只改 schema**,工具代码不动;这是本计划里"漂移成本最低"的一环。 + +### 3.4 本地运行与协议取证 + +| 工具 | 作用 | 用法 | +|---|---|---| +| `3DTank/tools/serve.js` | 本地静态服务器 + 反向代理(resources / config / balancer)+ 中继 config 改写 | `node serve.js --port 8080 [--relay-port 9091] [--verbose]` | +| `3DTank/tools/wss_server.js` | 本地游戏通信服务器(中继 + 抓包 + 实时解密) | `node wss_server.js --port 9091 --upstream wss://c1.3dtank.com:9090/` | +| `3DTank/tools/analyze_packets.js` | 抓包产物分析 | 见文件头 | +| `3DTank/tools/prefetch_assets.js` | 按 `resources.json` 预拉资源到本地镜像 | `node prefetch_assets.js` | +| `3DTank/tools/server/` | 协议服务端骨架(codec / dispatch / session / space / world…) | 供 S3 协议层对照 | + +### 3.5 HORNET 探针(现成的行为断言) + +`tools/hornet_lib.js` / `hornet_radar.js` / `hornet_3d_wireframe.js` / `hornet_noclip.js` / `hornet_noknockback.js` 等, +直接读引擎状态(位置、血量、队伍、物理形状)。S5 之后可用它们做"新包是否语义一致"的活体探针。 + +--- + +## 4. 阶段计划 + +### S0 基线重建(必须先做) +- **目标**:产出**当前版本**的职责表与依赖图,替换 `dir.file` 的旧数据。 +- **做法**: + 1. `node tools/split.js --clean` + 2. `node 3DTank/tools/extract.js ../main.7cfa4058.js ../src` + 3. `node mine_evidence.js`(更新 `evidence.json`) + 4. 以 `module-graph.json` 的 `DependsOn/Exports/Anchors/Kind` 重新分类 126 模块 +- **输出**:`3DTank/names/module-graph.json` + 新版职责表(按当前模块 id) +- **验收**:`node tools/test_loader.js` 全绿;分类计数与旧表基本一致(40/21/65 量级) + +### S1 依赖还原(86 个库) +- **目标**:把库模块映射为上游依赖,产出 `deps.lock`。 +- **做法**:用 `Anchors`(版本字符串、API 名)反推包名与版本;导出表对照 API 面。 +- **已知**:React/React-DOM 18.3.1、Sentry 5.30.0、Underscore 1.13.6。 +- **待挖**:Kotlin 编译器版本、kotlinx-coroutines / serialization / atomicfu 版本、kotlin-wrappers、Emotion。 +- **验收**:每个库模块都能指到"包名 + 版本 + 用到的导出符号"。 + +### S2 协议层(14 个 codec 模块) +- 模块:85183 / 47243 / 85196 / 31908 / 16100 / 10283 / 25377 / 54199 / 37504 / 88693 / 89803 / 85667 / 67222 / 99527 +- **目标**:Kotlin 数据类 + `@Serializable`,按 **命令 id + 字段序号** 对齐。 +- **验证**:用 `wss_server.js` 抓到的帧做 golden 编解码对比(现有 `names/*_table.json`、`codec_fields.json` 可直接用)。 +- **注意**:字段顺序/默认值错一位整包不可用,必须逐帧验。 + +### S3 状态与胶水 +- 模块:10032(Redux 核心)、18334(DI 装配)、61860(连接与对象模型)、13431(启动)、52943(工具库)、26482(SW 注册) +- **目标**:新工程能完成"启动 → 连接 → 进入大厅"。 +- **验收**:L2 的一半(登录 + 大厅)。 + +### S4 引擎核心 +- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入) +- **目标**:场景可渲染、物理可步进。 +- **做法**:先纯函数层(矩阵/几何/物理步进),再渲染管线;用同输入同输出做单元比对。 +- **验收**:车库 3D 预览可显示;进战斗不崩。 + +### S5 战斗系统(87072,3.78 MB) +- 按子系统拆文件:底盘/悬挂、物理、战斗模式、物品池、空投、特效、地图渲染。 +- **验收**:L2 完整(可进战斗并驾驶/开火),HORNET 探针读数与原包一致。 + +### S6 UI(82347 5.5 MB + 21300 HUD) +- **建议最后做**,或长期保留原 JS 模块(Kotlin 与 JS 模块可混跑)。 +- **验收**:各界面可打开、无报错;不追求像素级一致。 + +--- + +## 5. 目录规划(拟) + +``` +KT/ + settings.gradle.kts + build.gradle.kts + app/ # 入口(对应 13431) + core/ # 数学 32088、工具 52943 + ecs/ # 61378 + physics/ # 55088 + render/ # 92980 + resource/ # 36659、71105 + net/ # 61860 + codec/*(S2 全部模块) + state/ # 10032、18334 + battle/ # 87072(内部再按子系统分文件) + ui/ # 82347、21300(可延后) + deps/ # React/Redux/Sentry 的 external 声明 + web/ # index.html、resources.json、静态资源 +``` + +约定: +- 一个 webpack 模块 ≈ 一个 Kotlin 文件(巨型模块按类拆); +- 对外属性名保持不变(跨模块共享),类名可改; +- 浏览器 API 全部 `external`(WebGL2 / WebSocket / PointerLock / WebAudio / Storage / Worker)。 + +--- + +## 6. 依赖与版本 + +| 依赖 | 模块 | 版本 | 状态 | +|---|---|---|---| +| kotlin-stdlib-js | 37036 | 待挖 | — | +| kotlinx-serialization-core / json | 9539 / 61430 | 待挖 | — | +| kotlinx-coroutines | 89613 | 待挖 | — | +| atomicfu | 71426 | 待挖 | — | +| kvbuffer | 72834 | 待挖 | — | +| React / React-DOM | 78931 / 45555 | 18.3.1 | ✅ | +| Sentry Browser SDK | 30285 | 5.30.0 | ✅ | +| Underscore | 94965 | 1.13.6 | ✅ | +| Redux(Kotlin 侧) | 10849 | 自研封装 | 需评估 | +| qrcode 系列 | 59411 等 | 待挖 | — | +| Emotion / inline-style-prefixer | 14766 / 77249 等 | 待挖 | — | +| fflate | 29518 | 待挖 | — | + +--- + +## 7. 换版与漂移处理 + +1. 抓新 `main..js` → `node tools/split.js --clean` +2. `node tools/map_symbols.js <旧unpacked> <新bundle> all tools/symbol_map.json` +3. `__hornetSchema__.SelfCheck()` 列出语义字段失配 → **只改 `hornet_schema.js`** +4. 人工核对三类: + - `$_.xx` 导出键(队伍 `vd/dl`、碰撞世界 `55088.$_.h` 等) + - 工具 hook 的类内部方法(`o37` / `o3c` / `n3c` / `f3t` / `q9q` / `kz7`) + - 渲染管线字段语义(`d75_1` / `w39_1` / `b74_1` …) +5. `python build_bundle.py` → 实测工具 + +--- + +## 8. 产出"可启动 JS"的端到端流程 + +```bash +# 1) 基线 +node tools/split.js --clean +node 3DTank/tools/extract.js ../main.7cfa4058.js ../src +cd 3DTank/tools && node mine_evidence.js && cd .. + +# 2) Kotlin 编译(工程建好后) +./gradlew :app:build # 产物:build/dist/js/productionExecutable/ + +# 3) 产物接入(二选一) +# A. 用 Kotlin 产物直接替换 main..js(需自建 webpack runtime) +# B. 保留现有 loader,把 Kotlin 产物作为新模块注册进 __modules__ +node tools/pack_unpacked.js + +# 4) 验收 +node tools/test_loader.js # L1 +node 3DTank/tools/serve.js --port 8080 # L2:浏览器进站 +node 3DTank/tools/wss_server.js --port 9091 --upstream wss://c1.3dtank.com:9090/ # L3:抓包对比 +``` + +--- + +## 9. 风险与回退 + +| 风险 | 影响 | 应对 | +|---|---|---| +| 协议字段序号错位 | 整包不可用 | S2 逐帧 golden 对比;先只做只读命令 | +| Kotlin 版本差异 | 输出与原包不同 | 不追求字节一致,只追求语义等价(双跑) | +| 巨型模块拆分失控 | 进度不可控 | 87072/82347 按子系统拆文件,先骨架后逻辑 | +| 渲染管线语义不明 | 画面异常 | 保留原 92980 模块,先替换非渲染层 | +| UI 体量过大 | 拖慢整体 | S6 延后,允许长期混跑原 JS 模块 | +| 换版漂移 | 工具失效 | schema + `SelfCheck()` 作为唯一适配面 | + +**回退策略**:每个阶段都保留"原模块可用"的开关;Kotlin 模块按模块 id 替换,出问题单模块回退,不影响整包。 + +--- + +## 10. 待确认 + +1. `32088`(标注"Alternativa3D,Kotlin 编写")是自研移植还是开源库?决定 S4 是"引入"还是"重写"。 +2. `10032` / `10849`(Redux)是否必须保持 Kotlin 实现,还是可以换成原生 Redux? +3. S6 UI 是否允许永久保留原 JS 模块(推荐允许)。 +4. 是否要求最终产物能**替代** `main..js`,还是只要"能启动 + 职责等价"即可。