Files
agentmc/scaffold/ARCHITECTURE.md
JianFeeeee 16f2e04fff 架构重构:差异全部下移到 Java,Python 只做路由
用户口径:差异放在 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(分层原则 + 加新世代步骤 + 防回归清单)
2026-09-25 19:21:45 +08:00

3.6 KiB
Raw Permalink Blame History

架构分层:谁负责什么

用户口径(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 定向的收益)

  1. scaffold/java/gen_<ver>/ 建目录,拷上一代源码
  2. 对着 javap 输出改差异点(实测共 11 处,见 VERSION-DIFFS.md)
  3. mcfold/backends/gen_<ver>.py 声明 markers(判据类)
  4. mcfold/backends/__init__.py 的 GENERATIONS 加一条
  5. 各自对对应版本的 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 里。