MinecraftMixin

基本信息

属性 值
文件 net/sxmaa/headlessnh/mixins/early/MinecraftMixin.java
目标类 net.minecraft.client.Minecraft
注入点 runTick @HEAD、startGame @TAIL、displayGuiScreen @TAIL
Mixin 集 JOIN_WORLD_ON_MAINMENU_LOAD(EARLY)
平台 纯客户端(addClientMixins)

这是 HeadlessNH 的主入口:整个「自动点菜单」的行为都挂在一个 displayGuiScreen 的 TAIL 注入上——每当客户端切换界面,mixin 就检查这个新界面是不是下一步该自动操作的目标。

两个注入点

runTick @HEAD — 主线程任务队列

IntegrationTestController 内部所有可能触碰 OpenGL 上下文的操作都通过 runOnMainThread(Runnable) 投递到 MAIN_THREAD_TASKS(ConcurrentLinkedQueue)。本注入在每个 tick 开头把队列排干并逐个 task.run()。

[!NOTE] 这是必要的:菜单点击、字段写入、displayGuiScreen 切换都必须在渲染线程执行,而 MinecraftMixin 内部的「等待稳定」逻辑跑在自建的 new Thread(...) 里。

startGame @TAIL — 启动完成

置 headlessNH$finishedLoading = true,调用 logSettings() 打印全部属性,然后 onGameStarted() 写出主菜单 marker(仅在 isActive() 时,且 mc != null)。若主菜单尚未标记为已到达,立刻 changedScreen(this.currentScreen, null) 手动触发一次后续处理。

displayGuiScreen 的 6 个分支

整个入口先做一次守卫:if (!Boolean.getBoolean("headlessnh.active")) return;——即属性未设(自动化开启)才继续。

新界面 动作 硬编码延迟
GuiMainMenu 等 delay.mainmenu → 写主菜单 marker → 等 mainmenu gate → 合成 GuiButton(1=单人 / 2=多人) 派发 可配(500 ms)
GuiSelectWorld 派发 GuiButton(3)(「创建新世界」) 500 ms
GuiCreateWorld 若两个文本框为空则先 Thread.sleep(2500),再派发 GuiButton(0)(「创建」) 500 ms + 2500 ms
GuiMultiplayer 派发 GuiButton(4)(「直接连接」) 500 ms
GuiScreenServerList 把地址框文本设为 127.0.0.1,再设 serverIP 并 confirmClicked(true, 0) 500 ms
GuiDisconnected 等 1000 ms → onConnectionFailed() → 复位多人/直连标志 → 回主菜单 1000 ms
GuiIngameMenu 若不是自己请求的拆除则警告并关闭;否则派发 GuiButton(1)(返回游戏) 500 ms

需要注意的实现细节

  • 服务器地址硬编码为 127.0.0.1,没有任何系统属性可以覆盖。多人测试只能连本机。
  • 除主菜单分支外,所有延迟都是硬编码字面量(500 / 1000 / 2500 ms),不受 headlessnh.delay.* 控制。
  • GuiCreateWorld 分支读 createWorld.field_146333_g / field_146335_h(两个文本框)判断世界名是否为空,靠 access transformer 开放。
  • 按钮 ID 取自原版:GuiMainMenu 单人=1/多人=2、GuiSelectWorld 创建=3、GuiCreateWorld 创建=0、GuiMultiplayer 直连=4、GuiIngameMenu 返回游戏=1。这些 actionPerformed 方法在 1.7.10 是 private,靠 AT 开放。

相关条目