用户口径:差异放在 Java 层,Python 相当于路由。 下移到 Java(新增 cmd=setblocks): Python 只发 RLE([[stateId,count],...]),Java 用官方 API 完成 调色板组装 / 位打包 / 官方序列化 / 入 cache / 渲染。 ★ 意外收益:走 ClientboundLevelChunkPacketData(chunk).getReadBuffer() + replaceWithPacketData(...) 这条官方路径,两代签名完全一致 ⇒ 原先的 Storage.chunks 反射注入**整段删掉**。 新增 cmd=capacity:世界尺寸(sectionsCount/minSection/defaultBiome) 由运行时回答,Python 不再硬编码 24/-4(1.16 时代是 16/0)。 Python 侧删除(防回归,见 test_import_surface): render_bridge.py(旧桥,无世代概念) render._pack/_long_array/_encode_paletted/_encode_chunk/_varint anvil.encode_block_section/encode_chunk_payload/save_chunk_to_payload 合计 454→374 行(render.py)、300→177 行(anvil.py) 保留在 Python(都是「不随版本变」的知识): RLE 压缩(rle_encode/rle_decode,具名可测) 存档读取(region/NBT) 方块名→id 的路由(查询在 Java) NDJSON 协议 测试:62 项单元 + 6 项渲染联测全绿 新测试反映新架构:RLE 边界、运行时容量、世代分派、新路径渲染 test_anvil 里原有 8 项协议位打包测试已删(那已下移到 Java, 由两代后端各自的编译验证覆盖) 新增 scaffold/ARCHITECTURE.md(分层原则 + 加新世代步骤 + 防回归清单)
3.6 KiB
3.6 KiB
架构分层:谁负责什么
用户口径(2026-09-25):差异放在 Java 层,Python 相当于路由。
三层
Python(mcfold/)
├── backends/__init__.py 路由:按 client jar 判世代 → 选后端 ← 唯一的 switch
├── backends/base.py 路由:NDJSON 协议、命令集、RLE 压缩
├── anvil.py 读取:存档 region/NBT → 方块(名字规格)
└── render.py 读取 + 编排:WorldRenderer
│
│ NDJSON(chunk 数据 / RLE 方块 / 相机 / 渲染指令)
▼
Java(scaffold/java/gen_1_20/ , gen_1_21/)
├── McfoldRenderServer.java 协议格式、调色板组装、位打包、入 cache、渲染
└── McfoldBridge.java 包私有 API 桥(reloadShaders / loadEffect)
│
▼
官方客户端 jar(各自版本,重映射为可读名)
分界原则
| 层 | 负责 | 为什么 |
|---|---|---|
| Python | 路由、存档读取、方块名→id 查询、RLE 压缩、编排 | 这些不随 MC 版本变 |
| Java | 协议格式、调色板、位打包、官方序列化、渲染调用 | 这些历代改过,必须贴着版本走 |
判据:这个知识「随版本变吗」
| 知识 | 随版本变? | 归属 |
|---|---|---|
| 方块名 → state id | 变(mod/版本) | Java(问运行时注册表) |
| 世界尺寸(sectionsCount / minSection) | 变(1.16 是 16/0,1.20 是 24/-4) | Java(cmd="capacity") |
| PalettedContainer 位打包 | 变过 | Java(官方 API) |
| LongArray 编码 | 变过 | Java(官方 API) |
| 区块入 cache 的路径 | 签名变过 | Java(官方 replaceWithPacketData) |
| RLE 压缩 | 不变 | Python(只是压重复值) |
| 存档 NBT 结构 | 基本不变 | Python(读取) |
| NDJSON 协议 | 不变(我们自己定的) | Python(路由层) |
数据流:渲染一个存档
1. Python: 读 level.dat + region/*.mca
2. Python: NBT → Sections[].block_states(palette + data)
3. Python: palette 名字(+属性) → "minecraft:oak_log[axis=y]" 规格
4. Python: 向后端批量查询规格 → state id [Java: BuiltInRegistries.BLOCK.asLookup()]
5. Python: state id 列表 → RLE [[id,count],...] ← 唯一被压缩的东西
6. Python: setblocks(cx, cz, sections) ← NDJSON
7. Java: LevelChunk + LevelChunkSection.setBlockState(...)
8. Java: 光照(queueSectionData 全亮)
9. Java: ClientboundLevelChunkPacketData(chunk) ← 官方序列化
10. Java: replaceWithPacketData(官方入口入 cache)
11. Java: gameRenderer.render(...)(世代差异在此)
12. Java: 读 FBO → PNG → base64 回传
第 4/7/8/9/10/11 步都在 Java 侧 —— 版本相关知识全部集中在那里。
加新世代(用户 2026-09-25 定向的收益)
scaffold/java/gen_<ver>/建目录,拷上一代源码- 对着 javap 输出改差异点(实测共 11 处,见 VERSION-DIFFS.md)
mcfold/backends/gen_<ver>.py声明markers(判据类)mcfold/backends/__init__.py的GENERATIONS加一条- 各自对对应版本的 named jar 编译
Python 侧只动第 3、4 步(各一处),协议与渲染逻辑无需碰。
已删除(防回归)
| 已删 | 原因 |
|---|---|
mcfold/render_bridge.py |
被 backends/ 取代(旧版无世代概念) |
render._pack/_long_array/_encode_paletted/_encode_chunk/_varint |
协议编码下移到 Java |
anvil.encode_* / save_chunk_to_payload |
同上 |
回归判据见 tests/test_anvil.py::test_import_surface —— 这些名字不应再出现在 Python 里。