Building Minecraft mods with MCP_Deobfuscate

Today I set up a Jenkins to build my Minecraft mods, and had to basically teach it how to set up the complete development environment for MCP_Deobfuscate to work in. So now I can quite confidently share with you all the actual, fully-working steps it takes to acquire such a well-working build system that can build a mod in less than 10 seconds (on a Linode, at least).

MCPDeobfuscate

First things first, we need to build the mcp_deobfuscate jar. This is a Maven build, so Jenkins actually speaks it natively and is quite clever about it, so all we need to do is tell it the source is at git://github.com/FunnyMan3595/mcp_deobfuscate.git and that our Goals and options are "clean install". I've also got (possibly unnecessarily) the post-build action Archive the artifacts matching "**/mcp_deobfuscate-*.jar".

That was the exceptionally easy part. Now we move to the tricky bit: setting up the MCP+Forge environment.

MCPForge

The first thing I have for it is the sole build trigger: I have it building after MCPDeobfuscate, so that when the latter updates, it rebuilds my MCP for me.

Most importantly, I have it building in a custom workspace (hidden under Advanced Project Options, if you're having trouble finding it), namely one I named mcp-build. This ensures I have a well-known and easily-determinable location for my MCP, which will be very important once we get to setting up the mods themselves.

I also have it cleaning the workspace before starting to build, because trying to build on top of an old build is just likely to fail spectacularly in new and impressive ways.

After that, I have... a whole lot of an initial shell script:

#!/bin/bash

wget http://go.narc.ro/mcp-current -O mcp-current.zip && unzip mcp-current.zip
wget http://go.narc.ro/forge-current -O forge-current.zip && unzip forge-current.zip

These are mostly an artifact of how I've got my system set up -- I like specifying what MCP and Forge versions to use, but don't want to have them hard-coded in the script itself. The go.narc.ro redirector is my solution, but you could as easily just go ahead and replace those with links to the actual MCP and Forge-src downloads, or make your own alternative.

This just makes sure our build directory contains what it should: MCP itself, with Forge inside the forge/ subdirectory. MCP_Deobfuscate relies on this particular structure, and it should still be fully supported.

#!/bin/bash

# Ask Forge to patch itself into the innards of MCP
cd forge && python install.py

# and reobfuscate the resulting .jar -- we need it that way for mcp_deobfuscate to work properly
cd .. && python runtime/reobfuscate.py

# Set up this fairly important dependency:
ln -sv ../jars/bin/lwjgl.jar lib/

Now, after this, I have a new build step: Copy artifacts from another project, namely from MCPDeobfuscate's latest successful build, copying **/mcp_deobfuscate-*.jar into runtime/bin, with the Flatten directories option checked.

Next up, more shell scripting:

#!/bin/bash

# Bring in mcp_deobfuscate's dependencies, like the README says:
ln -sv ~/.m2/repository/org/ow2/asm/asm-all/4.0/asm-all-4.0.jar runtime/bin
ln -sv ~/.m2/repository/com/beust/jcommander/1.29/jcommander-1.29.jar runtime/bin

# And grab the mcp_interface, too:
git clone -b mcp_interface git://github.com/FunnyMan3595/mcp_deobfuscate.git mcp_interface
cp mcp_interface/runtime/*.py runtime/

# Have the scripts generate their directories:
python runtime/deobfuscate_libs.py
python runtime/recompile_mods.py

# And get our obfuscated dependency libs in place so we can deobfuscate them, too:
cp ../mcp-lib-obf/*.{jar,zip} lib-obf
python runtime/deobfuscate_libs.py

And that's it, the MCP directory is now ready to be built in, and you should generally only need to re-build it when you change MCP or Forge versions.

At the end here, note the next-to-last step: copying from ../mcp-lib-obf/*.{jar,zip}. This is a symlink I have in place that points to the lib-obf in my normal development environment, on the theory that the libraries in my dev environment should match the requirements for the build environment.

Generally, I expect this to work reasonably well, but there is one very important part that just doesn't happen automatically: if the contents of the dev lib-obf change, I have to manually run deobfuscate_libs in jenkins' name. A better option would be to split that part into its own job, so I can trigger it without forcing a complete rebuild of the MCP environment. For that matter, it might not be a bad idea to split off the mcp_deobfuscate installation part for the same reason. These improvements, however, are left as an exercise to the reader.

And a mod

So now we have a fully working MCP with Forge and mcp_deobfuscate, it's time to show how the average mod is configured. These guys all look mostly the same, only the name changes:

  • Set GitHub project to the correct one (in my case, https://github.com/narc0tiq/LiquidUU/) if you want the Jenkins to link back. This is purely optional and display-only, but it's nice to have.
  • Under Advanced Project Options you want to Block build when upstream project is building and Use custom workspace, specifically mcp-build/mods/LiquidUU. Remember that mcp_deobfuscate will give your final package name based on the name here.
  • Under Source Code Management, our code is coming from a Git repository, namely git://github.com/narc0tiq/LiquidUU.git. We only want to build the master branch, which is probably the default but why not be certain?
  • Build Triggers are to Build after other projects are built, namely MCPForge, and to Build when a change is pushed to GitHub. You could also have your Jenkins poll your SCM periodically, but I prefer reacting to push notifications instead.
  • For our Build Environment, we want to Delete workspace before build starts, hit Advanced and tell it to only bother deleting LiquidUU*. You'll see why in a moment.

Finally, we come to the build script itself, which is exceptionally simple:

#!/bin/bash

read VERSION <conf/VERSION
echo "$VERSION.$BUILD_NUMBER" > conf/VERSION

pushd ../..
python runtime/recompile_mods.py $JOB_NAME
popd

cp ../../packages/${JOB_NAME}* .

The last part is important, and the reason we partially clear our workspace: Jenkins doesn't normally look outside the workspace for build artifacts, but mcp_deobfuscate puts them outside the workspace we defined. Rather than have to mess about with a bigger workspace (and more complex git deployment), we'll just use our build script to grab the new package and stick it somewhere easily findable.

This brings us to the last setting, a post-build action of Archive the artifacts matching LiquidUU*. That gives us the nice list we have here at Last successful build artifacts and finish all the setuppy bits.

If all goes well, your project should now build successfully whenever you push to its GitHub, or when MCP updates for whatever reason. You should also be able to copy the job settings to build another project, as long as you remember to replace the project name in all the places it's hard-coded.

I hope this helps you start using the almost magical mcp_deobfuscate -- the instructions should be easy enough to perform manually to set up a regular build environment, and you should now know how to get your project building automatically, as well.