世界加载检测

基本信息

属性 值
文件 net/sxmaa/headlessnh/mixins/early/WorldRendererMixin.java
目标类 net.minecraft.client.renderer.WorldRenderer
注入点 updateRenderer @RETURN
Mixin 集 NOTIFY_OF_FINISHED_WORLD_LOAD(EARLY,排除 Angelica)
文件 net/sxmaa/headlessnh/mixins/late/CeleritasWorldRendererMixin.java
目标类 com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer(@Pseudo)
注入点 drawChunkLayer @RETURN(@Dynamic)
Mixin 集 NOTIFY_OF_FINISHED_WORLD_LOAD_ANGELICA(LATE,要求 Angelica)

这两个 Mixin 是一对互斥替代实现,负责告诉 IntegrationTestController 「世界已进入」。它们不判断加载是否完成,只在渲染循环的每帧回调 onWorldLoaded(),由后者按 stage 决定行为。

为什么分两个

WorldRendererMixin 挂在原版 WorldRenderer.updateRenderer 上。Angelica 用 Celeritas 接管了区块渲染,原版的调用点不再可靠,所以启用 Angelica 时由 Mixins.java 的 addExcludedMod / addRequiredMod 换成 Celeritas 版本。同一时刻只会加载其中一个。

CeleritasWorldRendererMixin 标了 @Pseudo + @Dynamic + remap = false:Celeritas 不在编译期 classpath 上,这样即使 Angelica 缺席也不会导致 Mixin 应用失败。

onWorldLoaded() 的分支

onWorldLoaded() 每帧被调用,内部先做 mc == null || mc.theWorld == null 早退,然后:

isActive() 为真(未设 headlessnh.active):不驱动菜单,只在每次世界加载都写 headlessnh.marker.worldloaded 标记。源码注释写明这是「保持原有行为」。

isActive() 为假(自动化真正在跑):mc.thePlayer == null 时早退(玩家还没进世界),然后按 stage:

  • MULTIPLAYER:serverLoadHandled 保证只处理一次,调度 disconnectAndAdvance(serverJoinSettleMillis(), markerServerLoadedName(), gateServerLoadedName())。
  • SINGLEPLAYER:额外要求 mc.isSingleplayer() 为真才算数——源码注释解释这是因为多人服务器还在拆除时,渲染帧可能带着 stage == SINGLEPLAYER 触发。处理后立即 stage = Stage.DONE。
  • DONE:无操作。

disconnectAndAdvance 的顺序

IntegrationTestController 里的 disconnectAndAdvance 在新线程上按固定顺序执行:Thread.sleep(settleMillis) → 写 marker → Thread.sleep(markerCooldownMillis()) → 等 gate(awaitGate 返回 false 表示已超时失败,直接 return)→ 投递主线程任务:置 teardownRequested = true、直接 displayGuiScreen(new GuiIngameMenu())、最后才把 stage 翻成 SINGLEPLAYER。

[!NOTE] 用 new GuiIngameMenu() 而不是 ingameMenu.displayInGameMenu(),源码注释说明是因为前者在前台已有界面时会被忽略。teardownRequested 标志则让 MinecraftMixin 的暂停菜单分支知道这个菜单是自己开的、应当点「返回游戏」而不是关掉。

相关条目