Skip to main content
Essential Loader for LaunchWrapper provides Mixin 0.8.x support and Jar-in-Jar dependency management for Minecraft 1.8.9 through 1.12.2 (commonly called “legacy Forge” or “LaunchWrapper”).

Features

  • Mixin 0.8.x Support: Even on 1.8.9, with improved third-party mod compatibility on 1.12.2
  • Jar-in-Jar Dependencies: Automatic version selection when multiple mods ship different versions of the same dependency
  • MixinExtras Support: Automatically initialized without requiring MixinExtrasBootstrap

Setup

1

Add Essential Repository

Add the Essential Maven repository to your build script:
build.gradle.kts
repositories {
    maven("https://repo.essential.gg/repository/maven-public/")
}
2

Configure Dependencies

Create an embed configuration and add Essential Loader as a dependency:
build.gradle.kts
val embed by configurations.creating
configurations.implementation.get().extendsFrom(embed)

dependencies {
    embed("gg.essential:loader-launchwrapper:1.3.2")
}
3

Configure Jar Task

Embed Essential Loader into your mod jar and set the TweakClass:
build.gradle.kts
tasks.jar {
    // Embed the contents of the Essential Loader jar into your mod jar
    dependsOn(embed)
    from(embed.files.map { zipTree(it) })
    
    // Set Essential Loader as the Tweaker for your mod
    manifest.attributes(mapOf(
        "TweakClass" to "gg.essential.loader.stage0.EssentialSetupTweaker"
    ))
}
If you already have a custom Tweaker, you can have it extend EssentialSetupTweaker instead of replacing it.
If you previously used the Mixin Tweaker, you can simply use Essential Loader instead. It automatically initializes Mixin for any mod with a MixinConfigs attribute.
4

Create essential.mod.json

Create a essential.mod.json file in your src/main/resources folder:
{
  "id": "your_mod_id_goes_here",
  "version": "1.0.0",
  "jars": [
    {
      "id": "com.example:examplelib",
      "version": "0.1.0",
      "file": "META-INF/jars/examplelib-0.1.0.jar"
    }
  ]
}

Jar-in-Jar Dependencies

Automate the inclusion of Jar-in-Jar dependencies based on Gradle dependencies:
1

Create JiJ Configuration

build.gradle.kts
val jij by configurations.creating
configurations.implementation.get().extendsFrom(jij)

dependencies {
    // Add all the dependencies you wish to jar-in-jar to the custom `jij` configuration
    jij("com.example:examplelib:0.1.0")
}
2

Configure processResources Task

Auto-generate the version and jars entries:
build.gradle.kts
tasks.processResources {
    val expansions = mapOf(
        "version" to version,
        "jars" to provider {
            jij.resolvedConfiguration.resolvedArtifacts.joinToString(",\n") { artifact ->
                val id = artifact.moduleVersion.id
                """
                    {
                        "id": "${id.group}:${id.name}",
                        "version": "${id.version}",
                        "file": "META-INF/jars/${artifact.file.name}"
                    }
                """.trimIndent()
            }
        },
    )
    inputs.property("expansions", expansions)
    filesMatching("essential.mod.json") {
        expand(expansions)
    }
}
3

Update Jar Task

Include the dependency jars in your mod jar:
build.gradle.kts
tasks.jar {
    dependsOn(jij)
    from(jij.files) {
        into("META-INF/jars")
    }
}
Essential Loader will automatically select the more recent version when two mods ship different versions of the same dependency.

Mixin 0.8.x Integration

To use Mixin 0.8.x with your mod:
1

Setup Annotation Processor

Configure Mixin’s annotation processor and refmap generation via your build system. This is done the same way as with stock Mixin 0.8.x or 0.7.10.
Essential Loader only affects things at runtime. Build-time configuration remains unchanged.
2

Add Mixin as JiJ Dependency

build.gradle.kts
dependencies {
    jij("gg.essential:mixin:0.2.0+mixin.0.8.7")
}
3

Configure MixinConfigs Attribute (Optional)

If not using a custom Tweaker, add the MixinConfigs attribute to your manifest:
build.gradle.kts
tasks.jar {
    manifest.attributes(mapOf(
        "TweakClass" to "gg.essential.loader.stage0.EssentialSetupTweaker",
        "MixinConfigs" to "yourmod.mixins.json"
    ))
}

