Skip to content

Mixins and ATs

For working with Mixins and ATs, it is HIGHLY recommended that you use IntelliJ IDEA with the Minecraft Development plugin. This plugin will help autofill entries and such that will be extremely useful for development.

Access Transformers (ATs) are used to widen member visibility and to add or remove the final modifier from classes, methods, and fields. They allow plugin developers to access and modify members that would otherwise be inaccessible.

AT files are parsed line-by-line at startup and applied during class transformation at load time.

Each definition begins with an access modifier that defines the new visibility of the targeted member. Modifiers are listed below in decreasing order of visibility:

  • public — Accessible from all classes, regardless of package
  • protected — Accessible within the same package and by subclasses
  • default — Accessible only within the same package
  • private — Accessible only within the declaring class

In addition, the special suffixes +f and -f may be appended to any access modifier to respectively add or * remove* the final modifier.

The final modifier prevents:

  • Subclassing (for classes)
  • Method overriding (for methods)
  • Field reassignment (for fields)

To target a class, use the following syntax:

<access modifier> <fully qualified class name>

Inner classes are referenced by joining the outer and inner class names using $ as a separator:

com.example.OuterClass$InnerClass

To target a field:

<access modifier> <fully qualified class name> <field name>

Methods require a full JVM method descriptor, including parameter and return types:

<access modifier> <fully qualified class name> <method name>(<parameter types>)<return type>

ATs use standard JVM type descriptors as defined in more technical detail here - Java Virtual Machine Specification, SE 8, sections 4.3.2 and 4.3.3

  • Bbyte
  • Cchar (Unicode character code point in UTF-16)
  • Ddouble
  • Ffloat
  • Iint
  • Jlong
  • Sshort
  • Zboolean
  • [ — Indicates one dimension of an array
    • Example: [[S represents short[][]
  • L<class name>; — Reference type using internal JVM naming
    • Example: Ljava/lang/String; represents java.lang.String
  • (<params>)<return> — Method descriptor format Example: (I)Z — Takes an int, returns boolean
  • V — Void return type (only valid as a return type) Example: ()V — No parameters, returns nothing

Horizon comes packaged with the latest Fabric-SpongePowered Mixin library and MixinExtras. Mixin is the core package and system included in Horizon, and is the main system plugins can use in Horizon to modify the server internals.

Mixins allow you to inject, redirect, overwrite, or otherwise modify bytecode of existing classes without directly patching or forking the upstream server codebase

To enable mixins for a plugin, declare one or more mixin configuration files in your plugin metadata:

horizon:
mixins:
- "mixins.example.json"

Each entry must reference a mixin configuration file packaged in your plugin JAR resources

If the horizon.mixins block is present, Horizon will treat the plugin as mixin-capable and process its mixins during bootstrap.

This is an example of a SpongePowered Mixin configuration file:

{
"required": true,
"package": "io.example.myplugin.mixin",
"mixins": [
"ExampleMixin",
"AnotherExampleMixin"
]
}
  • package

    • Base Java package containing all mixin classes declared in this configuration.
  • mixins

    • A list of mixin class names (relative to package) that apply in the Horizon environment.
  • required

    • If true, Horizon will fail fast if any mixin in this config fails to apply.

A basic mixin class looks like the following:

@Mixin(MinecraftServer.class)
public abstract class MinecraftServerMixin {
@Inject(
method = "runServer",
at = @At("HEAD")
)
private void horizon$onServerStart(CallbackInfo ci) {
// injected code here
}
}

The Mixin above injects code into the MinecraftServer#runServer method at the HEAD of the method. Mixins can target classes, methods, constructors, and <client>. Horizon bundles MixinExtras with the server, allowing more enhanced and expanded capabilities with Mixin. It is recommended to read over the Mixin and MixinExtras documentation pages linked below.

Mixins and ATs are very complementary, with ATs modifying the access and finality of fields, methods, and classes, and Mixins injecting and modifying code in the server.

Due to Horizon plugins and server internals being loaded by the Ember classloader, Mixins can target all server internals and some library internals. There are however, some restrictions to what places you can inject into. For example, you cannot inject into Horizon internals, for obvious reasons. The full list of excluded packages is below:

  • io.canvasmc.horizon.*

    • All Horizon internals, except io.canvasmc.horizon.inject.*. This exception allows Horizon’s own internal mixin/injection logic to function while protecting the rest of the framework from external mutation.
  • org.tinylog.*, org.slf4j.*, org.apache.logging.log4j.*

    • Logging libraries used by Horizon and NMS. Transforming logging frameworks can cause insane issues, and as such is prohibited.
  • org.spongepowered.asm.*, com.llamalad7.mixinextras.*

    • SpongePowered Mixin core implementation and MixinExtras. Transforming Mixin itself is unsafe and will almost certainly break class transformation.

Horizon plugins cannot transform classes in non-Horizon plugins due to classloader constraints. Horizon plugins CAN transform classes by other Horizon plugins, and packages not excluded by Horizon.

Mixin and MixinExtras Technical Writeup Documentation

Section titled “Mixin and MixinExtras Technical Writeup Documentation”