Instrumenting Minecraft: Fun and Games with New Relic Insights

Image1

Disclaimer: I am a New Relic employee on the Site Reliability Engineering team with an 8-year old-daughter who is currently enjoying exploring Minecraft and all it has to offer.

In this article I am going to talk about how you can use New Relic Insights and APM to track the health and player actions in CraftBukkit, the incredibly popular, moddable Minecraft server. As a server administrator for a multiplayer game, there is a lot of value in being able to track how your players use the game, what they enjoy, and what is causing them challenges–and also to be aware of when they are disrupting the enjoyment of other users.

Minecraft: A primer

Minecraft is an extremely popular, independent sandbox game written in Java and developed by the software company Mojang. The game is most easily described as a digital brick-building platform using only square blocks made from a multitude of materials. It also includes elements of creative building, random world-exploration, weather, monsters, survival, and multiplayer

Screenshot_6_17_14_9_36_AM

Minecraft has always included a server that allowed anyone to host a world for multiple people to play together in. To allow server owners to extend the game in ways that have not been envisioned by Mojang, third-party servers started appearing in the Minecraft community. The most popular of these servers is CraftBukkit.

Getting started

On March 19th, 2014, New Relic announced the beta version of Insights, a data analytics platform that complements APM, New Relic’s existing application performance management platform. With Insights, users can now run queries against the data collected from their applications and create useful dashboards that provide real-world knowledge about their software and user behaviors.

To demonstrate how easy and useful this functionality is, I recently added the New Relic Java agent to my own copy of the open source CraftBukkit Minecraft server and instrumented it so it would send some interesting data for analysis. The final dashboard built from this data looks like this:

image3

The top two graphs on the above dashboard show all the blocks that players have placed or removed in the last hour. The lower graphs display the creatures that have spawned and died in the world during the same time period.

To get started gathering the data required for these charts, I downloaded the New Relic Java agent and then added the source code tree to my Java project. Once that was done I opened up my favorite code editor and added the following two lines to the top of each CraftBukkit source file that had transactions that I wanted to get data from:


import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Trace;

Tracking errors

Since New Relic’s APM platform can track errors that occur in the Minecraft server’s code base, the very next thing I did was to go through the entire code base and ensure that any errors that are caught are then submitted to New Relic. This was as easy as adding a call to send a copy of the error to New Relic after every error catch:


for (Permission perm : perms) {
                 try {
                     pluginManager.addPermission(perm);
                 } catch (IllegalArgumentException ex) {
+                    NewRelic.noticeError(ex);
                     getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName()
                 }
             }
         } catch (Throwable ex) {
+            NewRelic.noticeError(ex);
             Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, ex.getMessage() + " loading " + plugin.getDescription().getFullName() + " (Is
         }
     }

Once this was done, it was very easy to track any errors that occurred while the server was running and then dig down into the stack trace and see what was going on. No longer were these errors mostly hidden from sight (and therefore often overlooked). Here’s a look at the result in APM:

i4

i5

Gathering the data

The next step is to determine which transactions in our software are of interest, and then to instrument them. Since the Minecraft server is not primarily a web server application, much of the auto-instrumenting functionality that New Relic provides for web platforms does not directly help us. However, it’s important to note that plug-ins that provide a web endpoint, like the dynamic world map plugin Dynmap, are typically detected and instrumented automatically.

In a world where placing blocks is fundamental to the experience, the method in the file src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java called callBlockPlaceEvent seems like the best place to start. This method is called whenever a block is placed in the Minecraft world.


+    @Trace (dispatcher=true)
     public static BlockPlaceEvent callBlockPlaceEvent(World world, EntityHuman who, BlockState replacedBlockState, int clickedX, int clickedY, int clicke
+        NewRelic.setTransactionName(null, "callBlockPlaceEvent");
+
         CraftWorld craftWorld = world.getWorld();
         CraftServer craftServer = world.getServer();

         Player player = (who == null) ? null : (Player) who.getBukkitEntity();

         Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ);
         Block placedBlock = replacedBlockState.getBlock();

