tutorial:mixin_override
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revision | |||
| tutorial:mixin_override [2025/12/06 00:19] – removed - external edit (Unknown date) 127.0.0.1 | tutorial:mixin_override [2025/12/06 00:19] (current) – ↷ Page moved from drafts:mixin_override to tutorial:mixin_override gauntrecluse | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | :!: //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' | ||
| + | |||
| + | ===== 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; | ||
| + | |||
| + | We will use a concrete example based on overriding a method from '' | ||
| + | |||
| + | This page assumes you already have an understanding of injectors and general Mixin usage. Please read [[tutorial: | ||
| + | |||
| + | |||
| + | ===== The Problem ===== | ||
| + | The class '' | ||
| + | |||
| + | ==== Direct Override: An incompatible approach ==== | ||
| + | We may be tempted to have our Mixin class extend the super of '' | ||
| + | <code java> | ||
| + | @Mixin(CopperGolemEntity.class) | ||
| + | abstract class CopperGolemEntityMixin extends GolemEntity { | ||
| + | // Dummy constructor from extending GolemEntity | ||
| + | protected CopperGolemEntityMixin(EntityType<? | ||
| + | super(entityType, | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public boolean damage(ServerWorld world, DamageSource source, float amount) { | ||
| + | ExampleMod.doSomething(); | ||
| + | return super.damage(world, | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 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, | ||
| + | |||
| + | |||
| + | ===== 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 '' | ||
| + | <code java> | ||
| + | @Mixin(LivingEntity.class) | ||
| + | abstract class LivingEntityMixin { | ||
| + | @WrapMethod(method = " | ||
| + | private boolean doSomethingForCopperGolem(ServerWorld world, DamageSource source, float amount, Operation< | ||
| + | if ((Object) this instanceof CopperGolemEntity) { | ||
| + | ExampleMod.doSomething(); | ||
| + | } | ||
| + | return original.call(world, | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | We must cast to '' | ||
| + | |||
| + | Remember of course that other injectors than '' | ||
| + | |||
| + | This solution has one main downside, however, and it is that we do not have direct access to the subclass' | ||
| + | |||
| + | ==== Submixin Pattern ==== | ||
| + | Alternatively, | ||
| + | |||
| + | For this tutorial, we will call the parent Mixin class '' | ||
| + | |||
| + | |||
| + | === The Parent Mixin === | ||
| + | The parent Mixin must create a " | ||
| + | <code java> | ||
| + | @Mixin(LivingEntity.class) | ||
| + | abstract class LivingEntityMixin { | ||
| + | @WrapMethod(method = " | ||
| + | protected boolean overrideForCopperGolem(ServerWorld world, DamageSource source, float amount, Operation< | ||
| + | return original.call(world, | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Once again, using '' | ||
| + | |||
| + | === The Child Mixin === | ||
| + | The child Mixin, or submixin, targets the class we want to override behavior for, and extends the parent Mixin: | ||
| + | <code java> | ||
| + | @Mixin(CopperGolemEntity.class) | ||
| + | abstract class CopperGolemEntitySubMixin extends LivingEntityMixin { | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | We then override the dummy handler, by virtue of it being '' | ||
| + | |||
| + | <code java> | ||
| + | @Mixin(CopperGolemEntity.class) | ||
| + | abstract class CopperGolemEntitySubMixin extends LivingEntityMixin { | ||
| + | @Override | ||
| + | protected boolean overrideForCopperGolem(ServerWorld world, DamageSource source, float amount, Operation< | ||
| + | ExampleMod.doSomething(); | ||
| + | return super.overrideForCopperGolem(world, | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 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. | ||