diff --git a/scaffold/java/VERSION-DIFFS.md b/scaffold/java/VERSION-DIFFS.md index 7f7a5da..6ef535b 100644 --- a/scaffold/java/VERSION-DIFFS.md +++ b/scaffold/java/VERSION-DIFFS.md @@ -79,3 +79,35 @@ Java 编译期绑定签名。实测: 不走老版 `downloads.classifiers` 结构 ⇒ 两种形态都要认。 - 跳过音频资源可省 **3744/3911 条**(仅 167 条实际下载), 实测 1.21.1 全量 274 文件 / 200MB / 32s,0 失败。 + +## 补充 2:1.21 的 gameLoadFinished 门禁(第 13 处,编译期看不出) + +gen_1_21 曾经「启动成功但渲染 0 区块、画面停在 Mojang 加载屏」。 +根因在 `GameRenderer.render(DeltaTracker, boolean)` 内部(1.21 反编译): + +```java +boolean ok = this.minecraft.isGameLoadFinished(); +if (ok && renderLevel && this.minecraft.level != null) { + this.renderLevel(deltaTracker); // ← 只有 ok 才渲染世界 +} +``` + +而该标志只在 **`reloadResourcePacks()` 的回调**里被置 true +(`Minecraft.onResourceLoadFinished` 第 628 行)。 + +⇒ 我们不跑异步 reload(会死锁,见前文)⇒ 该位恒 false ⇒ 世界永不渲染。 +**1.20.x 没有这个门禁**,所以 gen_1_20 一直正常。 + +修法(在 Java 侧,符合「差异留在 Java」的原则): + +```java +// gen_1_21/net/minecraft/client/renderer/McfoldBridge.java +public static void markGameLoadFinished(Minecraft mc) { + Field f = Minecraft.class.getDeclaredField("gameLoadFinished"); + f.setAccessible(true); + f.setBoolean(mc, true); +} +``` + +★ 这类差异**只在上层失败**,不会崩、不会报错,只是画面停在加载屏 —— +所以判据要用「`chunksRendered` / `distinctColors`」,不能只看「进程活着」。 diff --git a/scaffold/java/gen_1_20/McfoldRenderServer.java b/scaffold/java/gen_1_20/McfoldRenderServer.java index 36f29da..31ccf6f 100644 --- a/scaffold/java/gen_1_20/McfoldRenderServer.java +++ b/scaffold/java/gen_1_20/McfoldRenderServer.java @@ -233,6 +233,8 @@ public class McfoldRenderServer { }); light.setLightEnabled(new ChunkPos(cx, cz), true); light.runLightUpdates(); + // ★ 必须触发网格重建 —— 否则渲染器不知道该 section 有数据(实测) + mc.levelRenderer.allChanged(); JsonObject r = new JsonObject(); r.addProperty("ok", true); r.addProperty("placed", placed); @@ -529,7 +531,9 @@ public class McfoldRenderServer { static void tickFrames(int n) { MainTarget target = (MainTarget) mc.getMainRenderTarget(); for (int f = 0; f < n; f++) { - try { mc.tick(); } catch (Throwable ignore) {} + try { mc.tick(); } catch (Throwable t) { + if (f == 0) { System.err.println("[mcfold] tick 异常(第1帧): " + t); t.printStackTrace(System.err); } + } RenderSystem.clear(16640, Minecraft.ON_OSX); target.bindWrite(true); mc.gameRenderer.render(0.0F, (long) f * 16_000_000L, true); diff --git a/scaffold/java/gen_1_21/McfoldRenderServer.java b/scaffold/java/gen_1_21/McfoldRenderServer.java index 04e6763..44570b3 100644 --- a/scaffold/java/gen_1_21/McfoldRenderServer.java +++ b/scaffold/java/gen_1_21/McfoldRenderServer.java @@ -233,6 +233,8 @@ public class McfoldRenderServer { }); light.setLightEnabled(new ChunkPos(cx, cz), true); light.runLightUpdates(); + // ★ 必须触发网格重建 —— 否则渲染器不知道该 section 有数据(实测) + mc.levelRenderer.allChanged(); JsonObject r = new JsonObject(); r.addProperty("ok", true); r.addProperty("placed", placed); @@ -438,6 +440,12 @@ public class McfoldRenderServer { var cc = (net.minecraft.client.multiplayer.ClientChunkCache) level.getChunkSource(); r.addProperty("loadedChunks", cc.getLoadedChunksCount()); r.addProperty("chunksRendered", mc.levelRenderer.countRenderedSections()); + r.addProperty("screenNow", mc.screen != null ? mc.screen.getClass().getSimpleName() : "null"); + r.addProperty("overlayNow", mc.getOverlay() != null ? mc.getOverlay().getClass().getSimpleName() : "null"); + r.addProperty("levelNow", mc.level != null); + r.addProperty("playerNow", mc.player != null); + r.addProperty("cameraEntity", mc.getCameraEntity() != null ? "set" : "null"); + r.addProperty("noRender", mc.noRender); r.addProperty("hasRenderedAllChunks", mc.levelRenderer.hasRenderedAllSections()); var got = cc.getChunk(pcx, pcz, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, false); r.addProperty("chunkAtPlayer", got != null ? got.getClass().getSimpleName() : "null"); @@ -529,7 +537,9 @@ public class McfoldRenderServer { static void tickFrames(int n) { MainTarget target = (MainTarget) mc.getMainRenderTarget(); for (int f = 0; f < n; f++) { - try { mc.tick(); } catch (Throwable ignore) {} + try { mc.tick(); } catch (Throwable t) { + if (f == 0) { System.err.println("[mcfold] tick 异常(第1帧): " + t); t.printStackTrace(System.err); } + } RenderSystem.clear(16640, Minecraft.ON_OSX); target.bindWrite(true); // ★ 世代差异(1.21+):render(DeltaTracker, boolean) @@ -676,6 +686,17 @@ public class McfoldRenderServer { net.minecraft.client.renderer.McfoldBridge.reloadShaders(mc.gameRenderer, mc.getResourceManager()); + // ★★ 世代差异(1.21+,编译期看不出): + // GameRenderer.render(DeltaTracker,boolean) 里有门禁 + // boolean ok = minecraft.isGameLoadFinished(); + // if (ok && renderLevel && level != null) renderLevel(...); + // 而 gameLoadFinished 只在 **reloadResourcePacks 的回调** + // (onResourceLoadFinished)里被置 true。 + // 我们不跑异步 reload(会死锁)⇒ 该位恒 false ⇒ 世界永不渲染 + // (症状:画面停在 Mojang 加载屏,distinctColors≈199,chunksRendered=0)。 + // 1.20.x 无此门禁,所以 gen_1_20 不受影响。 + net.minecraft.client.renderer.McfoldBridge.markGameLoadFinished(mc); + mc.setOverlay(null); mc.setScreen(null); mc.options.setCameraType(net.minecraft.client.CameraType.THIRD_PERSON_BACK); diff --git a/scaffold/java/gen_1_21/net/minecraft/client/renderer/McfoldBridge.java b/scaffold/java/gen_1_21/net/minecraft/client/renderer/McfoldBridge.java index fff0d60..81d56c5 100644 --- a/scaffold/java/gen_1_21/net/minecraft/client/renderer/McfoldBridge.java +++ b/scaffold/java/gen_1_21/net/minecraft/client/renderer/McfoldBridge.java @@ -12,6 +12,26 @@ import net.minecraft.server.packs.resources.ResourceProvider; * loadEffect —— 官方由 onResourceManagerReload / 药水效果改 */ public class McfoldBridge { + /** + * 标志「游戏加载完成」。 + * + * ★ 1.21 新增的门禁:GameRenderer.render 里 + * boolean ok = minecraft.isGameLoadFinished(); + * if (ok && ...) renderLevel(...); + * 而该位只在 reloadResourcePacks 的回调里置 true(Minecraft 第 628 行)。 + * 我们不跑异步 reload ⇒ 必须手动置位。 + * 1.20.x 没有这个字段/方法,故本方法只存在于 gen_1_21 的 bridge。 + */ + public static void markGameLoadFinished(net.minecraft.client.Minecraft mc) { + try { + java.lang.reflect.Field f = net.minecraft.client.Minecraft.class + .getDeclaredField("gameLoadFinished"); + f.setAccessible(true); + f.setBoolean(mc, true); + } catch (Throwable t) { + throw new RuntimeException("无法置 gameLoadFinished(1.21 门禁)", t); + } + } public static void reloadShaders(GameRenderer gr, ResourceProvider provider) { gr.reloadShaders(provider); }