+        NewRelic.addCustomParameter("playerName", player.getName());
+        String blockname = placedBlock.getType().toString();
+        blockname = blockname.toLowerCase().replace("_"," ");
+        NewRelic.addCustomParameter("blockType", blockname);
+
         boolean canBuild = canBuild(craftWorld, player, placedBlock.getX(), placedBlock.getZ());

         BlockPlaceEvent event = new BlockPlaceEvent(placedBlock, replacedBlockState, blockClicked, player.getItemInHand(), player, canBuild);
         craftServer.getPluginManager().callEvent(event);

         return event;
     }

On the line immediately before the method we add a decorator, which tells New Relic that we want to trace this method (or transaction).


@Trace (dispatcher=true)

Then within the method we can start identifying what we want to collect and send to New Relic for analysis. In this instance the first thing that I did was explicitly name the transaction to something useful with the line:


NewRelic.setTransactionName(null, "callBlockPlaceEvent");

New Relic will use the method name by default, but I could have named this anything that I wanted, including “PlaceBlock” or “BlockPlacement”.

Finally, a little further down in the method I added the following code to gather the data I was interested in and send it to New Relic.


NewRelic.addCustomParameter("playerName", player.getName());
String blockname = placedBlock.getType().toString();
blockname = blockname.toLowerCase().replace("_"," ");
NewRelic.addCustomParameter("blockType", blockname);

This code sends two custom parameters to the New Relic platform, in addition to performance data, every time this method is called. The first custom parameter includes the name of the player who is breaking the block, and the second custom parameter includes a cleaned-up version of the block type.

Within the Insights Data Explorer, the raw data looks like this:

i6

Pulling it all together

Now that we have the Minecraft server set up to report data, we can compile the source code and deploy a new version of the server to our system. It is very useful to have a record of when new code was deployed so that we can correlate an increase or decrease in errors or performance to a specific release. Luckily, this is incredible easy to do in New Relic.

To create a deployment marker, we simply need to send a HTTP call to the API endpoint with the proper data and our api key. From the unix shell this can be done with the following curl command:


curl -H "x-api-key:${API_KEY}" -d "deployment[application_id]=1111111" -d "deployment[
description]=Deployment update via curl" -d "deployment[revision]=`date '+%Y%m%d%S'`" -d "deployment[changelog]=Java Source
 Update" -d "deployment[user]=Admin" https://api.newrelic.com/deployments.xml

With the new server running we can let our users enjoy playing Minecraft while collecting some interesting data for us to look at. At this point, we can log in to New Relic Insights and use the query interface to construct a New Relic Query Language (NRQL) statement that will generate a chart of the data for us.

NRQL> SELECT count(*) FROM Transaction Since 1 hour ago TIMESERIES AUTO WHERE name='OtherTransaction/Custom/callBlockPlaceEvent' FACET blockType

With the above command we are requesting to get the number of blocks that have been placed in the last hour broken out by block type. The resulting graph looks like this:

i7

Based on other transactions that were instrumented, we can construct a similar query to track all player deaths and the cause of their untimely demise.

NRQL> SELECT count(*) FROM Transaction WHERE name='OtherTransaction/Custom/callPlayerDeathEvent' SINCE 1 hour ago TIMESERIES AUTO FACET deathMessage

i9

Finally, I can set up New Relic APM to ping the HTTP endpoint used by the Dynmap plugin to ensure that the server is running and alert me if it is ever down.

i10

Play on

With all of this easily accessible information, it’s very easy to track the health of my Minecraft server and keep an eye on my players and their experiences within the world. Whether I want to simply track the creativity and success my players have in the game, or determine who used a bunch of TNT to destroy someone else’s structure, all of this is easily achievable using New Relic’s flexible platforms for application performance monitoring (APM) and data analytics (Insights).

If you’re interested in exploring yourself and using or working on the CraftBukkit source code with New Relic support, you can find it here.

Have fun!!

Sean is a Lead Site Reliability Engineer at New Relic. He is a long-time system administrator and operations engineer who has lived in places ranging from Alaska to Pakistan. View posts by .

Interested in writing for New Relic Blog? Send us a pitch!