====== 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.
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);
}
}