Skip to content

Mixins on Minecraft Forge

Mumfrey edited this page May 31, 2023 · 5 revisions

Mixin now ships as a library with Minecraft Forge which means it is no longer necessary, as it was in legacy versions, to shade Mixin into your mod jar. In fact doing so will likely cause issues.

As explained in the Obfuscation and Mixins chapter, certain mixin features require special handling in order to cross the obfuscation boundary when you build your mod for production. This guide explains how to have your mixins function in the forge UserDev environment, and also how to configure your build so that obfuscation information is properly generated for your mixins.

This guide asssumes that you have already created a package to contain your mixins and a mixin config to declare the mixins and options.

We will make the following changes to the build.gradle in order to configure the project to be run in development, and to manage obfuscation

  1. Add the MixinGradle plugin.
  2. Add the Mixin Annotation Processor dependency.
  3. Configure options for MixinGradle via the mixin closure.

For the purposes of this guide, I will assume that your mixin config is named mixins.mymod.json and the refmap is named mixins.mymod.refmap.json and that you are using only the main SourceSet.

Step 1 - Adding the MixinGradle plugin

Open your build.gradle and locate the plugins block at the top of the file. The plugins block already contains the necessary plugins for the Forge MDK. We will add the MixinGradle plugin by adding the following line to end of the plugins block, before the closing curly brace:

id 'org.spongepowered.mixin' version '0.7.+'

The plugins block should now look like this:

plugins {
    id 'eclipse'
    id 'maven-publish'
    id 'net.minecraftforge.gradle' version '5.1.+'
    id 'org.spongepowered.mixin' version '0.7.+'
}

Step 2 - Adding the Mixin Annotation Processor

MixinGradle's job is to configure the Mixin Annotation Processor (AP), however the AP is not applied automatically, we need to add it as a dependency. The AP dependency should be the same version or newer as the version used in your project (if you're not sure what version of Mixin you're using, see the section at the end of this guide).

To add the Mixin AP dependency to your project locate the dependencies block for the project. This is not the dependencies block in the buildscript block at the top of your gradle file, but the larger dependencies block further down the file. For the purposes of this guide we will assume that the version is 0.8.5 (the current release version at the time of writing).

It should currently contain the minecraft dependency and a bunch of comments, as well as any other dependencies you've added manually. The AP configuration for main is called annotationProcessor. Add the Mixin AP dependency to the dependencies block:

