Stage 状态机

基本信息

属性 值
实现类 net.sxmaa.headlessnh.IntegrationTestController
内部枚举 Mode(公开)、Stage(私有)
状态字段 private static volatile Stage stage

Mode 与 Stage 的关系

Mode 由三个 system property 决定,是公开 API(IntegrationTestController.mode()):

属性 Mode
headlessnh.singleplayer=true SINGLEPLAYER
headlessnh.combined=true COMBINED
都不设 NONE

[!WARNING] 判定顺序是 combined 先于 singleplayer。两个都设时 COMBINED 胜出,SINGLEPLAYER 被静默忽略。

Stage 是私有枚举,只有三个值,它才是真正驱动流程的状态:

Mode 初始 Stage
MULTIPLAYER / COMBINED MULTIPLAYER
SINGLEPLAYER SINGLEPLAYER
NONE DONE

[!NOTE] Mode.MULTIPLAYER 这个枚举值在 mode() 里永远不会返回——多人流程是由 combined 触发的,返回值同样是 COMBINED。switch (mode()) 里的 case MULTIPLAYER, COMBINED 合并分支因此是等价的。

推进流程

SINGLEPLAYER ──────────────────────────────► DONE
MULTIPLAYER ──► SINGLEPLAYER ──────────────► DONE

MULTIPLAYER 阶段:进入多人世界 → 稳定 headlessnh.delay.serverjoin(默认 7500 ms)→ 写 .serverloaded.headlessnh → 等 headlessnh.gate.serverloaded → 打开 GuiIngameMenu 并按按钮 1「返回游戏」触发拆除 → stage = SINGLEPLAYER。

SINGLEPLAYER 阶段:进入单人世界 → 稳定 headlessnh.delay.singleplayer(默认 7500 ms)→ 写 .worldloaded.headlessnh → 等 headlessnh.gate.worldloaded → 拆除 → stage = DONE。

stage 在 disconnectAndAdvance 的拆除动作之后才翻转成 SINGLEPLAYER,源码注释说明这是为了防止服务器世界还在卸载时渲染帧就发出单人世界 marker。

三个「各只触发一次」的一次性标志

MinecraftMixin 用 @Unique 布尔位保证每个界面只被自动点击一次,重试时按需复位:

字段 守护的界面 复位时机
menuMultiplayerTriggered GuiMainMenu 的多人按钮 连接失败重试时
menuSingleplayerTriggered GuiMainMenu 的单人按钮 从不
headlessNH$triggeredWorldSelection GuiSelectWorld 从不
headlessNH$triggeredWorldCreation GuiCreateWorld 从不
headlessNH$triggeredMultiplayer GuiMultiplayer 断开时与直连一并复位
headlessNH$triggeredDirectConnect GuiScreenServerList 断开时与多人一并复位

连接失败重试

onConnectionFailed() 在 GuiDisconnected 出现 1000 ms 后调用:未超过 headlessnh.connectRetries(默认 5)时只递增计数、复位多人/直连标志并 WARN 日志,流程由回到主菜单的 displayGuiScreen(new GuiMainMenu()) 重新驱动;超限则 fail("could not connect to server after N attempts"),其中 N = connectFailures - 1。

[!NOTE] 失败上限是 connectFailures <= connectRetryLimit() 才重试,所以实际重试次数是 5 次、日志里报的 N 是 4,两者相差 1 属于源码写法。

相关条目