FIXME //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.// :!: //A rewrite of this page is planned and will be drafted in the foreseeable future, whilst some quicker changes and improvements may be applied to this page in the process, more fundamental structural changes will happen as part of a broader rewrite and replacement by a new version of the page// ====== Mixin Examples ====== This is a collection of frequently used mixins. This page is intended as a cheat sheet. See [[tutorial:mixin_introduction|Mixin Introduction]] if you haven't already. ===== Mixing into a private inner class ===== 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 } ===== Access the this instance of the class your mixin is targeting ===== 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; } } ===== Injecting into the head of a static block ===== Mixin: @Inject(method = "", at = @At("HEAD")) private void injected(CallbackInfo ci) { doSomething3(); } Result: static { + injected(new CallbackInfo(“”, false)); doSomething1(); doSomething2(); } ===== Injecting into the head of a method ===== 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(); } ===== Injecting into the tail of a method ===== 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)); } ===== Injecting into the returns of a method ===== 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)); } ===== Injecting into the point before a method call ===== 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(); } ===== Injecting into the point after a method call ===== 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(); } ===== Injecting into the point with shift amount ===== 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)); } ===== Injecting with a slice ===== 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(); } } ===== Injecting and cancelling ===== 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(); } ===== Injecting and cancelling with a return value ===== Mixin: @Inject(method = "foo()I;", at = @At("HEAD"), cancellable = true) private void injected(CallbackInfoReturnable cir) { cir.setReturnValue(3); } Result: public int foo() { + CallbackInfoReturnable cir = new CallbackInfoReturnable("foo", true); + injected(cir); + if (cir.isCancelled()) return cir.getReturnValue(); doSomething1(); doSomething2(); doSomething3(); return 10; } ===== Capturing local values ===== ==== Capture locals without MixinExtras ==== 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); } ==== Capture locals with MixinExtras ==== :!: See the oficial MixinExtra's [[https://github.com/LlamaLad7/MixinExtras/wiki/Local|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); } ==== Capturing one of multiple locals of a type ==== 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); } ===== Modifying locals ===== 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 localRef) { localRef.set(localRef.get() + " - modified") } Result: public void foo() { String s = "example string"; doSomething(); + s = s + " - modified"; doSomething2(s); } ===== Modifying a return value ===== Mixin: @Inject(method = "foo()I;", at = @At("RETURN"), cancellable = true) private void injected(CallbackInfoReturnable cir) { cir.setReturnValue(cir.getReturnValue() * 3); } Result: public int foo() { doSomething1(); doSomething2(); - return doSomething3() + 7; + int i = doSomething3() + 7; + CallbackInfoReturnable cir = new CallbackInfoReturnable("foo", true, i); + injected(cir); + if (cir.isCancelled()) return cir.getReturnValue(); + return i; } ===== Redirecting a method call ===== 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(); } ===== Redirecting a get field ===== 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(); } } ===== Redirecting a put field ===== 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(); } } ===== Modifying an argument ===== 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(); } ===== Modifying multiple arguments ===== 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(); } ===== Modifying a parameter ===== 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(); } ===== Modifying a local variable on an assignment ===== 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(); } ===== Modifying a constant ===== 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); } }