:!: //This page was only recently pushed out of drafting! Whilst it has gone under feedback and revisions during drafting, it is still possible that there are inaccuracies or issues. Feedback and contributions are appreciated.//
====== Overriding a Mixin Target's parent ======
===== Introduction =====
When mixing into a class, you may want to create overrides for methods the target inherits from parents, as you would if it were your own class. However, there are specific design patterns that should be implemented to do so for Mixin targets in order to remain as compatible as possible with other mods wanting to do similar modifications. This page will go over both the approach of trying to directly create an override, and how it is problematic; and will then show the two primary design patterns to tackle the problem.
We will use a concrete example based on overriding a method from ''LivingEntity'' in ''CopperGolemEntity'' in Minecraft 1.21.10 with Yarn mappings.
This page assumes you already have an understanding of injectors and general Mixin usage. Please read [[tutorial:mixin_introduction|the Introduction]] page and, if you're an absolute beginner, the [[tutorial:mixin_your_first_mixin|tutorial for making your first Mixin]] is recommended to get familiar with basic syntax and concepts of Mixin.
===== The Problem =====
The class ''CopperGolemEntity'' does not have an override for ''LivingEntity#damage(ServerWorld, DamageSource, float)'', and we want to call ''ExampleMod#doSomething()'' everytime a copper golem specifically gets damaged. As the method we're trying to override the behavior of does not exist in ''CopperGolemEntity'', whether that be in source code or bytecode, we can't try to directly inject into it somehow.
==== Direct Override: An incompatible approach ====
We may be tempted to have our Mixin class extend the super of ''CopperGolemEntity'' and override like so:
@Mixin(CopperGolemEntity.class)
abstract class CopperGolemEntityMixin extends GolemEntity {
// Dummy constructor from extending GolemEntity
protected CopperGolemEntityMixin(EntityType extends GolemEntity> entityType, World wolrd) {
super(entityType, world)
}
@Override
public boolean damage(ServerWorld world, DamageSource source, float amount) {
ExampleMod.doSomething();
return super.damage(world, source, amount);
}
}
However, whilst it is an extremely simple approach, this is inherently incompatible with other mods who may want to apply a similar modification. This is because Mixin will attempt to merge both overrides of the same method in the target class; this cannot chain both modifications and will cause two conflicting methods. With a default Mixin configuration, this will result in a hard crash upon application. Instead, to preserve compatibility, we should opt for one of the following solutions:
===== The Solutions =====
==== Adding an instanceof to the super ====
We can inject a check within the method we wish to functionally override, which will execute our custom operations if the ''this'' instance is of the intended subclass. In our case, we could create a Mixin like the following:
@Mixin(LivingEntity.class)
abstract class LivingEntityMixin {
@WrapMethod(method = "damage")
private boolean doSomethingForCopperGolem(ServerWorld world, DamageSource source, float amount, Operation original) {
if ((Object) this instanceof CopperGolemEntity) {
ExampleMod.doSomething();
}
return original.call(world, source, amount);
}
}
We must cast to ''Object'' first to avoid a compiler error, casting to ''Object'' and potentially to the target class is often needed to access the ''this'' instance.
Remember of course that other injectors than ''@WrapMethod'' may be more fitting depending on your specific use-case. This Mixin only covers our specific situation. For example, in some situations it may be better to use ''@ModifyReturnValue'' if the intent is to do something with the method's returned value.
This solution has one main downside, however, and it is that we do not have direct access to the subclass's members as we would with a Mixin directly targeting that class. We may also prefer to have modifications targeting the subclass kept to Mixins targeting that class. Furthermore, chaining overrides like this for multiple classes is arguably merssier.
==== Submixin Pattern ====
Alternatively, we may use what is known as a submixin pattern. This will require to create two Mixin classes in dedicated files in our project's mixin package. One will target the class containing the method we want to functionally override -- we'll call it the "parent" Mixin or the "super" Mixin -- and the other will target the class we want to functionally create an override into, which we'll call the "child" Mixin or "sub" Mixin.
For this tutorial, we will call the parent Mixin class ''LivingEntityMixin'', and the child Mixin class ''CopperGolemEntitySubMixin''. These will be in separate files, with both registered in our ''mixins.json'' config.
=== The Parent Mixin ===
The parent Mixin must create a "dummy", or "noop" injector, with a ''protected'' handler method, which will apply no changes by itself:
@Mixin(LivingEntity.class)
abstract class LivingEntityMixin {
@WrapMethod(method = "damage")
protected boolean overrideForCopperGolem(ServerWorld world, DamageSource source, float amount, Operation original) {
return original.call(world, source, amount);
}
}
Once again, using ''@WrapMethod'' is not the only option for this. Use an injector tailored to the modifications you wish to apply within your override.
=== The Child Mixin ===
The child Mixin, or submixin, targets the class we want to override behavior for, and extends the parent Mixin:
@Mixin(CopperGolemEntity.class)
abstract class CopperGolemEntitySubMixin extends LivingEntityMixin {
}
We then override the dummy handler, by virtue of it being ''protected'', and apply the actual changes in the override. We can treat the override method as a handler corresponding to the injector defined in the superclass.
@Mixin(CopperGolemEntity.class)
abstract class CopperGolemEntitySubMixin extends LivingEntityMixin {
@Override
protected boolean overrideForCopperGolem(ServerWorld world, DamageSource source, float amount, Operation original) {
ExampleMod.doSomething();
return super.overrideForCopperGolem(world, source, amount, original);
}
}
This can allow us to access any field or member within that target class directly, and can be better to keep all modifications we make to that class contained in the child Mixin.\\
:!: However, due to a bug, the submixin pattern cannot be used if any class within the hierarchy is an interface.