> For the complete documentation index, see [llms.txt](https://xfl03.gitbook.io/coremodtutor/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://xfl03.gitbook.io/coremodtutor/5/5.2.md).

# 引导

有关Mixin引导部分的说明可以参考 [Introduction to Mixins The Mixin Environment](https://github.com/SpongePowered/Mixin/wiki/Introduction-to-Mixins---The-Mixin-Environment)。

由于Mixin是为CoreMod服务的，所以Mixin需要使用CoreMod引导。\
由于LiteLoader和Fabric已经做好了引导工作，因此不需要由模组手动引导。\
也可以使用-javaagent参数引导（参见[MixinAgent](https://github.com/SpongePowered/Mixin/blob/2c72246fe0b67c145db5510e66c8873df18b5a9f/src/agent/java/org/spongepowered/tools/agent/MixinAgent.java#L208)）。

## 引导的时机

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

![](https://raw.githubusercontent.com/SpongePowered/Mixin/master/docs/images/mixin_env_2.png)

因为Mixin自带一个 [Tweaker](https://github.com/SpongePowered/Mixin/blob/master/src/launchwrapper/java/org/spongepowered/asm/mixin/EnvironmentStateTweaker.java) 需要被添加，所以需要在LaunchWrapper还在调用实现`ITweaker`的类的阶段引导Mixin，也就是上图的过程①，所以需要在CoreMod入口类合适的方法中引导。

再回顾LaunchWarpper的[加载流程](/coremodtutor/3-yuan-ban-coremod/3.3.md)，因为Mixin会在`TweakClasses`中[添加一个Tweaker](https://github.com/SpongePowered/Mixin/blob/2c72246fe0b67c145db5510e66c8873df18b5a9f/src/launchwrapper/java/org/spongepowered/asm/service/mojang/MixinServiceLaunchWrapper.java#L147)，所以不能在循环`tweakClassNames`时（也就是在Tweaker的构造方法中）引导Mixin，否则会抛出`java.util.ConcurrentModificationException`。

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

```java
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`不能在这里被**直接调用**

#### 为什么？

因为`IFMLLoadingPlugin`的实现类是通过`LaunchClassLoader`加载的，`ITweaker`的实现类是通过`sun.misc.Launcher$AppClassLoader`加载的，而Mixin引导相关的类都会通过`AppClassLoader`加载（参见[MixinServiceLaunchWrapperBootstrap](https://github.com/SpongePowered/Mixin/blob/2c72246fe0b67c145db5510e66c8873df18b5a9f/src/launchwrapper/java/org/spongepowered/asm/service/mojang/MixinServiceLaunchWrapperBootstrap.java#L53)），所以如果直接调用，会抛出如下异常：

```
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`加载一次。\
\&#xNAN;*不推荐这么做，因为这样打破了FML加载模组的规则*

```java
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`属性会被读到，其他都会被忽略）。

按照[ReplayMod](https://replaymod.com/)的[方法](https://github.com/ReplayMod/ReplayMod/blob/ec50efec104c8345a3a4f20fd44cfb3608cd81bb/src/main/java/com/replaymod/core/LoadingPlugin.java#L26-L42)，只需要获取到`CoreModManager`类中的`ignoredModFiles`字段，并从中移除模组自身文件名即可。

所以，现在应该这样：

```java
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`注解也被识别了。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xfl03.gitbook.io/coremodtutor/5/5.2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
