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 处理——服务端世界通常不由客户端的"选择世界"界面删除。