修复 gen_1_21 渲染:1.21 的 gameLoadFinished 门禁(第 13 处世代差异)
症状:gen_1_21 启动成功、数据注入成功(loadedChunks=49),
但 chunksRendered=0、画面停在 Mojang 加载屏(distinctColors≈199)。
根因(1.21 GameRenderer.render 内部,编译期看不出):
boolean ok = minecraft.isGameLoadFinished();
if (ok && renderLevel && level != null) renderLevel(...);
该标志只在 reloadResourcePacks 回调(onResourceLoadFinished)里置 true。
我们不跑异步 reload(会死锁)⇒ 恒 false ⇒ 世界永不渲染。
1.20.x 无此门禁 ⇒ gen_1_20 从未受影响。
修法:gen_1_21 的 McfoldBridge 加 markGameLoadFinished(反射置位),
在 boot 里 shader 编译后调用 —— 差异留在 Java 侧。
★ 教训:这类差异不崩不报错,只是画面不对 ⇒ 判据必须用
chunksRendered/distinctColors,不能只看进程活着。
顺带修:tick 异常不再静默吞掉(try{tick()}catch(ignore){} → 打印第一帧异常)
新增诊断字段:screenNow/overlayNow/levelNow/playerNow/noRender
实测:gen_1_21 现在 chunksRendered=25、has_world_content=True
(草地/天空/太阳/HUD 正常)
This commit is contained in:
@ -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`」,不能只看「进程活着」。
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user