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/README.md b/minecraft-server/README.md index 3e57f472..88a41392 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,28 @@ 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. + +**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 ### Memory Limit 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 68e0973c..92513ab0 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" @@ -86,6 +89,31 @@ case $TYPE in esac +# If supplied with a URL for a world, download it and unpack +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 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 + fi + ;; + *) + echo "Invalid URL given for world: Must be HTTP or HTTPS and a ZIP file" + ;; +esac + if [ ! -e server.properties ]; then cp /tmp/server.properties . @@ -131,16 +159,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 ;; *) @@ -161,6 +189,12 @@ if [ ! -e server.properties ]; then c*) MODE=1 ;; + a*) + MODE=2 + ;; + s*) + MODE=3 + ;; *) echo "ERROR: Invalid game mode: $MODE" exit 1 @@ -193,4 +227,31 @@ 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-ips.json ]; then + echo '' > banned-ips.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 + 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 "$c"` + cp -rf "$c" /data/config + fi +done + + exec java $JVM_OPTS -jar $SERVER