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)
Section titled “Access Transformers (ATs)”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.
Access Modifiers
Section titled “Access Modifiers”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 packageprotected— Accessible within the same package and by subclassesdefault— Accessible only within the same packageprivate— 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)
Targeting Elements
Section titled “Targeting Elements”Classes
Section titled “Classes”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$InnerClassFields
Section titled “Fields”To target a field:
<access modifier> <fully qualified class name> <field name>Methods
Section titled “Methods”Methods require a full JVM method descriptor, including parameter and return types:
<access modifier> <fully qualified class name> <method name>(<parameter types>)<return type>Type Descriptors
Section titled “Type Descriptors”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
Primitive Types
Section titled “Primitive Types”B—byteC—char(Unicode character code point in UTF-16)D—doubleF—floatI—intJ—longS—shortZ—boolean
Arrays
Section titled “Arrays”[— Indicates one dimension of an array- Example:
[[Srepresentsshort[][]
- Example:
Reference Types
Section titled “Reference Types”L<class name>;— Reference type using internal JVM naming- Example:
Ljava/lang/String;representsjava.lang.String
- Example:
Methods
Section titled “Methods”(<params>)<return>— Method descriptor format Example:(I)Z— Takes anint, returnsbooleanV— Void return type (only valid as a return type) Example:()V— No parameters, returns nothing
Mixins
Section titled “Mixins”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
Enabling Mixins in a Horizon Plugin
Section titled “Enabling Mixins in a Horizon Plugin”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.
Mixin Configuration File
Section titled “Mixin Configuration File”This is an example of a SpongePowered Mixin configuration file:
{ "required": true, "package": "io.example.myplugin.mixin", "mixins": [ "ExampleMixin", "AnotherExampleMixin" ]}Important Configuration Fields
Section titled “Important Configuration Fields”-
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.
Writing a Mixin
Section titled “Writing a Mixin”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.
- All Horizon internals, except
-
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.