Table of Contents

:!: 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.

TODO List: Example of how a direct injection attempt would fail; Note about how submixin patterns cannot contain interfaces

Creating an Override in a target class (DRAFT)

Introduction

When mixing into a class, you may want to modify an inherited method's behavior for your target's instances. You may then be tempted to create an override. For this tutorial, we'll use an example situation where we want to call a custom method every time a copper golem gets hurt. Specifically, we'll be using 1.21.10 with Yarn mappings.

The Problem

The class CopperGolemEntity does not have an override for LivingEntity#damage(ServerWorld, DamageSource, float), and we want to call ExampleMod#doSomething() every time a copper golem takes damage. If this were our own class, we would just create an override.

Direct Override: What not to do

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, this is inherently incompatible with other mods who may want to apply a similar modification, and therefore should be avoided. The rest of this page will walk you through two solutions which are more compatible and do not sacrifice functionality.

The Solutions

Injecting an instanceof check in 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) { // We have to cast to Object first to satisfy the compiler
            ExampleMod.doSomething();
        }
        return original.call(world, source, amount);
    }
}

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.

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 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);
    }
}

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, and apply the actual changes in the override.

@Mixin(CopperGolemEntity.class)
abstract class CopperGolemEntitySubMixin extends LivingEntityMixin {
    @Override
    protected boolean overrideForCopperGolem(ServerWorld world, DamageSource source, float amount, Operation<Boolean> original) {
        ExampleMod.doSomething();
        return original.call(world, source, amount);
    }
}