====== Mixin 示例 ======
收录了较为常见的 Mixin 示例。此页面旨在帮助开发者快速学习 Mixin 的使用,建议在开始之前先查看 [[zh_cn:tutorial:mixin_introduction|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 = "", at = @At("HEAD"))
private void injected(CallbackInfo ci) {
doSomething3();
}
结果:
static {
+ injected(new CallbackInfo(“”, 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 cir) {
cir.setReturnValue(3);
}
结果:
public int foo() {
+ CallbackInfoReturnable cir = new CallbackInfoReturnable("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 cir) {
cir.setReturnValue(cir.getReturnValue() * 3);
}
结果:
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;
}
===== 重定向一个方法调用 =====
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);
}
}