dependencies {
    // Specify the version of Minecraft to use. If this is any group other than 'net.minecraft', it is assumed
    // that the dep is a ForgeGradle 'patcher' dependency, and its patches will be applied.
    // The userdev artifact is a special name and will get all sorts of transformations applied to it.
    minecraft 'net.minecraftforge:forge:1.17.1-37.0.70'

    // Apply Mixin AP
    annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'

Notice that the AP dependency has the additional classifier processor. This is a special fat jar which contains the upstream dependencies required by the Mixin AP so that you don't have to specify them all by hand.

Step 2.1 - If you have additional SourceSets containing mixins

If you only have one SourceSet main, you can skip this section.

Additional SourceSets will have corresponding gradle configurations which define their dependencies. If you have additional SourceSets which contain mixins then each will require its own Reference Map (more on that below) and each SourceSet configuration will require the Mixin AP dependency.

Let's assume you have additional SourceSets called client and api. The corresponding configuration names will be clientAnnotationProcessor and apiAnnotationProcessor, add the Mixin AP to each:

dependencies {
    // Specify the version of Minecraft to use. If this is any group other than 'net.minecraft', it is assumed
    // that the dep is a ForgeGradle 'patcher' dependency, and its patches will be applied.
    // The userdev artifact is a special name and will get all sorts of transformations applied to it.
    minecraft 'net.minecraftforge:forge:1.17.1-37.0.70'

    annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
    clientAnnotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
    apiAnnotationProcessor 'org.spongepowered:mixin:0.8.5:processor'

Step 3 - Configuring MixinGradle

MixinGradle provides an extension called mixin which allows us to configure the options for the plugin, MixinGradle will then apply these settings in the appropriate places. To configure the extension specify the extension name and a closure which will contain our settings. This can be placed anywhere in your build, but I recommend placing it after your sourceSets or dependencies blocks:

mixin {
    // MixinGradle Settings
}

Important Settings

MixinGradle can configure the AP options, but we need to tell it the name of the refmap to generate for each compile task, this must correspond to the refmap name in the config JSON.

Note that configs in the same SourceSet should all specify the same refmap name, regardless of which mixins they contain. This is because refmaps are coupled to the compile task for the SourceSet and don't care about the organisation of mixins within your configs.

We can also specify the name of our mixin config, this will perform two tasks:

  • The config will be injected into all of our run configurations
  • The config name will be added to the manifest of all obfuscated jars (in the MixinConfigs key)

Let's add the refmap name for our main SourceSet and our config name to the mixin closure:

mixin {
    // MixinGradle Settings
    add sourceSets.main, 'mixins.mymod.refmap.json'
    config 'mixins.mymod.json'
}

Useful Settings

The mixin closure can also be used to configure options such as Mixin System Properties (for dev runs) and options for the Mixin Annotation Processor. For example I recommend setting the mixin.debug.verbose and mixin.debug.export during development. We can set these properties in the mixin closure for convenience:

mixin {
    // MixinGradle Settings
    add sourceSets.main, 'mixins.mymod.refmap.json'
    config 'mixins.mymod.json'

    debug.verbose = true
    debug.export = true
}

Remember to regenerate runs using the genEclipseRuns, genIntellijRuns or genVSCodeRuns task after changing any settings which affect run configurations!

Step 4.1 - If you have additional SourceSets containing mixins

If you only have one SourceSet main, you can skip this section.

As well as gradle configurations, each SourceSet in your project gets a corresponding Java compile task which is named after the SourceSet. Since each SourceSet is compiled separately, the AP will run separately each time, hence why each SourceSet requires a separate Reference Map (refmap). Using the example above, let's add refmaps names and configs for our client and api SourceSets:

mixin {
    add sourceSets.main, 'mixins.mymod.refmap.json'
    add sourceSets.client, 'mixins.mymod.client.refmap.json'
    add sourceSets.api, 'mixins.mymod.api.refmap.json'

    config 'mixins.mymod.json'
    config 'mixins.mymod.client.json'
    config 'mixins.mymod.api.json'
}

Annotation Processor Options

MixinGradle configures the AP with all of the required options. However, the Mixin AP options can be manipulated via the mixin closure. Some useful options are:

boolean disableTargetValidator
Disables the target validator (validates that the mixin targets are sane (eg. superclass exists within target hierarchy)
boolean disableOverwriteChecker
Disables the overwrite checker which ensures @Overwrite method javadoc contains @author and @reason tags
String overwriteErrorLevel
Sets the error level for the overwrite checker (defaults to warning, can be set to error)
quiet
Suppresses the banner message and informational output from the AP
extraMappings
Specifies the name of an additional (custom) TRSG mapping file to feed to the AP, this can be used for mapping entries not specified in the main mapping file
mixin {
    // AP Settings
    disableTargetValidator = true
    overwriteErrorLevel = 'error'
    quiet
    extraMappings file("my_custom_srgs.tsrg")
}    

Example mixin closure with options

Here is a complete example of what our mixin closure may look like once we've configured everything:

mixin {
    // Refmaps for each SourceSet
    add sourceSets.main, 'mixins.mymod.refmap.json'

    // Configs to add to runs and jars
    config 'mixins.mymod.json'

    // Specify options for dev run configs
    debug.verbose = true
    debug.export = true
    dumpTargetOnFailure = true

    // Options for the Annotation Processor
    quiet
}

How to find the Mixin version

If you're not sure which version of Mixin is in use in your project you can find it in several ways:

  1. In your IDE, check the list of project dependencies. In eclipse you can look in the Project and External Dependencies container for example.

  2. Use the gradle dependencies task to emit the dependency tree for your project to the console and filter it using find (on windows) or grep on linux:

    gradlew --console=plain dependencies | find "mixin"
    

    This should emit several lines into the console and it shouldn't be too hard to identify the mixin dependency version.

  3. Run the game in your development environment and then search the debug.log for the mixin subsystem version