Mixin
Mixin,俗称迷信,使用ASM的Tree API解析的原始字节码。这个API生成一个基于节点的底层字节码视图,然后可以通过Mixin转换器(Transformer)合并到目标类中。
Mixin虽然好用,但是相比较而言也更加危险,兼容性更低,引发的错误也更难排查,很多时候mixin造成的报错会使得日志堆栈都是原版路径,甚至引发某些奇妙的行为。在调试整合包中的mixin问题时候,可以加入-Dmixin.debug.export=true作为jvm参数来导出所有mixin类
关于mixin的原理和文档,可以直接阅读mouse0w0翻译的mixin文档mixin
关于用例,也可以直接阅览fabric的mixin文档FabricMixin
(虽然这件事很奇怪,但是fabric的mixin文档确实相比较而言比较完善,Forge甚至没有关于mixin的文档)
这里只给出我个人的几个示例)
使用mixin
这里可以直接按照mouse0w0的教程进行配置在Minecraft Forge中使用Mixin
修改返回值
如果你想使得玩家在装备某个物品的时候,使得死亡音效为biu~时,翻查mc源码发现,玩家死亡音效是写死的
   protected SoundEvent getDeathSound() {
      return SoundEvents.PLAYER_DEATH;
   }
常规方法对这种要求束手无策,这里我们就需要mixin来直接修改这个方法的返回值
@Mixin(Player.class)
public abstract class MixinPlayer {
    @Shadow public abstract Inventory getInventory();
    @Inject(at = @At(value = "HEAD"),method = "getDeathSound", cancellable = true)
    private void injectAddData(CallbackInfoReturnable<SoundEvent> cir) {
        if (this.getInventory().getArmor(3).is(ItemRegistry.ReimuHeaddress.get())) {
            cir.setReturnValue(SoundRegistry.touhou_dead);
        } else {
            cir.setReturnValue(SoundEvents.PLAYER_DEATH);
        }
    }
}
这里我们mixin了Player类的getDeathSound方法,如果你的IDEA安装了Minecraft Dev插件,就能看到类名坐标有一个SpongePower的logo,这就是mixin类的标志。
Inject注释表明我们将注入这个方法,注释中有两个必要参数,要注入的方法和要注入的点。示例中表明我们将在Player.getDeathSound方法的开头中插入我们的代码,mixin方法的参数可以有Minecraft Dev插件自动生成。
注入点参考如下Mixin参考——注入点参考
在方法里,我们拿到玩家的物品栏并且判断装备栏的头部是否为这一个物品,如果是,就返回原来的SoundEvents.PLAYER_DEATH
但是显而易见,一个人的mixin对这里进行了修改,那么如果另一个人也想对这里进行修改,那么二者就会打架,虽然mixin也提供了Priority优先级,但是总归还是有一定兼容风险的。
使用目标类的内容
有时候,在需要修改的方法中,引用了目标类的方法或者字段,这里就可以使用@Shadow注解来调用,或者强转this类型进行调用
比如目标类中有一个私有字段private final Minecraft minecraft,这时候我们就可以在我们的mixin类中直接添加字段
    @Final
    @Shadow
    private Minecraft minecraft;
同样的,也可以用类似的方法调用方法,比如目标方法为
   public int getWidth() {
      return getWidth(this.minecraft.options.chatWidth().get());
   }
我们只需要有样学样的在mixin类中加入如下方法,即可使用
`````` java  
    @Shadow
    public int getWidth() {
        return 0;
    }
对于mixin的目标实例,可以通过((TargetClass) (Object) this)获得,虽然IDEA会提示你有问题,但是实际上是没有问题的
添加新方法
当然,mixin也允许你在类中凭空添加一个自己的方法,也就是重写方法
首先,我们要准备一个接口,这个方法就是我们将要写的新方法了
public interface GuiGraphicsInterface {
    void livelyDanmaku$drawLine(int x1, int y1, int x2, int y2, int pZ, int pColor, int width);
}
然后我们直接对GuiGraphics进行mixin
@Mixin(GuiGraphics.class)
public abstract class MixinGuiGraphics implements GuiGraphicsInterface {
    @Override
    public void livelyDanmaku$drawLine(int x1, int y1, int x2, int y2, int pZ, int pColor, int width) {
    }
}
这里我们的mixin类实现了预设的接口,并覆写了这个方法。在使用的时候我们需要先将GuiGraphics类转为GuiGraphicsInterface接口类,再调用我们自己的方法livelyDanmaku$drawLine
    @Override
    protected void renderWidget(@NotNull GuiGraphics pGuiGraphics, int pMouseX, int pMouseY, float pPartialTick) {
        GuiGraphicsInterface guiGraphics = (GuiGraphicsInterface) pGuiGraphics;
        guiGraphics.livelyDanmaku$drawLine(p1.x, p1.y, p2.x, p2.y, 0, TRANSLUCENT_BLACK, 2);
    }
提醒:每个mixin类都必须在mixins.json文件中表明