From ef34025dd820e1a7a64907df29b068e0ec0c75ff Mon Sep 17 00:00:00 2001 From: Steve Date: Sat, 19 Dec 2015 15:32:29 +1300 Subject: [PATCH 1/5] Add support for download of world in zipfile WORLD=http://hostname/world.zip Zipfile should contain world/ directory, and optionally any required mods in mods/ as well as optionally a default server.properties file (overwritten by any env vars) Also export volumes /mods and /config, the contents of which are copied to /data/mods and /data/config. This allows shared read-only mounts for modules without affecting world. --- minecraft-server/Dockerfile | 8 ++++++-- minecraft-server/start-minecraft.sh | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/minecraft-server/Dockerfile b/minecraft-server/Dockerfile index e1f8a32f..9d8c84b5 100644 --- a/minecraft-server/Dockerfile +++ b/minecraft-server/Dockerfile @@ -12,7 +12,9 @@ RUN wget -O /usr/bin/jsawk https://github.com/micha/jsawk/raw/master/jsawk RUN chmod +x /usr/bin/jsawk RUN useradd -M -s /bin/false --uid 1000 minecraft \ && mkdir /data \ - && chown minecraft:minecraft /data + && mkdir /config \ + && mkdir /mods \ + && chown minecraft:minecraft /data /config /mods EXPOSE 25565 @@ -20,6 +22,8 @@ COPY start.sh /start COPY start-minecraft.sh /start-minecraft VOLUME ["/data"] +VOLUME ["/mods"] +VOLUME ["/config"] COPY server.properties /tmp/server.properties WORKDIR /data @@ -32,4 +36,4 @@ ENV UID=1000 ENV MOTD A Minecraft Server Powered by Docker ENV JVM_OPTS -Xmx1024M -Xms1024M ENV TYPE=VANILLA VERSION=LATEST FORGEVERSION=RECOMMENDED LEVEL=world PVP=true DIFFICULTY=easy \ - LEVEL_TYPE=DEFAULT GENERATOR_SETTINGS= + LEVEL_TYPE=DEFAULT GENERATOR_SETTINGS= WORLD= diff --git a/minecraft-server/start-minecraft.sh b/minecraft-server/start-minecraft.sh index 68e0973c..86cf3068 100755 --- a/minecraft-server/start-minecraft.sh +++ b/minecraft-server/start-minecraft.sh @@ -86,6 +86,14 @@ case $TYPE in esac +# If supplied with a URL for a world, download it and unpack +case "X$WORLD" in + Xhttp*) + wget -q -O - $WORLD > /data/world.zip + unzip /data/world.zip + ;; +esac + if [ ! -e server.properties ]; then cp /tmp/server.properties . @@ -193,4 +201,21 @@ if [ -n "$ICON" -a ! -e server-icon.png ]; then fi fi +# If any modules have been provided, copy them over +[ -d /data/mods ] || mkdir /data/mods +for m in /mods/*.jar +do + if [ -f "$m" ]; then + cp $m /data/mods + fi +done +[ -d /data/config ] || mkdir /data/config +for c in /config/* +do + if [ -f "$c" ]; then + cp -r $c /data/config + fi +done + + exec java $JVM_OPTS -jar $SERVER From e902c6f40fe7ab190a5345cae30a861c941ccf54 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 19 Dec 2015 21:48:09 +1300 Subject: [PATCH 2/5] Set command blocks enabled on servers by default; improved use of http world download. --- minecraft-server/server.properties | 2 +- minecraft-server/start-minecraft.sh | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/minecraft-server/server.properties b/minecraft-server/server.properties index ddad1242..598256ac 100644 --- a/minecraft-server/server.properties +++ b/minecraft-server/server.properties @@ -21,7 +21,7 @@ online-mode=true resource-pack= pvp=true difficulty=1 -enable-command-block=false +enable-command-block=true player-idle-timeout=0 gamemode=0 max-players=20 diff --git a/minecraft-server/start-minecraft.sh b/minecraft-server/start-minecraft.sh index 86cf3068..ff7a2f35 100755 --- a/minecraft-server/start-minecraft.sh +++ b/minecraft-server/start-minecraft.sh @@ -89,8 +89,19 @@ esac # If supplied with a URL for a world, download it and unpack case "X$WORLD" in Xhttp*) + echo "Downloading world via HTTP" wget -q -O - $WORLD > /data/world.zip - unzip /data/world.zip + echo "Unzipping word" + unzip -q /data/world.zip + rm -f /data/world.zip + if [ ! -d /data/world ]; then + echo Renaming world directory... + for i in /data/*/level.dat; do + if [ -f $i ]; then + mv -f `dirname $i` /data/world + fi + done + fi ;; esac @@ -201,19 +212,29 @@ if [ -n "$ICON" -a ! -e server-icon.png ]; then fi fi +# Make sure files exist to avoid errors +if [ ! -e banned-players.json ]; then + echo '' > banned-players.json +fi +if [ ! -e banned-ip.json ]; then + echo '' > banned-ip.json +fi + # If any modules have been provided, copy them over [ -d /data/mods ] || mkdir /data/mods for m in /mods/*.jar do if [ -f "$m" ]; then - cp $m /data/mods + echo Copying mod `basename $m` + cp -f $m /data/mods fi done [ -d /data/config ] || mkdir /data/config for c in /config/* do if [ -f "$c" ]; then - cp -r $c /data/config + echo Copying configuration `basename $m` + cp -rf $c /data/config fi done From 2427cae5a24f2f624e41aa9ff895cb14bdeca191 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 19 Dec 2015 21:50:44 +1300 Subject: [PATCH 3/5] Quote pathnames in case of spaces and such --- minecraft-server/start-minecraft.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/minecraft-server/start-minecraft.sh b/minecraft-server/start-minecraft.sh index ff7a2f35..65df7ce4 100755 --- a/minecraft-server/start-minecraft.sh +++ b/minecraft-server/start-minecraft.sh @@ -90,15 +90,16 @@ esac case "X$WORLD" in Xhttp*) echo "Downloading world via HTTP" - wget -q -O - $WORLD > /data/world.zip + wget -q -O - "$WORLD" > /data/world.zip echo "Unzipping word" unzip -q /data/world.zip rm -f /data/world.zip if [ ! -d /data/world ]; then echo Renaming world directory... for i in /data/*/level.dat; do - if [ -f $i ]; then - mv -f `dirname $i` /data/world + if [ -f "$i" ]; then + d=`dirname "$i"` + mv -f "$d" /data/world fi done fi @@ -225,16 +226,16 @@ fi for m in /mods/*.jar do if [ -f "$m" ]; then - echo Copying mod `basename $m` - cp -f $m /data/mods + echo Copying mod `basename "$m"` + cp -f "$m" /data/mods fi done [ -d /data/config ] || mkdir /data/config for c in /config/* do if [ -f "$c" ]; then - echo Copying configuration `basename $m` - cp -rf $c /data/config + echo Copying configuration `basename "$c"` + cp -rf "$c" /data/config fi done From e76068e63ec9894a351ba3e8103c0433d8377c51 Mon Sep 17 00:00:00 2001 From: Steve Shipway Date: Tue, 22 Dec 2015 19:47:42 +1300 Subject: [PATCH 4/5] Many improvements to help it work with automated schedulers - Allow -e WORLD=http://xxxx/world.zip to download archived world and unpack - Allow 'adventure' and 'spectator' game modes - Allow /mods and /config mounts for shared Forge mods without using /data - Quote many strings in case of blank values - Let VERSION take lowercase options, and set to LATEST if blank or invalid - Enable command blocks by default (needed for adventure worlds) - Allow DIFFICULTY to be given numerically - default to empty banned-players and banned-ip files to avoid error message --- minecraft-server/README.md | 42 ++++++++++++++++++++++++++++- minecraft-server/start-minecraft.sh | 36 ++++++++++++++++--------- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/minecraft-server/README.md b/minecraft-server/README.md index 3e57f472..19ba7391 100644 --- a/minecraft-server/README.md +++ b/minecraft-server/README.md @@ -104,7 +104,13 @@ but you can also choose to run a specific version with `-e FORGEVERSION=10.13.4. -e TYPE=FORGE -e FORGEVERSION=10.13.4.1448 \ -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server -In order to add mods, you will need to attach the container's `/data` directory +In order to add mods, you have two options. + +### Using the /data volume + +This is the easiest way if you are using a persistent `/data` mount. + +To do this, you will need to attach the container's `/data` directory (see "Attaching data directory to host filesystem”). Then, you can add mods to the `/path/on/host/mods` folder you chose. From the example above, the `/path/on/host` folder contents look like: @@ -127,6 +133,19 @@ up: docker stop mc docker start mc +### Using separate mounts + +This is the easiest way if you are using an ephemeral `/data` filesystem, +or downloading a world with the `WORLD` option. + +There are two additional volumes that can be mounted; `/mods` and `/config`. +Any files in either of these filesystems will be copied over to the main +`/data` filesystem before starting Minecraft. + +This works well if you want to have a common set of modules in a separate +location, but still have multiple worlds with different server requirements +in either persistent volumes or a downloadable archive. + ## Using Docker Compose Rather than type the server options below, the port mappings above, etc @@ -206,6 +225,8 @@ shortcut values: * creative * survival +* adventure +* spectator (only for Minecraft 1.8 or later) For example: @@ -258,6 +279,25 @@ where the default is "world": **NOTE:** if running multiple containers be sure to either specify a different `-v` host directory for each `LEVEL` in use or don't use `-v` and the container's filesystem will keep things encapsulated. +### Downloadable world + +Instead of mounting the `/data` volume, you can instead specify the URL of +a ZIP file containing an archived world. This will be downloaded, and +unpacked in the `/data` directory; if it does not contain a subdirectory +called `world/` then it will be searched for a file `level.dat` and the +containing subdirectory renamed to `world`. This means that most of the +archived Minecraft worlds downloadable from the Internet will already be in +the correct format. + +The ZIP file may also contain a `server.properties` file and `modules` +directory, if required. + + docker run -d -e WORLD=http://www.example.com/worlds/MySave.zip ... + +**NOTE:** Unless you also mount `/data` as an external volume, this world +will be deleted when the container is deleted. + + ## JVM Configuration ### Memory Limit diff --git a/minecraft-server/start-minecraft.sh b/minecraft-server/start-minecraft.sh index 65df7ce4..2a2b4527 100755 --- a/minecraft-server/start-minecraft.sh +++ b/minecraft-server/start-minecraft.sh @@ -16,22 +16,25 @@ if [ ! -e /data/eula.txt ]; then fi echo "Checking version information." -case $VERSION in - LATEST) +case "X$VERSION" in + X|XLATEST|Xlatest) VANILLA_VERSION=`wget -O - https://s3.amazonaws.com/Minecraft.Download/versions/versions.json | jsawk -n 'out(this.latest.release)'` ;; - SNAPSHOT) + XSNAPSHOT|Xsnapshot) VANILLA_VERSION=`wget -O - https://s3.amazonaws.com/Minecraft.Download/versions/versions.json | jsawk -n 'out(this.latest.snapshot)'` ;; - *) + X[1-9]*) VANILLA_VERSION=$VERSION ;; + *) + VANILLA_VERSION=`wget -O - https://s3.amazonaws.com/Minecraft.Download/versions/versions.json | jsawk -n 'out(this.latest.release)'` + ;; esac cd /data echo "Checking minecraft / forge type information." -case $TYPE in +case "$TYPE" in VANILLA) SERVER="minecraft_server.$VANILLA_VERSION.jar" @@ -49,7 +52,7 @@ case $TYPE in ;; *) - norm=`echo $VANILLA_VERSION | sed 's/^\([0-9]\+\.[0-9]\+\).*/\1/'` + norm=`echo "$VANILLA_VERSION" | sed 's/^\([0-9]\+\.[0-9]\+\).*/\1/'` ;; esac @@ -76,7 +79,7 @@ case $TYPE in FORGE_INSTALLER="forge-$normForgeVersion-installer.jar" SERVER="forge-$normForgeVersion-universal.jar" - if [ ! -e $SERVER ]; then + if [ ! -e "$SERVER" ]; then echo "Downloading $FORGE_INSTALLER ..." wget -q http://files.minecraftforge.net/maven/net/minecraftforge/forge/$normForgeVersion/$FORGE_INSTALLER echo "Installing $SERVER" @@ -88,7 +91,7 @@ esac # If supplied with a URL for a world, download it and unpack case "X$WORLD" in - Xhttp*) + X[Hh][Tt][Tt][Pp]*[Zz][iI][pP]) echo "Downloading world via HTTP" wget -q -O - "$WORLD" > /data/world.zip echo "Unzipping word" @@ -104,6 +107,9 @@ case "X$WORLD" in done fi ;; + *) + echo "Invalid URL given for world: Must be HTTP or HTTPS and a ZIP file" + ;; esac if [ ! -e server.properties ]; then @@ -151,16 +157,16 @@ if [ ! -e server.properties ]; then if [ -n "$DIFFICULTY" ]; then case $DIFFICULTY in - peaceful) + peaceful|0) DIFFICULTY=0 ;; - easy) + easy|1) DIFFICULTY=1 ;; - normal) + normal|2) DIFFICULTY=2 ;; - hard) + hard|3) DIFFICULTY=3 ;; *) @@ -181,6 +187,12 @@ if [ ! -e server.properties ]; then c*) MODE=1 ;; + a*) + MODE=2 + ;; + s*) + MODE=3 + ;; *) echo "ERROR: Invalid game mode: $MODE" exit 1 From a313c03fffb7854a24d892b3d759c04917c52672 Mon Sep 17 00:00:00 2001 From: Steve Shipway Date: Tue, 22 Dec 2015 20:39:53 +1300 Subject: [PATCH 5/5] Verify source URL and log if not working --- minecraft-server/README.md | 3 +++ minecraft-server/start-minecraft.sh | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/minecraft-server/README.md b/minecraft-server/README.md index 19ba7391..88a41392 100644 --- a/minecraft-server/README.md +++ b/minecraft-server/README.md @@ -297,6 +297,9 @@ directory, if required. **NOTE:** Unless you also mount `/data` as an external volume, this world will be deleted when the container is deleted. +**NOTE:** This URL must be accessible from inside the container. Therefore, +you should use an IP address or a globally resolveable FQDN, or else the +name of a linked container. ## JVM Configuration diff --git a/minecraft-server/start-minecraft.sh b/minecraft-server/start-minecraft.sh index 2a2b4527..92513ab0 100755 --- a/minecraft-server/start-minecraft.sh +++ b/minecraft-server/start-minecraft.sh @@ -93,15 +93,17 @@ esac case "X$WORLD" in X[Hh][Tt][Tt][Pp]*[Zz][iI][pP]) echo "Downloading world via HTTP" + echo "$WORLD" wget -q -O - "$WORLD" > /data/world.zip echo "Unzipping word" unzip -q /data/world.zip rm -f /data/world.zip if [ ! -d /data/world ]; then - echo Renaming world directory... + echo World directory not found for i in /data/*/level.dat; do if [ -f "$i" ]; then d=`dirname "$i"` + echo Renaming world directory from $d mv -f "$d" /data/world fi done @@ -229,8 +231,8 @@ fi if [ ! -e banned-players.json ]; then echo '' > banned-players.json fi -if [ ! -e banned-ip.json ]; then - echo '' > banned-ip.json +if [ ! -e banned-ips.json ]; then + echo '' > banned-ips.json fi # If any modules have been provided, copy them over