Locally Debugging Spigot Minecraft Server Plugins in IntelliJ

This time I had to debug some code for a Spigot server plugin. Spigot is a custom Minecraft server. You download the Spigot jar file, run it and it sets up a Minecraft server for you. You can then write custom plugins for the server, using the Spigot API, and drop them in the “plugins” folder that Spigot generates for you. These can be used to customize the game experience (assuming you know Minecraft is a game etc. here.. :))

From my Googling experience (and asking from some people who developed some plugins..), the typical way to develop these plugins seems to be to write the code in Eclipse (the plugins are Java .JAR files), compile it, package it into a .jar file, copy it over to the server plugin folder, reload the server (causing the updated plugins to reload), and see what went wrong. Debugging is done with printouts from the plugin or using some weird remote debug setup in Eclipse. Seems like a slow and lengthy process to me.

Well, I am lazy so I wanted to run/debug my plugin code straight from IntelliJ, which is the IDE I use. Here is how I set it up, I had trouble finding anything on this from the internetz, so maybe this helps someone, if anyone happens to look for the same information. Or maybe it just helps me the next time I have wiped my environment and come here looking..

First we set up the .pom file for Maven

<!--Spigot API-->
<dependency>
  <groupId>org.spigotmc</groupId>
  <artifactId>spigot-api</artifactId>
  <version>1.9-R0.1-SNAPSHOT</version>
  <scope>provided</scope>
</dependency>
<!--Bukkit API-->
<dependency>
  <groupId>org.bukkit</groupId>
  <artifactId>bukkit</artifactId>
  <version>1.9-R0.1-SNAPSHOT</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.bukkit</groupId>
  <artifactId>craftbukkit</artifactId>
  <version>1.9-R0.1-SNAPSHOT</version>
  <!--version>${bukkit.version} </version-->
  <type>jar</type>
  <scope>compile</scope>
  <exclusions>
    <exclusion>
      <groupId>org.spigotmc</groupId>
      <artifactId>minecraft-server</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Using this configuration, we use Maven to package us a jar file. This seems well documented for Spigot so let’s just pretend this is done and all set up for our plugin project.

We then copy the Spigot jar to our working dir.  This is the Spigot jar we can download from the Spigot website to run the Spigot server. I created a folder “spigot-run” under my IntelliJ plugin project folder, put the spigot.jar file under this folder and ran it from there. First from the command line to see it generates the directory structure correctly. Because when you first run it, it creates the Minecraft server directory structure for you. Following this, add the spigot.jar file from “spigot-run” (or where-ever you put it) to the module classpath in the IntelliJ project.

Now, to set up the run configuration in IntelliJ IDEA, we can look into the METAINF in the spigot.jar file. The main class (at the time of writing this) is :

org.bukkit.craftbukkit.Main

So set that up as the main class for the run configuration. Now set up the working dir for the same run configuration as “spigot-run” or whatever is the place where you installed the Spigot jar and had it create the server directory structures. Try running this and see that Spigot starts right and the IntelliJ console window shows the Spigot console. Sweet.

So then our plugin. Let’s assume here that we have packaged it to a nice little jar file using Maven as described before. Now copy this plugin jar file to the “plugins” folder under the “spigot-run” folder (or whatever name you used). Start Spigot again from IntelliJ using the previously created run configuration. Spigot tries to load your plugin from the “plugins” folder and gives a ClassCastException or some similar error about PluginClassLoader not being used to load the class.

Now this is where you go Google it and find suggestions to go use the remote debugger. But no we can’t be that pragmatic. So what is the cause of the error? The cause is that when we run Spigot from IntelliJ where we also write the code for the plugin, IntelliJ loads the plugin code into memory using a different classloader that what Spigot needs. This is done before Spigot has a chance to go for it from the jar file we put in the “plugins” folder. Spigot wants to use its own classloader, the PluginClassLoader, but IntelliJ already loaded the class so Spigot is not happy. We can get around this by creating a run configuration for IntelliJ that does not load the plugin classes.

So to get this done, we create a new module in our IntelliJ plugin project. We call it “dummy” (or whatever you like). I put this module under the main plugin project working directory (the project top dir) in its own folder. I then let IntelliJ create an empty “src” folder for it. Basically this means I just clicked “next” until all was nice and shiny. This new module now has no code in it. So we just add the same spigot.jar (actually called spigot1.9.jar for me) file from under “spigot-run” to the “dummy” module classpath. We then take our old run configuration we already set up (that failed on PluginClassLoader error) and change this run configuration to use the “dummy” module classpath.

This causes IntelliJ to not load the plugin classes since they are not in the “dummy” module classpath. However, Spigot finds them and correctly loads them the jar we put in the “plugins” directory. Now you can run your plugin directly from IntelliJ. Just set up Maven to create the jar for you, and when the jar has been created drag and drop it into the “spigot-run/plugins” directory and hit “run”. If you are a good boy/girl, you can even automate copying the jar with Maven so you just start the Maven build and hit run. Two buttons, which generally are needed in software engineering (compile+run) so I am OK with that.

But wait!? What about debugging the code? Well it turns out you can now add debug breakpoints into your plugin code direct in IntelliJ. As you now start Spigot with the “dummy” run configuration, Spigot happily loads your plugin code from its “plugins” folder with its PluginClassLoader. However, as you started Spigot from IntelliJ, IntelliJ can still recognize your plugin classes are loaded and executed and lets you debug your code as normal. So when a breakpoint in your plugin is hit, you get your regular debug view in IntelliJ for the breakpoint. No remote debugger setups, not weird hassles (OK actually several with dummy modules etc but lets pretend we did good, OK?), no external copy or other complex procedures. Awesooomeeee!!! 🙂

As a final note, if you need some Craftbukkit classes (I did), run the buildtools for Spigot and it will install them in the local Maven repository. Something to do with developer arguments, DMCA requests and whatever. Hey, I am new to all this. 😮

That’s all folks. It’s really simple with just set up a dummy module to trick the classloaders, copy a jar and hit run/debug.

 

 

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s