Generics’ types are left out, as generics don’t exist on runtime. So Pair<Integer, ? extends Task<? super VillagerEntity>> would become Lcom/mojang/datafixers/util/Pair.
@Mixin(TitleScreen.class) publicclassExampleMixin { @Inject(at = @At("HEAD"), method = "init()V") privatevoidinit(CallbackInfo info) { System.out.println("This line is printed by an example mod mixin!"); } }
Mixin Accessors & Invokers
Mixin Accessors & Invokers 是一种用于访问或修改非公共或 final 字段和方法的机制
@Mixin(targets = "net.minecraft.client.render.block.BlockModelRenderer$AmbientOcclusionCalculator") publicclassAmbientOcclusionCalculatorMixin { // do your stuff here }
Access the this instance of the class your mixin is targeting访问混入目标类的实例
public void foo() { doSomething1(); Something something = new Something(); something.doSomething(); doSomething2(); + injected(new CallbackInfo("foo", false)); }
Injecting with a slice在切片范围内注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@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") ) ) privatevoidinjected(CallbackInfo ci) { doSomething5(); }
Result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
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(); } }
public int foo() { + CallbackInfoReturnable<Integer> cir = new CallbackInfoReturnable<Integer>("foo", true); + injected(cir); + if (cir.isCancelled()) return cir.getReturnValue(); doSomething1(); doSomething2(); doSomething3(); return 10; }
Capturing local values 捕获本地数值
Mixin:
1 2 3 4 5
@Inject(method = "foo()V", at = @At(value = "TAIL"), locals = LocalCapture.CAPTURE_FAILHARD) privatevoidinjected(CallbackInfo ci, TypeArg1 arg1) { //CAPTURE_FAILHARD: If the calculated locals are different from the expected values, throws an error. arg1.doSomething4(); }
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; }
Redirecting a method call 重定向一个方法调用
Mixin:
1 2 3 4
@Redirect(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething(I)I")) privateintinjected(Something something, int x) { return x + 3; }
Result:
1 2 3 4 5 6 7
public void foo() { doSomething1(); Something something = new Something(); - int i = something.doSomething(10); + int i = injected(something, 10); doSomething2(); }
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:
1 2 3 4
@Redirect(method = "foo()V", at = @At(value = "FIELD", target = "La/b/c/Something;aaa:I", opcode = Opcodes.PUTFIELD)) privatevoidinjected(Something something, int x) { something.aaa = x + doSomething5(); }
Result:
1 2 3 4 5 6 7 8 9
public class Something { public int aaa; public void foo() { doSomething1(); - this.aaa = doSomething2() + doSomething3(); + inject(this, doSomething2() + doSomething3()); doSomething4(); } }
Modifying an argument 修改一个参数
Mixin:
1 2 3 4
@ModifyArg(method = "foo()V", at = @At(value = "INVOKE", target = "La/b/c/Something;doSomething(ZIII)V"), index = 2) privateintinjected(int x) { return x * 3; }
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 修改parameter
Mixin:
1 2 3 4
@ModifyVariable(method = "foo(ZIII)V", at = @At("HEAD"), ordinal = 1) privateintinjected(int y) { return y * 3; }
Result:
1 2 3 4 5 6
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:
1 2 3 4
@ModifyVariable(method = "foo()V", at = @At("STORE"), ordinal = 1) privatedoubleinjected(double x) { return x * 1.5D; }
package net.fabricmc.example; publicinterfaceBucketEmptySoundGetter { // The methods in an injected interface MUST be default, // otherwise code referencing them won't compile! default Optional<SoundEvent> getBucketEmptySound() { return Optional.empty(); } }
@Mixin(FlowableFluid.class) publicclassMixinFlowableFluidimplementsBucketEmptySoundGetter { @Override public Optional<SoundEvent> getBucketEmptySound() { //This is how to get the default sound, copied from BucketItem class. return Optional.of(((FlowableFluid) (Object) this).isIn(FluidTags.LAVA) ? SoundEvents.ITEM_BUCKET_EMPTY_LAVA : SoundEvents.ITEM_BUCKET_EMPTY); } }