User Tools

Site Tools


drafts:mixin_override

:!: This is a draft! Feedback is appreciated, but keep in mind this is currently not intended to be read by Wiki users, it is not guaranteed that this page is fully accurate.

Tutorial: Overriding a Target's Parent (DRAFT)

Introduction

When mixing into a class, you may want to override methods from its parent. This tutorial will show you the problematic situation and how not to approach it, followed by both of the effective and compatible solutions to it. We will use a direct 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 the Introduction page and, if you're an absolute beginner, the 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 solution

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<Boolean> 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” 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<Boolean> 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<Boolean> 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.

drafts/mixin_override.txt · Last modified: 2025/11/24 00:48 by gauntrecluse