CoreModTutor
  • 源代码仓库(求star)
  • 目录
  • 0 绪论
  • 1 简介
    • CoreMod
    • Minecraft混淆方式
  • 2 Java虚拟机
    • ClassLoader类加载器
    • ByteCode字节码
  • 3 原版 CoreMod
    • 直接修改class文件
    • JavaAgent
    • LaunchWrapper
    • ModLauncher
  • 4 FML CoreMod
    • 1.3.2-1.5.2
    • 1.6.1-1.12.2
    • 1.13.2-1.15.2
  • 5 Mixin
    • 配置
    • 引导
    • 注入
    • 修改
    • 定位
    • 融合
    • 扩展
    • 调试
  • 6 ASM
  • 附录
    • 附录A 相关工具下载
    • 附录B 常见Java字节码指令表
    • 附录C 参考资料
Powered by GitBook
On this page
  • 引导的时机
  • TweakClass or FMLCorePlugin?
  • 在Tweaker-Mod中使用@Mod注解

Was this helpful?

  1. 5 Mixin

引导

Previous配置Next注入

Last updated 5 years ago

Was this helpful?

有关Mixin引导部分的说明可以参考 。

由于Mixin是为CoreMod服务的,所以Mixin需要使用CoreMod引导。 由于LiteLoader和Fabric已经做好了引导工作,因此不需要由模组手动引导。 也可以使用-javaagent参数引导(参见)。

引导的时机

借用Mixin wiki的一张图来说明这个问题:

所以,Mixin需要在ITweaker中的acceptOptions或者injectIntoClassLoader中引导。一个基本的Mixin引导方法如下所示:

package com.example;

import java.io.File;
import java.util.List;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.LaunchClassLoader;
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"); // 添加自己的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]; }
}

TweakClass or FMLCorePlugin?

我们知道,FML提供了一个对ITweaker的封装接口,也就是IFMLLoadingPlugin,但是,MixinBootstrap不能在这里被直接调用

为什么?

java.lang.NoClassDefFoundError: org/spongepowered/asm/service/IMixinService
    at org.spongepowered.asm.service.MixinService.initService(MixinService.java:120)
    at org.spongepowered.asm.service.MixinService.getServiceInstance(MixinService.java:111)
    at org.spongepowered.asm.service.MixinService.getService(MixinService.java:106)
    at org.spongepowered.asm.launch.MixinBootstrap.<clinit>(MixinBootstrap.java:77)
    at com.example.ExamplePlugin.injectData(ExamplePlugin.java:60)
    at net.minecraftforge.fml.relauncher.CoreModManager$FMLPluginWrapper.injectIntoClassLoader(CoreModManager.java:151)
    at net.minecraft.launchwrapper.Launch.launch(Launch.java:115)
    at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
Caused by: java.lang.ClassNotFoundException: org.spongepowered.asm.service.IMixinService
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:106)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 8 more

怎么办?

那就让IFMLLoadingPlugin实现类也用AppClassLoader加载一次。 不推荐这么做,因为这样打破了FML加载模组的规则

package com.example;

import java.util.Map;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.spongepowered.asm.launch.MixinBootstrap;
import org.spongepowered.asm.mixin.Mixins;

public class ExamplePlugin implements IFMLLoadingPlugin {
    public static void initMixin() {
        MixinBootstrap.init();
        Mixins.addConfiguration("mixins.example.json");
    }
    @Override public void injectData(Map<String, Object> data) {
        try {
            ClassLoader appClassLoader = Launch.class.getClassLoader();
            MethodUtils.invokeMethod(appClassLoader, true, "addURL", this.getClass().getProtectionDomain().getCodeSource().getLocation());
            MethodUtils.invokeStaticMethod(appClassLoader.loadClass(this.getClass().getName()), "initMixin");
        } catch (Exception e) {}
    }
    @Override public String[] getASMTransformerClass() { return null; }
    @Override public String getModContainerClass() { return null; }
    @Override public String getSetupClass() { return null; }
    @Override public String getAccessTransformerClass() { return null; }
}

在Tweaker-Mod中使用@Mod注解

在FMLCoreMod中,只需要在清单文件中添加FMLCorePluginContainsFMLMod属性就能让这个模组也能作为普通模组加载,但是在Tweaker-Mod中是不行的,按照加载流程,FML在读取到有TweakClass属性后就不再继续读取其他的属性了(也就是只有FMLAT属性会被读到,其他都会被忽略)。

所以,现在应该这样:

package com.example;

import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.List;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.LaunchClassLoader;
import net.minecraftforge.fml.relauncher.CoreModManager;
import org.apache.logging.log4j.LogManager;
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");
        CodeSource codeSource = this.getClass().getProtectionDomain().getCodeSource();
        if (codeSource != null) {
            URL location = codeSource.getLocation();
            try {
                File file = new File(location.toURI());
                if (file.isFile()) {
                    CoreModManager.getReparseableCoremods().remove(file.getName());
                }
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
        } else {
            LogManager.getLogger().warn("No CodeSource, if this is not a development environment we might run into problems!");
            LogManager.getLogger().warn(this.getClass().getProtectionDomain());
        }
    }
    @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]; }
}

现在,就可以让这个模组的@Mod注解也被识别了。

因为Mixin自带一个 需要被添加,所以需要在LaunchWrapper还在调用实现ITweaker的类的阶段引导Mixin,也就是上图的过程①,所以需要在CoreMod入口类合适的方法中引导。

再回顾LaunchWarpper的,因为Mixin会在TweakClasses中,所以不能在循环tweakClassNames时(也就是在Tweaker的构造方法中)引导Mixin,否则会抛出java.util.ConcurrentModificationException。

因为IFMLLoadingPlugin的实现类是通过LaunchClassLoader加载的,ITweaker的实现类是通过sun.misc.Launcher$AppClassLoader加载的,而Mixin引导相关的类都会通过AppClassLoader加载(参见),所以如果直接调用,会抛出如下异常:

按照的,只需要获取到CoreModManager类中的ignoredModFiles字段,并从中移除模组自身文件名即可。

Tweaker
加载流程
添加一个Tweaker
MixinServiceLaunchWrapperBootstrap
ReplayMod
方法
Introduction to Mixins The Mixin Environment
MixinAgent