Mixin 集成

基本信息

属性 值
Core Mod 类 journeymap.client.mixin.MixinLoadingPlugin(MixinLoadingPlugin.java:11)
Mixin 配置 src/main/resources/mixins.journeymap.json
Mixin 包 journeymap.client.mixin.mixins(gradle.properties 的 mixinsPackage)
实际 Mixin 数 2(均为客户端)
辅助接口 journeymap.client.mixin.interfaces.GuiSelectWorldExt
UniMixins 软依赖(反射探测,未装则跳过)

功能

JourneyMap 用 Mixin 做的唯一一件事是:在删除存档时一并删除该世界的 JourneyMap 地图数据。为此它向原版的"选择世界"和"删除确认"两个 GUI 注入一个复选框。

MixinLoadingPlugin

MixinLoadingPlugin 实现 IFMLLoadingPlugin,三个转换器方法全部返回 null(getASMTransformerClass()、getModContainerClass()、getSetupClass()、getAccessTransformerClass(),:15-42)——它本身不做任何字节码转换,唯一作用是 injectData()(:25-44):

Class.forName("org.spongepowered.asm.mixin.Mixins")
      .getDeclaredMethod("addConfiguration", String.class)
      .invoke(null, "mixins.journeymap.json");

源码注释写明"手动注册 mixin 配置是为了让 mixin 保持软依赖"。捕获 ClassNotFoundException 等四类异常后仅记一条 info 日志 JourneyMap mixins won't be loaded(:42),游戏照常启动。

build.gradle.kts 的 tasks.jar 块配合这一点:从 manifest 中 attributes.remove("TweakClass"),注释说明"否则未安装 UniMixins 时会 class not found 启动崩溃"。

2 个 Mixin

1. MixinGuiSelectWorld

@Mixin(GuiSelectWorld.class),实现 GuiSelectWorldExt(MixinGuiSelectWorld.java:19-20)。

  • @Unique 字段 jm$deleteJmData 默认 true(:23-24)
  • @ModifyArg(:32-37)拦截 confirmClicked(ZI)V 中对 ISaveFormat.deleteWorldDirectory(String)Z 的调用,在原调用执行前把传入的 worldName 重写
  • 实际副作用:若 jm$deleteJmData 为真且 worldName 非空,删除 <mcDataDir>/journeymap/data/sp/<世界名>/(:45-52,路径由 Constants.SP_DATA_DIR 拼出,Constants.java:50),成功后记 info 日志
  • 三层异常处理:IOException → warn、Throwable → warn(:54-61)
  • 无论是否删除,最后都 return worldName(:64),不改变原版删除逻辑

2. MixinGuiYesNo

@Mixin(GuiYesNo.class),@Shadow 掉 parentScreen(:21-22)。

  • @Inject(method = "initGui", at = @At("RETURN"))(:27-42):仅当 parentScreen instanceof GuiSelectWorldExt 时,在 width/2 - 80, 110 处加入一个 GuiCheckBox(FML 的 cpw.mods.fml.client.config.GuiCheckBox,见 import :3,不是原版 net.minecraft.client.gui.GuiCheckBox),id -164816186,文字为红色 Constants.getString("jm.common.deleteworld_text"),默认勾选(true,:37),并把父界面的 jm$deleteJmData 置 true(:39)
  • @Inject(method = "actionPerformed", at = @At("HEAD"), cancellable = true)(:44-52):点击该复选框时同步勾选状态到父界面并 ci.cancel(),阻止事件继续传递

GuiSelectWorldExt(interfaces/GuiSelectWorldExt.java)只声明一个方法 void jm$setShouldDeleteJmData(boolean value),是 MixinGuiYesNo 与 MixinGuiSelectWorld 之间的桥接接口。

mixins.journeymap.json

{
  "required": true,
  "minVersion": "0.8.5-GTNH",
  "package": "journeymap.client.mixin.mixins",
  "refmap": "mixins.journeymap.refmap.json",
  "target": "@env(DEFAULT)",
  "compatibilityLevel": "JAVA_8",
  "mixins": [],
  "client": ["MixinGuiSelectWorld", "MixinGuiYesNo"],
  "server": []
}

mixins 与 server 数组均为空——两个 Mixin 都是纯客户端。

数值

数值名 值
Mixin 总数 2
客户端 Mixin 2(MixinGuiSelectWorld、MixinGuiYesNo)
服务端 Mixin 0
MixinLoadingPlugin 提供的 ASM 转换器 0(全部返回 null)
注入的 GUI 控件 1 个 GuiCheckBox
控件 id -164816186
控件位置 (width/2 - 80, 110)
控件默认状态 勾选(true)
加载的 mixin 配置 mixins.journeymap.json
最低 mixin 版本 0.8.5-GTNH

交互

触发 行为
游戏启动 injectData() 反射注册 mixin 配置;UniMixins 缺失时记 info 并跳过
打开"选择世界"→ 删除世界 MixinGuiYesNo 在确认框加入红色复选框"删除 JourneyMap 数据",默认勾选
勾掉复选框 同步到 jm$deleteJmData = false,删除世界时保留地图数据
确认删除 MixinGuiSelectWorld 在 deleteWorldDirectory 调用前删除 <mcDataDir>/journeymap/data/sp/<世界名>/
删除目录失败 仅记 warn 日志,不影响原版删除流程

注意被删的是 SP_DATA_DIR(journeymap/data/sp/,单人存档数据)。多人服务器的世界数据在 MP_DATA_DIR(Constants.java:51),不由本 Mixin 处理——服务端世界通常不由客户端的"选择世界"界面删除。

相关条目