The Fabric Wiki's Mixin segments is under heavy reviews, pages on the topic are subject to major edits or rewrites. This page in particular is noted as being unreliable to learn Mixin as a tool from. Learning by example should be used sparingly, as it may lead to a lack of understanding. Prioritize, if you are unable to learn through documentation, presenting your intention in detail in a Mixin support channel on the Fabric or SpongePowered Discord servers and discussing with experienced devs there.
This is a collection of frequently used mixins. This page is intended as a cheat sheet. See Mixin Introduction if you haven't already.
Use the targets parameter and a $
sign to get the inner class.
@Mixin(targets = "net.minecraft.client.render.block.BlockModelRenderer$AmbientOcclusionCalculator") public class AmbientOcclusionCalculatorMixin { // do your stuff here }
Note: Double casting this
should be avoided when possible. If you intend to use a method or field from the target class, use @Shadow
. If the method or field is from a parent of the target class, have your mixin extend the direct parent of the target class.
Mixin:
@Mixin(TargetClass.class) public class MyMixin extends EveryThingThatTargetClassExtends implements EverythingThatTargetClassImplements { @Inject(method = "foo()V", at = @At("HEAD")) private void injected(CallbackInfo ci) { TargetClass thisObject = (TargetClass)(Object)this; } }
Mixin:
@Inject(method = "<clinit>", at = @At("HEAD")) private void injected(CallbackInfo ci) { doSomething3(); }
Result:
static { + injected(new CallbackInfo(“<clinit>”, false)); doSomething1(); doSomething2(); }
Mixin:
@Inject(method = "foo()V", at = @At("HEAD")) private void injected(CallbackInfo ci) { doSomething4(); }
Result:
public void foo() { + injected(new CallbackInfo("foo", false)); doSomething1(); doSomething2(); doSomething3(); }
Mixin:
@Inject(method = "foo()V", at = @At("TAIL")) private void injected(CallbackInfo ci) { doSomething4(); }
Result:
public void foo() { doSomething1(); if (doSomething2()) { return; } doSomething3(); + injected(new CallbackInfo("foo", false)); }
Mixin:
@Inject(method = "foo()V", at = @At("RETURN")) private void injected(CallbackInfo ci) { doSomething4(); }
Result:
public void foo() { doSomething1(); if (doSomething2()) { + injected(new CallbackInfo("foo", false)); return; } doSomething3(); + injected(new CallbackInfo("foo", false)); }
Mixin:
@Inject(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething()V")) private void injected(CallbackInfo ci) { doSomething3(); }
Result:
public void foo() { doSomething1(); Something something = new Something(); + injected(new CallbackInfo("foo", false)); something.doSomething(); doSomething2(); }
Mixin:
@Inject(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething()V", shift = At.Shift.AFTER)) private void injected(CallbackInfo ci) { doSomething3(); }
Result:
public void foo() { doSomething1(); Something something = new Something(); something.doSomething(); + injected(new CallbackInfo("foo", false)); doSomething2(); }
Mixin:
@Inject(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething()V", shift = At.Shift.BY, by = 2)) private void injected(CallbackInfo ci) { doSomething3(); }
Result:
public void foo() { doSomething1(); Something something = new Something(); something.doSomething(); doSomething2(); + injected(new CallbackInfo("foo", false)); }
Mixin:
@Inject( method = "foo()V", at = @At( value = "INVOKE", target = "La/b/c/Something;doSomething()V" ), slice = @Slice( from = @At(value = "INVOKE", target = "La/b/c/Something;doSomething2()V"), to = @At(value = "INVOKE", target = "La/b/c/Something;doSomething3()V") ) ) private void injected(CallbackInfo ci) { doSomething5(); }
Result:
public class Something { public void foo() { this.doSomething1(); + // It will not inject into here because this is outside of the slice section this.doSomething(); this.doSomething2(); + injected(new CallbackInfo("foo", false)); this.doSomething(); this.doSomething3(); + // It will not inject into here because this is outside of the slice section this.doSomething(); this.doSomething4(); } }
Mixin:
@Inject(method = "foo()V", at = @At("HEAD"), cancellable = true) private void injected(CallbackInfo ci) { ci.cancel(); }
Result:
public void foo() { + CallbackInfo ci = new CallbackInfo("foo", true); + injected(ci); + if (ci.isCancelled()) return; doSomething1(); doSomething2(); doSomething3(); }
Mixin:
@Inject(method = "foo()I;", at = @At("HEAD"), cancellable = true) private void injected(CallbackInfoReturnable<Integer> cir) { cir.setReturnValue(3); }
Result:
public int foo() { + CallbackInfoReturnable<Integer> cir = new CallbackInfoReturnable<Integer>("foo", true); + injected(cir); + if (cir.isCancelled()) return cir.getReturnValue(); doSomething1(); doSomething2(); doSomething3(); return 10; }
Mixin:
@Inject(method = "foo()V", at = @At(value = "TAIL"), locals = LocalCapture.CAPTURE_FAILHARD) private void injected(CallbackInfo ci, TypeArg1 arg1) { //CAPTURE_FAILHARD: If the calculated locals are different from the expected values, throws an error. arg1.doSomething4(); }
Result:
public void foo() { TypeArg1 arg1 = getArg1(); arg1.doSomething1(); arg1.doSomething2(); TypeArg2 arg2 = getArg2(); arg2.doSomething3(); + injected(new CallbackInfo("foo", false), arg1); }
See the oficial MixinExtra's Wiki.
MixinExtras required Fabric Loader 0.15 or above, or you have to manually specify it in
build.gradle
.
If there are multiple locals with that type, you have to specify
ordinal
or it will throw an error.
the use of
@Local
is recommended over LocalCapture
Mixin:
@Inject(method = "foo()V", at = @At(value = "TAIL")) private void injected(CallbackInfo ci, @Local TypeArg2 arg2) { arg2.doSomething4(); }
Result:
public void foo() { TypeArg1 arg1 = getArg1(); arg1.doSomething1(); arg1.doSomething2(); TypeArg2 arg2 = getArg2(); arg2.doSomething3(); + injected(new CallbackInfo("foo", false), arg2); }
Mixin:
@Inject(method = "foo()V", at = @At(value = "TAIL")) private void injected(CallbackInfo ci, @Local(ordinal = 2) TypeArg arg) { arg.doSomething4(); }
Result:
public void foo() { TypeArg arg1 = getArg1(); TypeArg arg2 = getArg2(); TypeArg arg3 = getArg3(); TypeArg arg4 = getArg4(); doSomething(); + injected(new CallbackInfo("foo", false), arg3); }
This requires MixinExtras.
Mixin:
@Inject(method = "foo()V", at = @At(value = "INVOKE", target = "doSomething()V", shift = At.Shift.AFTER)) private static void injected(CallbackInfo ci, @Local LocalRef<String> localRef) { localRef.set(localRef.get() + " - modified") }
Result:
public void foo() { String s = "example string"; doSomething(); + s = s + " - modified"; doSomething2(s); }
Mixin:
@Inject(method = "foo()I;", at = @At("RETURN"), cancellable = true) private void injected(CallbackInfoReturnable<Integer> cir) { cir.setReturnValue(cir.getReturnValue() * 3); }
Result:
public int foo() { doSomething1(); doSomething2(); - return doSomething3() + 7; + int i = doSomething3() + 7; + CallbackInfoReturnable<Integer> cir = new CallbackInfoReturnable<Integer>("foo", true, i); + injected(cir); + if (cir.isCancelled()) return cir.getReturnValue(); + return i; }
Mixin:
@Redirect(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething(I)I")) private int injected(Something something, int x) { return x + 3; }
Result:
public void foo() { doSomething1(); Something something = new Something(); - int i = something.doSomething(10); + int i = injected(something, 10); doSomething2(); }
Mixin:
@Redirect(method = "foo()V", at = @At(value = "FIELD", target = "La/b/c/Something;aaa:I", opcode = Opcodes.GETFIELD)) private int injected(Something something) { return 12345; }
Result:
public class Something { public int aaa; public void foo() { doSomething1(); - if (this.aaa > doSomething2()) { + if (injected(this) > doSomething2()) { doSomething3(); } doSomething4(); } }
Mixin:
@Redirect(method = "foo()V", at = @At(value = "FIELD", target = "La/b/c/Something;aaa:I", opcode = Opcodes.PUTFIELD)) private void injected(Something something, int x) { something.aaa = x + doSomething5(); }
Result:
public class Something { public int aaa; public void foo() { doSomething1(); - this.aaa = doSomething2() + doSomething3(); + inject(this, doSomething2() + doSomething3()); doSomething4(); } }
Mixin:
@ModifyArg(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething(ZIII)V"), index = 2) private int injected(int x) { return x * 3; }
Result:
public void foo() { doSomething1(); Something something = new Something(); - something.doSomething(true, 1, 4, 5); + something.doSomething(true, 1, injected(4), 5); doSomething2(); }
Mixin:
@ModifyArgs(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething(IDZ)V")) private void injected(Args args) { int a0 = args.get(0); double a1 = args.get(1); boolean a2 = args.get(2); args.set(0, a0 + 3); args.set(1, a1 * 2.0D); args.set(2, !a2); }
Result:
public void foo() { doSomething1(); Something something = new Something(); - something.doSomething(3, 2.5D, true); + // Actually, synthetic subclass of Args is generated at runtime, + // but we omit the details to make it easier to understand the concept. + Args args = new Args(new Object[] { 3, 2.5D, true }); + injected(args); + something.doSomething(args.get(0), args.get(1), args.get(2)); doSomething2(); }
Mixin:
@ModifyVariable(method = "foo(ZIII)V", at = @At("HEAD"), ordinal = 1) private int injected(int y) { return y * 3; }
Result:
public void foo(boolean b, int x, int y, int z) { + y = injected(y); doSomething1(); doSomething2(); doSomething3(); }
Mixin:
@ModifyVariable(method = "foo()V", at = @At("STORE"), ordinal = 1) private double injected(double x) { return x * 1.5D; }
Result:
public void foo() { int i0 = doSomething1(); double d0 = doSomething2(); - double d1 = doSomething3() + 0.8D; + double d1 = injected(doSomething3() + 0.8D); double d2 = doSomething4(); }
Mixin:
@ModifyConstant(method = "foo()V", constant = @Constant(intValue = 4)) private int injected(int value) { return ++value; }
Result:
public void foo() { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < injected(4); i++) { doSomething(i); } }