Tag Archives: minecraft

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.

 

 

Configuring EC2 instance connection firewall rules within the VPC (security groups)

OK, again with them Minecraft servers. This time I already had a bunch of them up and running on different free tier instances on EC2. Now I wanted to add a database and connect some of the server plugins to this. The DB should run in its own EC2 instance and the Minecraft servers on their own. So how do I configure my instances so the Minecraft server instances can talk to the DB server but nothing outside my instances can talk to it?

EC2 uses Security Groups to define the firewall configurations. These are simple types of rules such as “Allow TCP from IP X”. But I cannot configure this with the other instances public IP address since all the instances keep changing their IP addresses as they are shut down and started again. So how can I do it? The solution is to put all the Minecraft servers in a specific Security Group. Then set the IP address where the DB server instance allows communications from to the name of the Security Group the Minecraft servers are on. And of course the port the DB server listents to.

The weirdest part of this is the poor documentation as I would not expect to write textual group names in a box expecting an IP address (numbers and dots). Only way I finally figured this out was to look at the “default” security group which is configured by default for such policy.

And how do you rename an existing security group? You dont (sheesh). But you can use an existing one, clone it, and give it a new name. Then go to EC2 instance management console, choose something like “network->change security group” to define your new security group for the instances. Then delete the old group.

Most of this is only possible if the instances are in the same VPC (virtual private cloud). Good thing is, AWS seems to create a VPC and link everything to this by default these days..

Updating configuration files for EC2 dynamic IP’s

Needed to connect some of the EC2 instances running my kids Minecraft servers. Problem is, I am cheap and run a bunch of the free tier instances. I need to stop and start the instances all the time to save on the hosting costs. This results in all of them getting new IP addresses all the time, which makes a mess of the application server configurations. I can use a dynamic DNS service to address the basic requirement to give the main server an address. But then I also needed to connect several of them together, and their addresses keeps changing..

The solution is actually very simple (as usual). The AWS development kit provides means to query all my AWS instance information, including their names and IP addresses. The name is actually just a tag given to the instance, so any other tag could be used as well. To give a simple interface to redo configurations I just query the data, put all the addresses using their tag information as keys and use a template engine to generate a list of the IP addresses preformatted for the server configuration. The server in this case being BungeeCord, which needs to be able to forward players to the different EC2 instances.

Java code:

    AWSCredentials credentials = null;
    try {
      credentials = new ProfileCredentialsProvider().getCredentials();
    } catch (Exception e) {
      throw new AmazonClientException("Cannot load AWS credentials.", e);
    }
    AmazonEC2 ec2 = new AmazonEC2Client(credentials);
    Region eu = Region.getRegion(Regions.EU_CENTRAL_1);
    ec2.setRegion(eu);
    DescribeInstancesResult result = ec2.describeInstances();
    List<Reservation> reservations = result.getReservations();
    for (Reservation res : reservations) {
      List<Instance> instances = res.getInstances();
      for (Instance ins : instances) {
        String ip = ins.getPublicIpAddress();
        List<Tag> tags = ins.getTags();
        for (Tag tag : tags) {
          String key = tag.getKey();
          if (key.equals("Name")) {
            String name = tag.getValue();
            vc.put(name + "-ip", ip);
          }
        }
      }
    }

    StringWriter sw = new StringWriter();
    velocity.mergeTemplate("template.vm", "UTF8", vc, sw);
    write(sw.toString(), "generated.txt");

With this, it gets the names of all of my EC2 instances, loads a Velocity template and sets a key-value pair with key=-ip and the value as IP. I then run Velocity to generate a configuration file with the current instance IP addresses filled in. Now the kids can just run the command to update the server config.