实测链路(全绿): 1. 官方 ProGuard 映射 → SRG → SpecialSource 重映射 → client-named.jar 2. 反编译确认 MC 渲染器挂在 Minecraft 单例上,不能独立 new 3. 权威序列:tryDetectVersion → Bootstrap → RenderSystem → new Minecraft(cfg) 4. 渲 3 帧 + glReadPixels → PNG,画面是 Mojang 加载屏(真渲染,非清屏) 三个坑(实测证据): - 漏 tryDetectVersion → Game version not set - FolderData 参数顺序错 → icons/ 404 - assetIndex 用 id(5) 而非落盘名(1.20.1) → 资源全找不到 下一步:ClientLevel 构造只需 listener.registryAccess(), ClientChunkCache.replaceWithPacketData 接收 map_chunk 原始字节 (与 chunk.py 同格式)⇒ 这就是数据喂渲染器的落点。
mcfold scaffold —— 用官方客户端渲染,但不跑客户端
目标(用户 2026-09-25 定向):不起完整客户端,把 MC 的渲染能力抽出来, 用我们自己的世界数据驱动它出图。
实测结论(2026-09-25 全绿)
客户端 jar 是 ProGuard 全量混淆的(LevelRenderer 叫 fjv),
所以「抽渲染器」的第一步必须是反混淆。三层关卡全部验证通过:
| 关卡 | 证据 | 结果 |
|---|---|---|
| 反混淆 | client-named.jar 25MB;javap 读出 net.minecraft.client.renderer.LevelRenderer |
✅ |
| 编译期引用 | javac 编译引用 Minecraft/LevelRenderer 的代码 |
✅ |
| GL 离屏 | Xvfb + llvmpipe → OpenGL 4.5 core | ✅ |
| 渲染栈初始化 | new Minecraft(cfg) 完成 GL/资源/渲染器初始化,不进主循环 |
✅ |
| 真渲染出图 | 渲 3 帧 + 读像素 → PNG,画面是 Mojang 加载屏(logo 贴图 + 进度条) | ✅ |
为什么渲染器不能独立 new
反编译(Vineflower)给出的实测签名:
LevelRenderer(Minecraft, EntityRenderDispatcher, BlockEntityRenderDispatcher, RenderBuffers)
renderLevel(PoseStack, float, long, boolean, Camera, GameRenderer, LightTexture, Matrix4f)
全部挂在 Minecraft 单例上 ⇒ 不能 new LevelRenderer() 独立用。
正确形态是「初始化 MC 渲染栈,但不跑主循环」。
权威序列(反编译 net.minecraft.client.main.Main 得到)
SharedConstants.tryDetectVersion(); // ← 漏了会 "Game version not set"
SharedConstants.enableDataFixerOptimizations();
Bootstrap.bootStrap();
RenderSystem.initRenderThread();
RenderSystem.beginInitialization();
Minecraft mc = new Minecraft(gameConfig); // ← 构造即完成渲染栈初始化
RenderSystem.finishInitialization();
// 此处官方会起 Game thread 跑主循环 —— 我们不做
渲染一帧(反编译 Minecraft.render() 得到):
RenderSystem.clear(16640, ON_OSX);
mainRenderTarget.bindWrite(true);
gameRenderer.render(partialTick, nanos, renderLevel);
mainRenderTarget.unbindWrite();
mainRenderTarget.blitToScreen(w, h);
window.updateDisplay();
踩过的三个坑(都有实测证据)
- 漏
tryDetectVersion()→DataFixers.<clinit>抛Game version not set FolderData参数顺序(gameDirectory, resourcePackDirectory, assetDirectory, assetIndex)—— 顺序写错会让icons/icon_16x16.png404- assetIndex 名要用落盘名
version.json 里
assetIndex.id = "5",但磁盘是assets/indexes/1.20.1.json(MCLC 按version.number落盘)—— 传"5"则索引加载失败、资源全找不到
下一步:渲染真实世界
ClientLevel 构造只需 listener.registryAccess()(其余字段不用 listener),
且 ClientChunkCache.replaceWithPacketData(...) 接收的正是 map_chunk 包的原始字节
—— 与 mcfold chunk.py 解码器同一格式。这就是设计文档说的桥梁落点。
构建
./build.sh /path/to/store/vanilla-1.20.1 1.20.1