Faster Minecraft plugin development using Hotswapping

Practical guide on how to speed up your plugin development workflow using DCEVM and IntelliJ

The single thing that had the biggest impact on my plugin development workflow is definitely code hotswapping. Without it, the typical development cycle looks like this:

  1. Implement Changes
  2. Rebuild Plugin
  3. Install new Plugin
  4. Restart Server

Especially building the plugin and restarting the server can take pretty long which causes very extensive feedback loops. This is especially annoying if you are implementing a lot of small changes and want to see their results.

Most servers have the option to reload plugins, saving the restart step. This is not an adequate solution as many plugins are not compatible with it, and it still requires a rebuild of your plugin which can take pretty long in big projects.

In this post I want to present an elegant solution to this by code hotswapping using IntelliJ and DCEVM. Eclipse also supports Hotswapping, but I won’t be covering that in this tutorial. This technique is not just applicable to minecraft plugins, it can be used in any long-running system. Minecraft Plugins have just been the area where Hotswapping has brought me the biggest benefit.

What is DCEVM?

The standard JVM allows its users to redefine classes while the VM is running. This process can be triggered through several ways, but it is limited to some very specific kinds of changes. It only allows the method bodies to be updated. This can already be useful in many cases, but any major change to your project still requires you to rebuild and restart.

Dynamic Code Evolution VM is a JVM Implementation which extends the class redefinition capabilities to include basically any change. It allows you to add and remove fields and methods, and to update the class hierarchy. This is a major improvement compared to the standard JVM. DCEVM was developed as part of a research project at JKU Linz.

Choosing the right JVM

In order to make use of all the benefits DCEVM offers use you need to run your server with a JVM implementation that includes it. Luckily there are many options available. Choose the right one depending on your desired Java Version.

For Java 11 choose trava-jdk-11-dcevm. For Java 17 or 21 use JetBrainsRuntime. Just look for a release with the appropriate version on the release page and download JBR (vanilla) for your platform.

Starting the server

You need to pass some special options to your minecraft server in order for Hotswapping to work. Replace paper.jar by whatever your minecraft server jar is called. Make sure your plugin is already installed before starting the server.

Start server with options
$
java -XX:+AllowEnhancedClassRedefinition -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005 -jar paper.jar nogui

The -XX:+AllowEnhancedClassRedefinition option is required in order to enable the enhanced class redefinition features.

There are multiple ways to redefine classes at runtime. We will be doing it by remotely attaching a debugger to your running server. The -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005 option causes your server to listen for debuggers on port 5005.

Debugging on a remote server using SSH

Sometimes our development server is not running on our local machine. If this is not the case for you, feel free to skip this section.

At the time of writing, IntelliJ does not yet support remote debugging over an SSH tunnel, managed inside the IDE. So in order to establish a connection to our server securely we will have to create an SSH tunnel ourselves.

Open SSH tunnel
$
ssh host -L 5005:127.0.0.1:5005 -N

Replace host with the host of your server. If you need to authenticate, then do it as you normally do when connecting to your server with SSH. Ideally you have this configured in your ssh config.

The command forwards all requests from port 5005 on your local machine to 127.0.0.1:5005 on your remote server. The -N option is used, so no commands get executed on your server, we only need the tunnel.

Attaching a debugger from IntelliJ

In IntelliJ, create a new Debug Configuration of Type Remote JVM Debug and copy these settings. Make sure the port is the same as the one you specified when starting your server.

IntelliJ Remove Debug Configuration

Hotswapping your changes

Now finally, after all this work, we are ready to start hotswapping. Doing that is pretty simple. First attach the debugger by selecting the Debug Configuration we created earlier and starting the debugger. Then just change something in your code and this button should appear in the top right in your IDE.

IntelliJ Code changed Button

Press it, or use the Ctrl+F9 shortcut. This will trigger a rebuild of your project and update the classes on your server. The changes are now live. Here is a quick demo showcasing this process:

Limitations

This technology is really cool and useful for speeding up plugin development. Nevertheless, there are still some limitations to it.

While DCEVM has support for hierarchy changes, when using JBR I found these to only work in some special cases. It’s possible, for example, to add an interface to class. But you are not able to remove it, without restarting.

Any code that relies on server lifecycle hooks will not be rerun when hotswapping. For example adding a new Event Listener will not work, because that depends on registration logic that is usually performed once on server startup. Hotswapping will not cause your startup logic to run again, so you can’t just add a new Listener and expect it to work, without manually reregistering it.

Bonus Tip: Hotswap single file

When applying the Hotswap in IntelliJ it triggers a rebuild of your entire project. In small projects this is barely noticeable, but for bigger projects that can cause quite some delay. Luckily there is the Single Hotswap Plugin for IntelliJ which lets us apply Hotswaps more finely. It allows you to only hotswap the File which is currently opened in your editor. This can greatly decrease the waiting time if you only made changes to that file. To trigger it, there is a Hammer symbol in the top menu bar.

Single Hotswap Button

Conclusion

Hotswapping is a really powerful technique that many plugin developers sadly don’t know about. I believe that integrating it into your workflow can greatly increase your productivity and fun while programming.

If you want to learn more about how DCEVM works internally, you can check out the original PhD Thesis. It gives an overview over how the HotSpot JVM works internally and the algorithms used for enhanced class redefinition.