A robust, drop-in Java plugin designed to bootstrap SpongePowered Mixin, MixinExtras, and Access Wideners into modern PaperMC server environments.
Modern PaperMC servers utilize a highly isolated, multi-layered classloader architecture. Between Paperclip's custom classloading, legacy Bukkit PluginClassLoaders, and modern PaperPluginClassLoaders, getting a custom Mixin loader to successfully see, target, and inject into server and plugin classes is notoriously difficult.
MixinLoader solves this by acting as a self-bootstrapping plugin. It installs like a normal plugin, but automatically injects itself into the JVM arguments, hijacks the server's root classloader, bridges the isolation gaps between the server and plugin environments, and initializes the Mixin ecosystem before the server fully boots.
- 🪄 Drop-In Installation: Install it like a normal plugin. No need to edit
start.shorstart.bat. MixinLoader automatically detects if it's running as an agent, and if not, gracefully closes network sockets and relaunches the JVM with itself injected as a-javaagent. - 🤝 Broad Ecosystem Compatibility: Natively supports loading and running Mixin plugins built for Ignite, Horizon, and Origami.
- 🚀 Paperclip Hijacking: Intercepts
Paperclip's initialization to replace the rootURLClassLoaderwith a customRoutingServerClassLoader, giving the loader full visibility of the server environment. - 🔀 Smart Class Routing: Automatically maps plugin packages to their respective classloaders. If a Mixin targets a class in a plugin, the loader knows exactly which
PluginClassLoaderto ask. - 📖 Access Widener Support: Fully supports Access Wideners out of the box, allowing your mixins and plugins to bypass Java visibility restrictions seamlessly.
- 🔄 Universal Plugin Support: Fully compatible with both legacy (
org.bukkit.plugin.java.PluginClassLoader) and modern (io.papermc.paper.plugin...PaperPluginClassLoader) plugin loading systems. - 🧰 MixinExtras Included: Bootstraps MixinExtras automatically for advanced Mixin capabilities.
- 🛡️ Java 17+ Ready: Automatically opens required
java.baseinternals viaInstrumentation.redefineModuleto ensure deep reflection works on modern JVMs.
While MixinLoader can successfully load and execute plugins designed for Ignite, Horizon, and Origami, it is important to understand how this compatibility works:
- What IS supported: Core Mixin injection, MixinExtras extensions, Access Wideners, and standard classloader routing. If a plugin relies purely on these standard tools, it will work perfectly.
- What IS NOT supported: MixinLoader does not implement the proprietary public or private Java APIs provided by Ignite, Horizon, or Origami. We focus strictly on the core Mixin ecosystem. If a plugin strictly depends on custom utility classes, proprietary lifecycle events, or specific API methods unique to those other loaders, it will fail to load or function correctly.
Installing MixinLoader is as simple as installing any other Paper plugin. No modifications to your startup scripts are required.
- Download the latest
MixinLoader-1.0.0-all.jar. - Drop it into your server's
plugins/folder. - Start your server exactly as you normally would:
java -jar server.jar
Upon startup, the plugin will detect that it is not running as a Java agent. It will cleanly close the server's listening sockets to prevent port-binding conflicts, and seamlessly relaunch the JVM with itself injected as a -javaagent before the server fully loads.
When the server starts, MixinLoader checks the mixinloader.loaded system property. If it's missing, it knows it needs to inject itself:
- On Linux/POSIX: It reads
/proc/self/fdto find open network sockets and closes them via nativelibccalls. It then usesexecvpto replace the current process with the new JVM command, ensuring no orphaned processes are left behind. - On Windows: It iterates through process handles, checking for listening sockets via
WinSock2(getsockoptwithSO_ACCEPTCONN), closes them, and uses aProcessBuilderto launch the new JVM. - Why close sockets? If the JVM restarts without closing the sockets first, the OS keeps the port (e.g., 25565) in a
TIME_WAITstate, causing the newly launched JVM to crash withjava.net.BindException: Address already in use.
Once running as an agent, it uses ByteBuddy to perform runtime bytecode manipulation:
- Root Classloader Replacement: Uses
MemberSubstitutionto intercept thenew URLClassLoader(...)call insidePaperclip.mainand replaces it withRoutingServerClassLoader. - Namespace Registry: Scans plugin JARs to build a concurrent map of package names to classloaders.
- LoadClass Interception: Injects
Adviceinto theloadClassmethods of the server and plugin classloaders to route missing classes to the correct plugin classloader.
This project uses Gradle for dependency management.
# Clone the repository
git clone https://github.com/Chest-Solutions/MixinLoader.git
cd MixinLoader
# Build the project
./gradlew buildThe compiled JAR will be located in the build/libs/ directory.
Massive thanks to the creators of the following libraries:
- SpongePowered Mixin - The core mixin framework.
- MixinExtras by LlamaLad7 - Essential Mixin extensions.
- ByteBuddy - For powerful runtime bytecode generation.
- reflectionremapper by jpenilla - For clean, type-safe reflection proxies.
- JNA (Java Native Access) - For OS-level socket and process management.
- PaperMC - For the amazing server software.
This project is licensed under the MIT License - see the LICENSE file for details.