Mixins And Access Wideners
Use Fabric Mixins and access wideners to inject into Minecraft or SoulFire internals when the public event surface is not enough.
SoulFire plugins are Fabric mods, so you get the normal Fabric toolbox:
- Mixins for behavior changes and hook injection
- access wideners for changing member visibility
- direct access to Minecraft classes on the runtime classpath
Use Mixins only when you need them
Use the public SoulFire API first:
- events
- settings pages
- metadata
BotConnection,ControlState, andControllingTask
Reach for Mixins when:
- there is no event at the right timing point
- you need to change or observe control flow inside Minecraft
- you need access to data that SoulFire does not expose yet
- you need a hook inside one specific Minecraft method
SoulFire itself uses Mixins heavily. Its own event hooks for chat, packets, ticks, client settings, and movement input are all implemented through Mixins. That makes the SoulFire source tree a practical reference for your plugin Mixins.
Required resource files
Your plugin normally needs these resources:
src/main/resources/fabric.mod.jsonsrc/main/resources/<plugin-id>.mixins.jsonsrc/main/resources/<plugin-id>.accesswidenerif you need widened access
fabric.mod.json
Make sure the Fabric metadata points to the Mixin config and optional access widener:
{
"schemaVersion": 1,
"id": "my-plugin",
"version": "${version}",
"entrypoints": {
"main": ["com.example.myplugin.Main"]
},
"mixins": ["my-plugin.mixins.json"],
"accessWidener": "my-plugin.accesswidener",
"depends": {
"fabricloader": "*",
"minecraft": "*",
"soulfire": "*"
}
}Mixin config
A minimal Mixin config looks like this:
{
"required": true,
"minVersion": "0.8",
"package": "com.example.myplugin.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [],
"client": ["MyMixin"],
"injectors": {
"defaultRequire": 1
}
}Follow the template and the SoulFire runtime you target. Do not invent your own compatibility level blindly.
Loom wiring
The template wires the access widener like this:
loom {
accessWidenerPath = file("src/main/resources/my-plugin.accesswidener")
}If you do not use an access widener yet, you can still keep the empty file so the structure is already in place.
Current-safe pattern for bot-aware Mixins
The example repository is useful, but low-level API details can drift between SoulFire versions.
For current versions, prefer currentOptional() or current() instead of relying on old internal field access patterns.
Example:
@Mixin(LocalPlayer.class)
public class MyMixin {
@Inject(method = "tick", at = @At("HEAD"))
private void onTick(CallbackInfo ci) {
var bot = BotConnection.currentOptional().orElse(null);
if (bot == null) {
return;
}
if (bot.settingsSource().get(MySettings.ENABLED)) {
// your low-level logic here
}
}
}That keeps the Mixin resilient when it executes outside a bot-owned thread.
Access widener versus Mixin
Use an access widener when:
- you only need visibility changes
- you want to read or call an otherwise inaccessible field or method
- you do not need to alter control flow
Use a Mixin when:
- you need to inject before or after a method call
- you need to wrap or replace logic
- you need access to locals or method parameters at a precise point
Many plugins need both.
Best practices
- Keep Mixins thin. Put the real logic in ordinary classes.
- Prefer stable high-level targets over fragile local-variable-heavy injections.
- Null-check runtime objects like
player,level, andgameMode. - Test against the exact SoulFire version you ship for.
- Recheck the current built-in mixins before copying old examples.
Source trees worth reading
Next steps
How is this page?
Last updated on
Bot Control And Direct Access
Use ControlState, ControllingTask, BotConnection, and direct Minecraft access when you need in-process control beyond the public scripting surface.
Architecture And Protocols
Understand how SoulFire boots, loads plugins, manages instances and bots, and translates GUI or CLI actions into low-level bot behavior.