A Mixin library that physically removes disabled injection methods from Mixin bytecode at runtime, driven by a TOML config file. No runtime guards are needed in your Mixin methods!
- Bytecode-level removal: Disabled injection methods are stripped from the Mixin ClassNode before the Mixin framework processes them. Your methods simply don't get injected: no
isEnabled()checks required. - Auto-generated TOML config: Scans your Mixin classes and generates a TOML config file with one toggle per injection method (and group).
- Package or class scanning: Scan entire packages recursively, or specify individual Mixin classes.
- Method grouping: Methods with the same base name (e.g.
injectHealth,injectHealth_1) share a single config toggle. - Auto-migration: Existing config values are preserved; new injection points are added automatically on next load.
- Runtime queries: Optional
isEnabled()helper for cases where you need to know a Mixin's state at runtime.
Gradle (Groovy):
repositories {
maven { url = uri("https://jitpack.io") }
}
dependencies {
implementation("com.github.Tater-Certified:SimpleMixinConfig:<version>")
}Add the preconfigured plugin to your mixins.<modid>.json:
"plugin": "com.github.tatercertified.simplemixinconfig.plugin.MixinConfigPlugin"If you need your own plugin (e.g. to combine with other Mixin libraries like MixinConstraints), add the following to your IMixinConfigPlugin:
public class MyMixinPlugin implements IMixinConfigPlugin {
@Override
public void onLoad(String mixinPackage) {
// Make sure it is declared in this order
Path configPath = Path.of("config/mymod_mixins.toml");
SimpleMixinConfig.init(configPath);
SimpleMixinConfig.scanPackages("com.example.mixins");
SimpleMixinConfigBootstrap.registerExtension();
}
// ... other methods
}If you used the built-in IMixinConfigPlugin, you need to register these before Mixins are loaded:
// These must be declared in this order
Path configPath = Path.of("config/mymod_mixins.toml");
SimpleMixinConfig.init(configPath);
SimpleMixinConfig.scanPackages("com.example.mixins");That's it! Disabled injection methods are physically removed during runtime. No changes are needed in your Mixin code.
The library generates a TOML file with one section per Mixin class:
[PlayerMixin]
"injectHealth" = true
"modifySpeed" = false
[InventoryMixin]
"onOpen" = truetrue: the injection method is applied normallyfalse: the injection method is physically removed from the mixin bytecode during runtime; it will never be injected- New injection points are automatically added when you add methods to your Mixins
| Method | Description |
|---|---|
init(Path) |
Set the config file path. Call once before scanning. |
scanPackages(String...) |
Scan Mixin packages, read/generate config, apply settings. |
scanClasses(String...) |
Scan specific Mixin classes, read/generate config, apply settings. |
isEnabled(String className, String methodName) |
Check if an injection point is enabled at runtime. Returns null if not found. |
getScannedData() |
Access the raw scanned Mixin data. |
isInitialized() |
Check if init() was called and clear() was not. |
clear() |
Free all internal data and memory. Call after Mixin processing is complete. |
A more detailed explanation of certain API features is explained below.
Scanning allows this library to access all the Mixins in a package (or specific Mixin file) and gather all injection points within the Mixins. This data is then used to build a config file. Both methods can be used at the same time, but it is discouraged to use the same method twice.
SimpleMixinConfig.scanPackages(
"com.example.mixins",
"com.example.mixins.world" // This one is not necessary since the first covers it
);SimpleMixinConfig.scanClasses(
"com.example.mixins.PlayerMixin",
"com.example.mixins.InventoryMixin"
);Because disabled methods are removed at transform time, you normally don't need isEnabled(). Use it only when you have non-injection code that depends on knowing whether a mixin is active:
@Mixin(SomeClass.class)
public class PlayerMixin {
@Inject(method = "getHealth", at = @At("head"))
private void modid$injectHealth(CallbackInfo info) {
// This method is only injected if it's enabled in config.
// No guard needed here.
doSomething();
}
// Non-injection code that depends on the Mixin being active:
public static void maybeDoSomething() {
Boolean enabled = SimpleMixinConfig.isEnabled("PlayerMixin", "modid$injectHealth");
if (enabled != null && enabled) {
// The injection is active, do something that relies on it
}
}
}Once all Mixins have been applied, the scanned data is no longer needed for the bytecode removal extension. Call clear() to release it.
A good place to do this is once the server starts. Most loaders have an event for it, but if they don't, make the following Mixin:
@Mixin(MinecraftServer.class)
public class ClearConfigMemoryMixin {
@Inject(method = "<clinit>", at = @At(value = "FIELD", target = "Lnet/minecraft/server/MinecraftServer;LOGGER:Lorg/slf4j/Logger;"))
private static void modid$onServerStarting(CallbackInfo ci) {
// Clears the Mixin's held storage
SimpleMixinConfig.clear();
}
}After clear():
isInitialized()returnsfalseisEnabled()returnsnullshouldApplyMixin()returnstrue
Don't call clear() if you use runtime isEnabled() checks.
Licensed under MIT