User Tools

Site Tools


tutorial:mixin_injects

This is an old revision of the document!


FIXME This page is due rewrites because of inaccuracies. This page may change very suddenly and should be taken with a grain of salt.

@Inject

Introduction

In the context of Mixins, when talking about injects, it is typically in reference to the specific @Inject injector(i.e. “an inject”). This is not to be confused with injectors as a whole or the general process of injection, which include other injectors such as @ModifyArg.

Inject is included in “stock” or “base” Mixins, meaning it does not come from an additional library.

It is arguably the most intuitively understandable injector, and a detailed example can be found here.

Utility

@Inject primarily injects a call to the annotated method at the specified point in the target method. It is also able to inject early return statements by cancelling

This makes @Inject primarily useful for “plain” injections of additional instructions, and maybe optionally injecting an early return where needed. Whilst versatile, it is often overused compared to other injectors that may be better-suited for the situation. Some generic use-cases where @Inject would be fitting may be:

  • Listening to a specific method's operations, optionally cancelling. This can be used to create events or an injector with a purpose similar to an event.
    • Note that depending on the specific operation, some injectors able to get more context about individual operations may be more suitable.
  • Adding a consistent operation at a part of a target method, without the intent of replacing any of the existing operations.
  • Populating fields added by the Mixin class used, via an injection at the tail of the target class's constructor.

It is important to not use @Inject if the goal can be achieved with a more precise and less intrusive change.

Structure

A barebones structure of @Inject would look as follows:

@Inject(method = "TARGET METHOD NAME OR SIGNATURE", at = @At(value = "INJECTION POINT"), cancellable = <false BY DEFAULT>)
private void injectedHandlerMethod(<TARGET METHOD PARAMETERS>, CallbackInfo info) {
 
}

Note that the value = is optional if @At only contains an injection point, such as when the injection point does not need to specify further via a target.

Most of the attributes of @Inject are used by other injectors, but as this page may be read by people unfamiliar with general injector syntax, this page will go into some additional detail on their contents.

Target method

The method attribute specifies which method in the target class to inject into. If the method has no overloads, the name is enough. Otherwise, a more specific descriptor is required, consisting of the name followed by (<PARAMETER DESCRIPTORS>)<RETURN TYPE DESCRIPTOR>. See the target section for JVM descriptors.

It should be noted that for both target methods and injection point targets, the MCDev IntelliJ plugin provides autocompletion to get around the need of manually entering the method descriptors.

Targeting Constructors

Constructors include the “static constructor”, which can be targeted by inputting “<clinit>” as the target method, and the target class's constructors.

The static constructor is the method which internally is in charge of declaring all static fields in a class. Targeting it may be relevant when wanting to target a specific field. It should however be noted that “static initializers” (in Java, formed with static {} code blocks) of a Mixin class are merged into the target class aswell, and they may be used rather than injecting into <clinit> at certain times.

Non-static constructors are targeted by inputting “<init>” as the method name. If there are multiple constructors, the same approach to specification via JVM descriptors applies, with a return type of V. Such that a no-args constructor would be referenced by “<init>()V”.

:!: Fabric's Mixin fork allows to inject at any point in a constructor method, however it may be the case when developing using other forks or an upstream version that injection points are limited to RETURN or TAIL. This will not be the case when developing on Fabric or other modding frameworks using Fabric's Mixin fork.

Injection Point

The injection point defines what instructions to inject at in the target method. The following table describes a few of the options:

Name Description
HEAD Top of the method
RETURN Before every return instruction
INVOKE At a method call
TAIL Before the final return instruction

See the Mixin Injection Point Reference for a more thorough list of Mixin's stock injection points.

Injection Point Target

In the case of injection points that target calls, statements or members, such as INVOKE or FIELD; the injection point's complementary target can be specified via the format of: @At(value = “INJECTION POINT”, target = “TARGET VALUE”). Target values are specified using JVM bytecode descriptors.

Oracle defines the following field descriptors:

Descriptor Primitive Description
B byte signed byte
C char Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
D double double-precision floating-point value
F float single-precision floating-point value
I int signed integer
J long signed long integer
LClassName; reference an instance of ClassName
S short signed short
Z boolean true or false
[ reference one array dimension

A method descriptor is comprised of the method name, followed by a set of parentheses containing the parameter types, followed by the return type. A method defined in Java as Object m(int i, double[] d, Thread t) would have the method descriptor m(I[DLjava/lang/Thread;)Ljava/lang/Object;.

In the case that the return type is void, you need to use V (Void Descriptor Type) as the type (for example, void foo(String bar) would become foo(Ljava/lang/String;)V).

Generics' types are left out, as generics are stripped at runtime. So Pair<Integer, ? extends Task<? super VillagerEntity>‍> would become Lcom/mojang/datafixers/util/Pair.

Furthermore, when using a descriptor for a target complementing an injection point, the descriptor form of the class path containing the method, followed by a ; character prefixes the method name.

Handler Method

A handler method, often abbreviated to just “handler”, is the method annotated by the injector and called by the injected instructions.

@Inject handler methods always return void. The method name does not functionally matter; using something that describes what the inject does is best. The access modifier typically does not matter either. The target method's arguments are placed first in the method's header, followed by a CallbackInfo parameter if the target method returns void. If the target method has a return type R, CallbackInfoReturnable<R> is used instead of CallbackInfo.

Cancelling

@Inject is natively able to cancel the target method by injecting an early return statement. This can be done using the CallbackInfo parameter and calling cancel() on it, whilst setting the @Inject to have a cancellable = true attribute. In the case of a CallbackInfoReturnable<R> parameter, the return value must be set instead via setReturnValue(R).

This is also possible for other injectors via MixinExtras' @Cancellable annotation.

tutorial/mixin_injects.1764512401.txt.gz · Last modified: 2025/11/30 14:20 by gauntrecluse