M1+M2:1.21.1 构建跑通 + 协议客户端进 play 阶段

M1(构建):
- build.sh 编译 CP 加入 $OUT/fabric/*.jar,解决 net.fabricmc.* 找不到
- gen_1_21 内联 Sodium 通知改为委托共用 RenderCompat,补 reportRenderCompat
- 1.21.1 全量七步构建产物齐备(classes-intermediary.jar / names*.json / srg / inheritance.map)

M2(协议):
- login_start 按协议数据发 UUID(1.21 必填;1.20 可选)
- login_acknowledged / serverbound 包 id 改为查表,不再写死 0x00
- send() 实现压缩帧格式(varint(dataLen)+数据,小包不压)
- configuration 双向协商:发 settings、回 select_known_packs、拒 resource_pack
- protodef 补 bitflags / entityMetadataLoop / count(ctx引用) / compareTo 路径表达式
- NBT 读取器由占位符改为真实现(anonymousNbt 支撑 registry_data)
- 坏包容错:长度前缀是帧层权威,解不开整包丢弃并布告,不断流

实测:login→configuration(11 注册表)→play,位置 (5.5,-60.0,-0.5),map_chunk×9
判据:protodef 6→13 项(含线上真实 entity_metadata 字节回归);全量 77 项通过
This commit is contained in:
JianFeeeee
2026-09-26 12:15:48 +08:00
parent b21b869e09
commit 547f4beb7e
8 changed files with 1122 additions and 240 deletions

38
plan.md
View File

@ -560,8 +560,8 @@ shot.rough → ASCII + 线索(给 agent 看它为什么选了这个角度)
| # | 内容 | 依赖 | 状态 |
|---|---|---|---|
| **M0** | 起**空白测试服**(§1c),版本隔离三件套 | — | ✅ |
| **M1** | 修 build.sh 编译 CP(加 Fabric 库)⇒ 1.21.1 构建跑通 | — | 🔄 |
| **M2** | **协议客户端**:连测试服 → configuration → registry_data → 进 play | M0 | ⬜ |
| **M1** | 修 build.sh 编译 CP(加 Fabric 库)⇒ 1.21.1 构建跑通 | — | ✅ |
| **M2** | **协议客户端**:连测试服 → configuration → registry_data → 进 play | M0 | ✅ |
| | (这是最大缺口:现有只到 `login_start` 冒烟级) | | |
| **M3** | **状态层**:World / entities / inventory 从**服务端真状态**来 | M2 | ⬜ |
| **M4** | **事件层**:粗粒度 + 细分层(照 mineflayer,省 token) | M2 | ⬜ |
@ -571,6 +571,40 @@ shot.rough → ASCII + 线索(给 agent 看它为什么选了这个角度)
| **M8** | **细渲染**(单次调用):出图,带光影 | M2 | ⬜ |
| **M9** | 第一个用例:`capture_photo()`(粗搜机位 → 细出图) | M6, M8 | ⬜ |
### M2 实测结论(2026-09-26)
**已达成的可复现事实**(连 M0 测试服 127.0.0.1:25566):
```
login phase=login → success(服务端回 UUID 与离线算法一致)
configuration phase=play 注册表=11(biome/damage_type/enchantment/…)
play 28 种包 map_chunk×9、position、declare_commands、entity_metadata
position (5.5, -60.0, -0.5)
```
**踩过的 7 个真实坑**(都是「世代差异」或协议数据理解错误):
| 症状 | 根因 |
|---|---|
| `Failed to decode packet 'hello'` | 1.21.1 `login_start.playerUUID` 是**必填 UUID**;1.20 是 `['option','UUID']` |
| configuration 收不到包 | `login_acknowledged` 包 id 写死 `0x00`,实际 **`0x03`** |
| `Badly compressed packet` | 写侧没实现压缩帧:`varint(dataLen)+数据`,小包 `dataLen=0` |
| 服务端发完 `select_known_packs` 不动 | configuration 是**双向协商**,必须回 `select_known_packs`/发 `settings` |
| `registry_data` 解不开 | 缺 `anonymousNbt`;旧 NBT 只是占位符 |
| `declare_recipes` 流错位 | 数组计数有 `countType`(103处) 与 **`count`(引用 ctx,6处)** 两种 |
| `player_info`/`declare_commands` 解不开 | `compareTo` 可用**路径**:`flags/x`、`../flags/x`(12 处) |
| `entity_metadata` 越界 | 终止符是**光秃 0xFF 一个字节**,不含 type/value |
**关键架构发现(修正项目前提)**:vanilla 1.21.1 只在线同步 **11 个动态注册表**,
**不含 `minecraft:block` / `minecraft:item`**。静态注册表不走线同步 ——
客户端用自己 jar 里的。⇒ Java 侧读 client jar 的路线对 vanilla 正确;
但 **mod 新增方块 vanilla 不会告诉客户端**,那条要靠加载器自身的同步机制。
**已知上游数据缺口(非本仓 bug,已容错)**:`declare_recipes`(108KB)
在**官方 minecraft-data 3.117 + minecraft-protocol** 上同样解不开
(PartialReadError: found 108271 / expected 114153)。
处理:长度前缀是帧层权威 ⇒ **坏包整包丢弃不断流**(`decode_failures` 布告)。
**建议顺序:M0 → M1 → M2**。
- M0/M1 是解阻断(M1 是我已经查到根因的那个 classpath bug)
- **M2 是真正的门槛**:从「能渲染」到「能玩」,缺的不是渲染,是**完整协议层**。