Skip to content
diskria edited this page Apr 23, 2026 · 23 revisions

Problem

In standard mixins, you often need to write the same thing in several places. For example, first you want to inject into the head of the method:

@Inject(
    method = "Lnet/minecraft/world/entity/player/Player;crit(Lnet/minecraft/world/entity/Entity;)V",
    at = @At(value = "HEAD")
)
private void doStuff(Entity entity, CallbackInfo ci) {}

Then you will want to change the argument when calling it from another method:

@WrapOperation(
    method = "Lnet/minecraft/world/entity/player/Player;attackVisualEffects(Lnet/minecraft/world/entity/Entity;ZZZZF)V",
    at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;crit(Lnet/minecraft/world/entity/Entity;)V")
)
private void doStuffAgain(Player instance, Entity entity, Operation<Void> original) {}

Then you want to call this method directly in mixin and use @Shadow:

@Shadow
public abstract void crit(Entity entity);

And finally, if you need to call this method from outside the mixin and it is private, then you create an separate class with @Invoker:

@Mixin(Player.class)
interface PlayerAccessor {
    @Invoker("crit")
    void crit(Entity entity);
}

Or you use AW for it (text file outside of code):

accessible method net/minecraft/world/entity/player/Player crit (Lnet/minecraft/world/entity/Entity;)V

This triggers a "redundancy cycle" where you manually duplicate the signature across injection parameters, @At targets, original.call arguments, @Shadow and @Invoker methods, and external files—effectively turning modding into error-prone manual data entry. Maintaining this is highly inefficient: a single change to the crit method signature in a future Minecraft update would require manual updates in multiple locations, making the codebase fragile and difficult to refactor.

Solution

Single Source of Truth (SSoT)

Clone this wiki locally