MixinExtras Support

MixinExtras is automatically initialized by Essential Loader:
build.gradle.kts
dependencies {
    // Essential Loader will automatically initialize MixinExtras
    // No need to call `MixinExtrasBootstrap`
    jij(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")!!)
    
    // Or if you've previously used Essential's relocated MixinExtras version:
    jij(annotationProcessor("gg.essential.lib:mixinextras:0.4.0")!!)
}
annotationProcessor is necessary to generate refmaps if your build system does not setup MixinExtras for you.

Custom Tweaker Integration

If you have an existing custom Tweaker:
package com.example.mod.tweaker;

import gg.essential.loader.stage0.EssentialSetupTweaker;
import net.minecraft.launchwrapper.LaunchClassLoader;

public class MyCustomTweaker extends EssentialSetupTweaker {
    @Override
    public void injectIntoClassLoader(LaunchClassLoader classLoader) {
        super.injectIntoClassLoader(classLoader);
        // Your custom logic here
    }
}

Complete Example

Here’s a complete build.gradle.kts example:
build.gradle.kts
repositories {
    maven("https://repo.essential.gg/repository/maven-public/")
}

val embed by configurations.creating
val jij by configurations.creating
configurations.implementation.get().extendsFrom(embed, jij)

dependencies {
    // Essential Loader
    embed("gg.essential:loader-launchwrapper:1.3.2")
    
    // Mixin 0.8.x
    jij("gg.essential:mixin:0.2.0+mixin.0.8.7")
    
    // MixinExtras (optional)
    jij(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")!!)
    
    // Your JiJ dependencies
    jij("com.example:examplelib:0.1.0")
}

tasks.processResources {
    val expansions = mapOf(
        "version" to version,
        "jars" to provider {
            jij.resolvedConfiguration.resolvedArtifacts.joinToString(",\n") { artifact ->
                val id = artifact.moduleVersion.id
                """
                    {
                        "id": "${id.group}:${id.name}",
                        "version": "${id.version}",
                        "file": "META-INF/jars/${artifact.file.name}"
                    }
                """.trimIndent()
            }
        },
    )
    inputs.property("expansions", expansions)
    filesMatching("essential.mod.json") {
        expand(expansions)
    }
}

tasks.jar {
    // Embed Essential Loader
    dependsOn(embed)
    from(embed.files.map { zipTree(it) })
    
    // Include JiJ dependencies
    dependsOn(jij)
    from(jij.files) {
        into("META-INF/jars")
    }
    
    manifest.attributes(mapOf(
        "TweakClass" to "gg.essential.loader.stage0.EssentialSetupTweaker",
        "MixinConfigs" to "yourmod.mixins.json"
    ))
}

Platform Details

Background

LaunchWrapper is what Forge 1.8.9 - 1.12.2 are based on. While technically independent from Forge (published by Mojang), stage0 is completely independent from Forge, and stage1 has separate code paths for pure LaunchWrapper vs LaunchWrapper+Forge.

Entrypoint

LaunchWrapper’s main entry point is an ITweaker class. These are discovered via:
  1. First Round: Command line arguments (includes Forge)
  2. Second Round: Forge looks at the TweakClass property in MANIFEST.MF files of jars in the mods folder
Forge’s CoreModManager queues these tweakers during early loading, which is where Essential Loader gets to run.

Relaunching

Relaunching is always required on 1.8.9 because the default ASM library is too old for Mixin 0.8.x needs. On 1.12.2, relaunching is used for simplicity and consistency.
Essential Loader uses a “relaunch” mechanism that creates a second, mostly clean, mostly isolated environment within the existing LaunchWrapper environment. This allows using more recent versions of certain libraries that would otherwise not be possible.

Troubleshooting

If you’re packaging Essential Loader for ModLauncher 9+, you must relocate stage0 to avoid package conflicts. For LaunchWrapper, relocation is optional but recommended.
Chain-loading production mods is trivial on LaunchWrapper: just add them to the classpath and Forge will discover them as usual.

Build docs developers (and LLMs) love