zh_cn:tutorial:mixin_examples
Table of Contents
Mixin 示例
收录了较为常见的 Mixin 示例。此页面旨在帮助开发者快速学习 Mixin 的使用,建议在开始之前先查看 Mixin介绍。
注入到内部私有类的一个方法
使用 targets 参数并添加 $
符号来指向内部类。
@Mixin(targets = "net.minecraft.client.render.block.BlockModelRenderer$AmbientOcclusionCalculator") public class AmbientOcclusionCalculatorMixin { // 为所欲为 ^ v ^ }
在 Mixin 类中获取目标类实例对象
注意:应该避免像这样两次强转 this
。如果你是要使用来自目标来的方法或字段,使用 @Shadow
。如果方法或字段是来自目标类的父类,可以直接将你的这个 mixin 继承那个父类。
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(); }
结果:
static { + injected(new CallbackInfo(“<clinit>”, false)); doSomething1(); doSomething2(); }
注入在一个方法的开头
Mixin:
@Inject(method = "foo()V", at = @At("HEAD")) private void injected(CallbackInfo ci) { doSomething4(); }
结果:
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(); }
结果:
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(); }
结果:
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(); }
结果:
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(); }
结果:
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(); }
结果:
public void foo() { doSomething1(); Something something = new Something(); something.doSomething(); doSomething2(); + injected(new CallbackInfo("foo", false)); }
使用 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(); }
结果:
public class Something { public void foo() { this.doSomething1(); + // 不会注入到此位置,因为它位于Slice范围之外 this.doSomething(); this.doSomething2(); + injected(new CallbackInfo("foo", false)); this.doSomething(); this.doSomething3(); + // 不会注入到此位置,因为它位于Slice范围之外 this.doSomething(); this.doSomething4(); } }
注入并直接使当前方法返回
Mixin:
@Inject(method = "foo()V", at = @At("HEAD"), cancellable = true) private void injected(CallbackInfo ci) { ci.cancel(); }
结果:
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); }
结果:
public int foo() { + CallbackInfoReturnable<Integer> cir = new CallbackInfoReturnable<Integer>("foo", true); + injected(cir); + if (cir.isCancelled()) return cir.getReturnValue(); doSomething1(); doSomething2(); doSomething3(); return 10; }
捕获局部变量
不用 MixinExtras 捕获局部变量
Mixin:
@Inject(method = "foo()V", at = @At(value = "TAIL"), locals = LocalCapture.CAPTURE_FAILHARD) private void injected(CallbackInfo ci, TypeArg1 arg1) { //CAPTURE_FAILHARD: 如果计算局部变量与预期不符则抛出一个异常。 arg1.doSomething4(); }
结果:
public void foo() { TypeArg1 arg1 = getArg1(); arg1.doSomething1(); arg1.doSomething2(); TypeArg2 arg2 = getArg2(); arg2.doSomething3(); + injected(new CallbackInfo("foo", false), arg1); }
使用 MixinExtras 捕获局部变量
MixinExtras 需要 Fabric Loader 0.15 或者以上版本,否则你需要在 build.gradle
中手动指定。
如果有多个同一类型的局部变量,你需要指定 ordinal
否则会抛出错误。
Mixin:
@Inject(method = "foo()V", at = @At(value = "TAIL")) private void injected(CallbackInfo ci, @Local TypeArg2 arg2) { arg1.doSomething4(); }
结果:
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) { arg1.doSomething4(); }
结果:
public void foo() { TypeArg arg1 = getArg1(); TypeArg arg2 = getArg2(); TypeArg arg3 = getArg3(); TypeArg arg4 = getArg4(); doSomething(); + injected(new CallbackInfo("foo", false), arg3); }
修改返回值
Mixin:
@Inject(method = "foo()I;", at = @At("RETURN"), cancellable = true) private void injected(CallbackInfoReturnable<Integer> cir) { cir.setReturnValue(cir.getReturnValue() * 3); }
结果:
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; }
结果:
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; }
结果:
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(); }
结果:
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; }
结果:
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); }
结果:
public void foo() { doSomething1(); Something something = new Something(); - something.doSomething(3, 2.5D, true); + // 事实上,自动生成的 Args 子类是在运行时被创建的, + // 但是我们抹去了一些细节以便于理解。 + 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; }
结果:
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; }
结果:
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; }
结果:
public void foo() { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < injected(4); i++) { doSomething(i); } }
zh_cn/tutorial/mixin_examples.txt · Last modified: 2023/12/18 02:19 by solidblock