SoulFire LogoSoulFire

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.

Use this page only when events, settings, metadata, and normal plugin APIs are no longer 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

External docs you should keep open

These are the official references that matter most when you start doing low-level integration work:

TopicLink
Fabric documentationdocs.fabricmc.net
Fabric wiki: Mixin injection referencetutorial:mixin_injects
Fabric wiki: Access widenerstutorial:accesswideners
SpongePowered Mixin wikigithub.com/SpongePowered/Mixin/wiki

Use Mixins only when you need them

Use the public SoulFire API first:

  • events
  • settings pages
  • metadata
  • BotConnection, ControlState, and ControllingTask

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.json
  • src/main/resources/<plugin-id>.mixins.json
  • src/main/resources/<plugin-id>.accesswidener if 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.

The current SoulFire source tree and official example template both use compatibilityLevel: "JAVA_21". Keep that aligned with the runtime you actually target instead of forcing it to match your local JDK version.

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, and gameMode.
  • 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

On this page