Mixin 自 0.8 版本以来支持了被 ModLauncher 引导,因此对于使用何种引导 Mixin 的方式又有了新的选择。
以下代码皆参照使用 Mixin 0.8 稳定版本
- 对于使用 LaunchWrapper 启动游戏的情况:
- 如果你需要的 Mixin 由其他模组提供,且没有对 Mixin 环境做其他设置的需求,那么只需要在
META-INF/MANIFEST.MF
中写入MixinConfigs
属性即可。 - 如果你的模组内置了 Mixin ,由于 Mixin 有自己的 Tweaker ,因此理论上如果自己不写继承于
ITweaker
的类作为入口时,引导的方法如下:- 在
META-INF/MANIFEST.MF
中添加TweakClass
用于指定 Mixin 的入口类:TweakClass: org.spongepowered.asm.launch.MixinTweaker MixinConfigs: <mixins.examplemod.json>
- 如果你的模组需要使用
@Mod
注解,由于 FML 搜索此注解时会忽略具有 TweakClass 属性的 jar 包,所以 Mixin 添加了一个属性ForceLoadAsMod
,当此属性设置为true
时,Mixin 会把这个模组从忽略列表中移除。 - 如果你的模组需要使用
IFMLLoadingPlugin
,传统上当 FML 遇到同时具有TweakClass
和FMLCorePlugin
属性的 jar 包时,只会加载TweakClass
而忽略FMLCorePlugin
,但是 Mixin 做了一些额外的操作来让二者共存。一个较为完整的MANIFEST.MF
应该像下面这样:TweakClass: org.spongepowered.asm.launch.MixinTweaker MixinConfigs: <mixins.examplemod.json> FMLCorePlugin: <com.example.examplemod.asm.FMLLoadingPlugin> ForceLoadAsMod: true
- 在
- 如果介于某些原因,你的模组内置了 Mixin ,但是又必须使用实现
ITweaker
的类作为入口的话,由于TweakClass
不能同时填写两个类,因此需要自己完成 Mixin 的引导工作,由于 Mixin 封装程度较高,你只需要添加一行代码即可:
另外,上面所阐述的所有写进package com.example.examplemod.asm; import java.io.File; import java.util.List; import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.LaunchClassLoader; import org.spongepowered.asm.launch.MixinBootstrap; public class ExampleTweaker implements ITweaker { @Override public void injectIntoClassLoader(LaunchClassLoader classLoader) { MixinBootstrap.init(); // <-- 开始 Mixin 的引导工作 } @Override public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) { } @Override public String getLaunchTarget() { return ""; } @Override public String[] getLaunchArguments() { return new String[0]; } }
MANIFEST.MF
的属性在这种情况下依然有效,而且也可以通过代码手动完成:package com.example.examplemod.asm; import java.io.File; import java.util.List; import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.LaunchClassLoader; import net.minecraftforge.fml.relauncher.CoreModManager; import org.spongepowered.asm.launch.MixinBootstrap; import org.spongepowered.asm.mixin.Mixins; public class ExampleTweaker implements ITweaker { @Override public void injectIntoClassLoader(LaunchClassLoader classLoader) { MixinBootstrap.init(); Mixins.addConfiguration("mixins.example.json"); // <-- 添加配置文件 try { CoreModManager.getIgnoredMods().remove(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getName()); // <-- 将自身从 CoreMod 忽略列表中移除 } catch (Throwable ignored) {} } @Override public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) { } @Override public String getLaunchTarget() { return ""; } @Override public String[] getLaunchArguments() { return new String[0]; }
- 如果你需要的 Mixin 由其他模组提供,且没有对 Mixin 环境做其他设置的需求,那么只需要在
- 对于使用 ModLauncher 启动游戏的情况,由于 ModLauncher 使用了 SPI 作为查找和加载所有实现
ITransformationService
和ILaunchPluginService
的类的手段,因此即使我们在同一个 jar 包内也使用了这两个接口,我们也无需再额外写代码手动引导 Mixin,同时,MixinBootstrap 模组的出现也大大缓解了「所有人都在 bundle Mixin」的情况:- 如果你的模组没有用到
ITransformationService
接口,并且无需对 Mixin 做任何额外的设置,那么把MixinConfigs
属性写入MENIFEST.MF
即可。 - 如果你需要对 Mixin 做额外设置,但是不使用
ITransformationService
接口,那么 Mixin 提供了一个需要添加到MANIFEST.MF
之中的属性MixinConnector
用于在预启动阶段添加所有继承于IMixinConnector
的类,在这之中你可以设置 Mixin 环境:
META-INF/MANIFEST.MF
:MixinConnector: <com.example.examplemod.MixinConnector>
com/example/examplemod/MixinConnector.java
:package com.example.examplemod; import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.asm.mixin.connect.IMixinConnector; public class MixinConnector implements IMixinConnector { @Override public void connect() { Mixins.addConfiguration("mixins.examplemod.json"); } }
- 如果你的模组需要使用
ITransformationService
或者IModLocator
接口,由于 FML 扫描模组时会将使用了这两个接口的模组排除在外,同时由于 Mixin 注入类只能从常规模组中获取到,因此你的模组必须定义IModLocator
接口以在扫描模组时一并把你的模组定位进去:
META-INF/services/net.minecraftforge.forgespi.locating.IModLocator
:<com.example.examplemod.ModLocator>
com/example/examplemod/ModLocator.java
:
同时如果你的模组使用了package com.example.examplemod; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; import net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileLocator; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.forgespi.locating.IModFile; public class ModLocator extends AbstractJarFileLocator { @Override public List<IModFile> scanMods() { List<IModFile> list = new ArrayList<>(); try { IModFile file = new ModFile(Paths.get(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()), this); // <-- 把自身添加到常规模组中去 this.modJars.compute(file, (mf, fs) -> this.createFileSystem(mf)); list.add(file); } catch (Throwable ignored) {} return list; } @Override public String name() { return "Example Mod Locator"; } @Override public void initArguments(Map<String, ?> arguments) { } }
ITransformationsService
,由于 ModLauncher 检测到META-INF/services/cpw.mods.modlauncher.api.ITransformationService
文件后不再检测META-INF/services/net.minecraftforge.forgespi.locating.IModLocator
文件,因此你需要手动把模组自身加到locators
里去:package com.example.examplemod; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Set; import cpw.mods.modlauncher.api.IEnvironment; import cpw.mods.modlauncher.api.ITransformationService; import cpw.mods.modlauncher.api.ITransformer; import net.minecraftforge.fml.loading.ModDirTransformerDiscoverer; public class TransformationService implements ITransformationService { @Override public String name() { return "examplemod"; } @Override public void initialize(IEnvironment environment) { try { ModDirTransformerDiscoverer.getExtraLocators().add(Paths.get(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI())); // <-- 把自身添加到 ModDirTransformerDiscoverer#locators 里去 } catch (Throwable ignored) {} } @Override public void beginScanning(IEnvironment environment) { } @Override public void onLoad(IEnvironment env, Set<String> otherServices) { } @Override public List<ITransformer> transformers() { return new ArrayList<>(); } }
- 如果你的模组需要内置 Mixin ,那么除了需要做上一条所述的事情之外,还需要做以下额外操作:(以 MixinBootstrap 模组为例)
- 由于 Mixin 需要被
AppClassLoader
加载,同时因为 ModLauncher 使用独立的类加载器来加载这些模组,因此你必须把模组自身添加到AppClassLoader
的 classpath 中去,并且使用AppClassLoader
来初始化 Mixin 的类。因为自 Java 9 以来,AppClassLoader
不再继承于URLClassLoader
,因此不能简单地使用URLClassLoader#addURL
方法来添加,所以 MixinBootstrap 模组使用了一套较为复杂的反射来实现。 - 由于 ModLauncher 并不原生支持从模组中加载实现
ILaunchPluginService
的类,因此你需要把 Mixin 实现ILaunchPluginService
的类手动添加到LaunchPluginHandler#plugins
里去。 - 由于 Forge 在运行时并不包含 asm-analysis 和 asm-util 这两个库,但是 Mixin 运行时需要它们,因此打包模组时还需要将这两个库一并打包。
- 由于 Mixin 需要被
- 如果你的模组没有用到