Mixin Injection Point Discovery
Use this workflow to find safe, reproducible mixin targets before writing injection code.
Inputs
- Target Minecraft common jar (for this project):
./space-logger-mod/.gradle/loom-cache/minecraftMaven/net/minecraft/minecraft-common-9a7fd27717/26.1-rc-2/minecraft-common-9a7fd27717-26.1-rc-2.jar
- Feature intent (what behavior to observe/record/change)
Workflow
- Confirm target classes exist in jar.
jar tf "$JAR" | rg 'ClassName1|ClassName2|...'
- Decompile first for human-readable control flow (recommended).
java -jar ~/Downloads/vineflower-1.10.1-slim.jar "$JAR" ./tmp-mc-src
- Use the decompiled source to understand actual branches, return points, and where side effects happen.
- Treat decompilation output as guidance; method descriptors still need bytecode verification.
- Read exact method signatures from bytecode.
javap -classpath "$JAR" -p net.minecraft.some.TargetClass | sed -n '1,300p'
- Choose hook method by semantics, not by name similarity alone.
- Prefer methods that encode final server-side outcome.
- For action success/failure, inject at
@At("RETURN")and inspect return value. - For pre-state capture + post-state diff, use
HEAD+RETURNpair.
- Record exact descriptor in comments while coding.
- Example format:
Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/InteractionResult;
- Verify ownership class.
- Ensure method belongs to the mixin target class (or inherited and still callable from target).
- Avoid injecting into broad base classes unless behavior is truly global.
- Validate side and threading.
- For game logic persistence, only log on server side.
- Guard with
instanceof ServerPlayer/instanceof ServerLevelwhere applicable.
- If method is too broad, narrow context.
- Add runtime guards (container type, block entity presence, action type, non-empty stacks).
- Prefer extra guard code over fragile
ordinal-based injection.
- If mixin body must call inherited base-class methods directly, use subclass-style mixin shape.
- Pattern:
@Mixin(TargetClass.class)abstract class XMixin extends TargetBaseClass- add a fake constructor that matches base constructor and calls
super(...)
- The constructor is never called at runtime by normal game flow; it only satisfies Java type/compile constraints.
- Use this when plain
@Shadowis not enough and you need direct inherited method access from the mixin type itself.
Selection Heuristics (Minecraft 26.1 unobfuscated)
- Combat hit amount:
LivingEntity.actuallyHurt(ServerLevel, DamageSource, float) - Kill event:
LivingEntity.die(DamageSource) - Block break:
ServerPlayerGameMode.destroyBlock(BlockPos) - Block use/place flow:
ServerPlayerGameMode.useItemOn(...)and/orBlockItem.place(BlockPlaceContext) - Container click diffing:
AbstractContainerMenu.clicked(int, int, ContainerInput, Player)
Always re-check signatures via javap before coding; do not assume from older MC versions.
Command Snippets
# Optional: decompile full jar for readable logic
java -jar ~/Downloads/vineflower-1.10.1-slim.jar "$JAR" ./tmp-mc-src
# Show methods in target class
javap -classpath "$JAR" -p net.minecraft.server.level.ServerPlayerGameMode
# Find likely classes quickly
jar tf "$JAR" | rg 'ServerPlayerGameMode|LivingEntity|AbstractContainerMenu|BlockItem'
# Find save/nbt helpers
javap -classpath "$JAR" -p net.minecraft.nbt.NbtIo
Failure Patterns to Avoid
- Targeting client-only classes for server persistence logic.
- Injecting at
HEADfor outcomes that are only known at return time. - Assuming mapped names from Yarn/Mojmap docs when current jar is unobfuscated snapshot/RC.
- Relying on local variable capture without bytecode confirmation.
- Calling inherited base methods from mixin without subclass-shape setup (
extends+ fake ctor), leading to compile-time access issues.
Done Criteria
- Every mixin target class and method signature confirmed from
javapoutput. - Injection point has explicit rationale (
why this method,why this at). - Server-side guards present where needed.
- Behavior validated with an automated or deterministic test path.