DRAFT:
====== Mixin Inheritance ======
==== Motivation ====
The Mixin framework enables us as modders to make a number of precise changes to Minecraft's decompiled source code, including such things as:
* Adding new methods and fields to a vanilla class
* Implementing new interfaces onto classes
* Overriding methods of a superclass in a subclass to create extra functionality
* Change the behavior of vanilla methods
With such a powerful and granular tool at our disposal, it's worth considering whether any given structure you employ for your code might have implications for compatibility with other mods. Not all mods need to tout themselves as fundamentally compatible with others, but most mods unless otherwise stated are expected to try to maintain a minimum of compatibility in terms of Mixin techniques used. Example techniques for good compatibility include using the ''@Unique'' annotation for custom fields, preferring ''@Inject'' to more invasive ''@Redirect'' and ''@Overwrite'' where possible, and applying Mixins to as few methods and classes as possible to create the desired behavior.
Sometimes, the scope of a given mod necessitates sweeping changes to one or more pieces of vanilla code. Consider a mod that adds some kind of functionality to ''LivingEntity'', the class that governs shared behavior of mobs, villagers, and even players. It could be the case that the general code added or changed in ''LivingEntity'' could create a bug due to interactions with code in one of the subclasses of ''LivingEntity'', perhaps ''PlayerEntity''. We could do something simple like make a new Mixin class that targets ''PlayerEntity'' and applies the necessary changes, and most of the time that would be perfectly reasonable. But let's say that our mod is ambitious and we end up needing a certain ''@Redirect'' in ''PlayerEntity'' to achieve our desired behavior. Now, what would happen if another mod ends up using a ''@Redirect'' on the same target? Well, one of the Mixins would have to fail!
==== A Technique For Improved Compatibility ====
Mixin Inheritance is one solution to the hypothetical conflict posed in our example. The basic form of Mixin Inheritance is to use a parent Mixin to "bookmark" a position in a vanilla method where we wish to apply changes, and to then use child Mixins that target individual classes which may require tailored code in order to work properly. Let's take a look at the outline:
== Parent Mixin ==
@Mixin(Parent.class)
public abstract class ParentMixin {
@Inject(method = "something", at = @At("SOMEWHERE"))
protected void handlerMethod(CallbackInfo ci) {
//Leave empty
}
}
== Child Mixin ==
@Mixin(Child.class)
public abstract class ChildMixin extends ParentMixin {
@Override
protected void handlerMethod(CallbackInfo ci) {
doStuff(); // Actual behaviour
}
}
Notably, we are "spending" our ''ChildMixin'''s ''extends'' keyword to designate ''ParentMixin'' as its superclass. This is somewhat atypical: often for ease of use, we'd have a Mixin class extend the //target class'// parent, rather than some other Mixin class. Our reason for doing so is to unlock for ourselves the possibility of using ''@Override'' to perform a neat sleight-of-hand in our target child class: because of how Mixins are applied to their targets, as long as the compiler is willing to accept our inheritance structure, the final result of this form is to have `Child` perform whatever code we provide in its ''@Override'' at whatever location we pinpointed in ''ParentMixin'''s method annotations. Had we tried to do things with the conventional approach, we'd either end up replacing all of the code in ''Child'''s inherited version of ''Parent'''s method and thus need to re-write a hefty amount of code, or we'd have to deal with the compatibility problems mentioned above.