I run a fairly small Minecraft server with my brother and a few friends, and one of the requests we had was for a dynamic world map, using the Dynmap Bukkit plugin.

I thought this would be an interesting challenge, as the game is hosted at Multiplay, who don’t provide hosting or port allocations (as far as I can see, anyway). This would prove the challenge, as Dynmap includes its own web server, which obviously wouldn’t be able to run.

I discovered Dynmap also offered proxying via Apache/Nginx which would’ve been suitable, but for the same problem - Multiplay don’t provide hosting!

After some fiddling with the configuration, I ended up FTP’ing the Dynmap output to the Pyxis web server. This worked, but it essentially made Dynmap redundant as no real dynamic features could be used - it’d always rely on a cron running every few minutes.

After some Googling, I came across Minecraft Overviewer, which - apart from having excellent documentation - generates pretty nice isometric maps.

The original plan was to run Overviewer on the Pyxis web server, but that proved to be an issue due to the amount of system resources it used generating the map (which itself ended up at around 600MB) at the highest/best smooth-lighting rendering.

What I decided on in the end was to run Overviewer on a separate machine (actually a work machine) which would act as a proxy between Multiplay and the Pyxis web server. Not ideal, but better to do that than get my account terminated for excessive resource usage!

Below is the Bash script I came up with. It’s pretty rough, but does the job nicely and runs every 10 minutes, which seems reasonable given it takes a few minutes for Overviewer to re-generate the map.

If you’re interested in using this yourself, scroll down past the code to find further information.

#!/bin/bash
working_dir="/home/nick/tmp/mcmap-import"
(
        flock -w 300 200 || exit 1

        if [[ -d "$working_dir/world" ]]; then
                echo -n "Removing old world directory... "
                rm -rf "$working_dir/world"
                echo "done"
        fi

        mkdir -p "$working_dir"
        cd "$working_dir"

        echo -n "Fetching world from Multiplay... "
        wget -q -nH --cut-dirs=3 -m ftp://xx.xx.xx.xx/minecraft/beta/server-id/world
        echo "done"

        sleep 2

        echo -n "Merging with current world... "
        rsync -av --delete world/ world-ref/
        echo "done"

        sleep 2

        echo -n "Generating... "
        ~/bin/overviewer.py --config="$working_dir/mco.conf"
        echo "done"

        sleep 2

        echo -n "Pushing to Pyxis... "
        rsync -av --delete world-live/ user@host:~/webapps/pyxis/map/
        echo "done"
) 200>$working_dir/cron.lock

Prerequisites

You’ll need the following in order for the script to work:

  1. wget
  2. rsync
  3. overviewer.py (I installed it into my home directory - just change the path on line 75)

Setup

First, create in your home directory a file named .wgetrc - this will contain your FTP username and password, without exposing the credentials on the command line or as environment variables.

username = you
password = example

Second, make sure you have an SSH public key setup if you’re pushing via rsync to your live server. You could use FTP, but ideally you want to delete any extraneous files from the live site at the same time.

Third, create an Overviewer configuration file named mco.conf in the working directory. See the documentation for further information.

worlds["Pyxis"] = "/home/nick/tmp/mcmap-import/world-ref"
outputdir = "/home/nick/tmp/mcmap-import/world-live"
texturepath = "/home/nick/tmp/mcmap-import"
renders["smooth-lighting"] = {
        'world': 'Pyxis',
        'title': 'Pyxis',
        }

Explanation

Here’s a quick run-through of how the script works:

  1. Create a lock to ensure the script doesn’t run multiple times - this is mainly useful for cron, as overviewer.py takes a few minutes, and we don’t want the next cron to kick off and start doing the same thing
  2. If it exists, remove the previous world directory from the working directory. This probably isn’t necessary, but I like to keep things obvious
  3. Create the working directory (it’ll do nothing if it already exists) and change into it
  4. Download the world directory from Multiplay. By default, wget creates a set of directories named xx.xx.xx.xx/minecraft/beta/server-id - we want the world to be stored in the current directory, so we tell wget not to create a host directory (the IP) and to remove the first 3 directories, leaving us with just world
  5. Merge the world we’ve just downloaded with the previous world. This will remove old files, add new files and update any changed files. This step is important, as tools such as WorldEdit can remove blocks, and unless you merge the worlds, overviewer.py won’t be aware that they have been removed - resulting in glitched maps
  6. Generate the map with Overviewer. In most cases this will just perform an incremental generation, but the first one can take a while
  7. Push the world to the Pyxis web server

Note that this script essentially keeps 3 copies of the world - the first, world is always the version most recently downloaded from Multiplay. The next, world-ref is the world that should always exist, and which is always merged with world. The last, world-live contains the generated map from overviewer.py, and which is uploaded to the Pyxis web server.

Any questions, feedback, etc - let me know in the comments 😄