From 39ae41c3c8505226d0d88f610afdf1f9c2dc5b01 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Mon, 30 May 2016 08:35:06 -0600 Subject: [PATCH] initial files --- Makefile | 20 +++ src/minecraftctl | 223 +++++++++++++++++++++++++++++++++ src/minecraftd-backup@.service | 6 + src/minecraftd-backup@.timer | 11 ++ src/minecraftd@.service | 18 +++ 5 files changed, 278 insertions(+) create mode 100644 Makefile create mode 100755 src/minecraftctl create mode 100644 src/minecraftd-backup@.service create mode 100644 src/minecraftd-backup@.timer create mode 100644 src/minecraftd@.service diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..87efeed --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +.DEFAULT = install + +PREFIX ?= / + +.PHONY: install +install: + install -D -m0644 src/minecraftd@.service \ + $(PREFIX)/usr/lib/systemd/system/minecraftd@.service + install -D -m0644 src/minecraft-backup@.service \ + $(PREFIX)/usr/lib/systemd/system/minecraftd-backup@.service + install -D -m0644 src/minecraft-backup@.timer \ + $(PREFIX)/usr/lib/systemd/system/minecraftd-backup@.timer + install -D -m0755 src/minecraftctl.sh $(PREFIX)/usr/bin/minecraftctl + +.PHONY: uninstall +uninstall: + rm $(PREFIX)/usr/lib/systemd/system/minecraftd@.service + rm $(PREFIX)/usr/lib/systemd/system/minecraftd-backup@.service + rm $(PREFIX)/usr/lib/systemd/system/minecraftd-backup@.timer + rm $(PREFIX)/usr/bin/minecraftctl diff --git a/src/minecraftctl b/src/minecraftctl new file mode 100755 index 0000000..ecb6d95 --- /dev/null +++ b/src/minecraftctl @@ -0,0 +1,223 @@ +#!/bin/bash + +# Launch Minecraft docker container + +set -e + +# Default listen port +# This is the published host port, +# internally the container always listens on 25565 +PORT=${PORT-25565} + +# Default max java heap size in MB +MAXHEAP=${MAXHEAP-2048} + +# Default min java heap size in MB +MINHEAP=${MINHEAP-512} + +# Path to docker executable +DOCKER=${DOCKER-$(which docker)} + +# Wether to mount a persistent volume for the server +# If true a volume will not be mounted and a restart will lose all world data +EPHEMERAL=${EPHEMERAL-false} + +# Directory for persisting minecraft data +DATA_DIR=${DATA_DIR-/srv/minecraft} +# Relative directory to $DATA_DIR for saving minecraft backups +BACKUP_DIR=${BACKUP_DIR-"./backups"} + +# IDs of minecraft user and group +MINECRAFT_UID=${MINECRAFT_UID-$(id -u minecraft)} +MINECRAFT_GID=${MINECRAFT_GID-$(id -g minecraft)} + +# Name of world dir +LEVEL=${LEVEL-world} + +# Game command timeout +TIMEOUT=${TIMEOUT-0} + +usage() { + echo "Usage: $0 [start|stop|restart|status|backup] " + echo "" + echo "Name is a name for the container and the data dir" + exit 1 +} + +# Check if container is running +running() { + status="$($DOCKER inspect -f '{{.State.Status}}' "$name" 2>/dev/null)" + test running = "$status" + return $? +} + +is_anyone_online() { + player_count=$(game_command list "players online:" | sed -r 's/.*([[:digit:]]+)\/[[:digit:]]+ players online:.*/\1/') + test $player_count -ne 0 + return $? +} + +# Start the minecraft docker container +start() { + if running + then + echo "Server already running..." + exit 0 + fi + stop_container + + vol_mount="--volume=$DATA_DIR/$name:/data" + if $EPHEMERAL + then + vol_mount="" + echo "Ephemeral server, a restart will lose all world data" + fi + + $DOCKER run -d -i \ + --name "$name" \ + $vol_mount \ + -p $PORT:25565 \ + -e "JVM_OPTS=-Xmx${MAXHEAP}M -Xms${MINHEAP}M" \ + -e EULA=true \ + -e "VERSION=$VERSION" \ + -e "DIFFICULTY=$DIFFICULTY" \ + -e "WHITELIST=$WHITELIST" \ + -e "OPS=$OPS" \ + -e "ICON=$ICON" \ + -e "MAX_PLAYERS=$MAX_PLAYERS" \ + -e "MAX_WORLD_SIZE=$MAX_WORLD_SIZE" \ + -e "ALLOW_NETHER=$ALLOW_NETHER" \ + -e "ANNOUNCE_PLAYER_ACHIEVEMENTS=$ANNOUNCE_PLAYER_ACHIEVEMENTS" \ + -e "ENABLE_COMMAND_BLOCK=$ENABLE_COMMAND_BLOCK" \ + -e "FORCE_GAMEMODE=$FORCE_GAMEMODE" \ + -e "GENERATE_STRUCTURES=$GENERATE_STRUCTURES" \ + -e "HARDCORE=$HARDCORE" \ + -e "MAX_BUILD_HEIGHT=$MAX_BUILD_HEIGHT" \ + -e "MAX_TICK_TIME=$MAX_TICK_TIME" \ + -e "SPAWN_MONSTERS=$SPAWN_MONSTERS" \ + -e "SPAWN_NPCS=$SPAWN_NPCS" \ + -e "VIEW_DISTANCE=$VIEW_DISTANCE" \ + -e "SEED=$SEED" \ + -e "MODE=$MODE" \ + -e "MOTD=$MOTD" \ + -e "PVP=$PVP" \ + -e "LEVEL_TYPE=$LEVEL_TYPE" \ + -e "GENERATOR_SETTINGS=$GENERATOR_SETTINGS" \ + -e "LEVEL=$LEVEL" \ + -e "WORLD=$WORLD" \ + -e "UID=$MINECRAFT_UID" \ + -e "GID=$MINECRAFT_GID" \ + itzg/minecraft-server + + echo "Started minecraft container $name" + +} + +# Send a command to the game server +game_command() { + expected=$2 + if [ -n "$expected" ] + then + # Start tailing log from end before we issue the command + timeout "$TIMEOUT" grep -m 1 "$expected" <($DOCKER logs -f --tail=0 $name) & + fi + # Issue command + echo "$1" | $DOCKER attach "$name" + # Wait for grep if any + wait +} + +# Do a world backup +backup() { + filename="$name-$(date +%Y_%m_%d_%H.%M.%S).tar.gz" + + game_command "say Starting backup..." + # Make sure we always turn saves back on + set +e + ret=0 + game_command "save-off" + ret=$(($ret + $?)) + game_command "save-all flush" "Saved the world" + ret=$(($ret + $?)) + sync + ret=$(($ret + $?)) + $DOCKER exec -u minecraft "$name" mkdir -p "/data/$BACKUP_DIR" + ret=$(($ret + $?)) + $DOCKER exec -u minecraft "$name" tar -C /data -czf "$BACKUP_DIR/$filename" --totals "$LEVEL" server.properties + ret=$(($ret + $?)) + game_command "save-on" + ret=$(($ret + $?)) + + game_command "say Backup finished" + exit $ret +} + +# Stop the server +stop() { + if running + then + if is_anyone_online + then + game_command "save-all" + for i in {10..1} + do + game_command "say Server shutting down in ${i}s..." + sleep 1 + done + game_command "say Shutting down..." + fi + game_command "stop" + # Wait for container to stop on its own now + $DOCKER wait "$name" + fi + + stop_container +} + +# Stop the container +stop_container() { + $DOCKER stop "$name" > /dev/null 2>&1 || true + $DOCKER rm "$name" > /dev/null 2>&1 || true +} + + +name=$2 +if [ -z "$name" ] +then + usage +fi + +if [ -n "$DEBUG" ] +then + set -x +fi + +case "$1" in +status) + if running + then + echo "Minecraft server $name is running" + exit 0 + else + echo "Minecraft server $name is stopped" + exit 2 + fi + ;; +start) + start + ;; +stop) + stop + ;; +restart) + stop + start + ;; +backup) + backup + ;; +*) + usage + ;; +esac + diff --git a/src/minecraftd-backup@.service b/src/minecraftd-backup@.service new file mode 100644 index 0000000..8ae6c68 --- /dev/null +++ b/src/minecraftd-backup@.service @@ -0,0 +1,6 @@ +[Unit] +Description=Backup %I Minceraft server + +[Service] +Type=oneshot +ExecStart=/usr/bin/minecraftctl backup %i diff --git a/src/minecraftd-backup@.timer b/src/minecraftd-backup@.timer new file mode 100644 index 0000000..45fca60 --- /dev/null +++ b/src/minecraftd-backup@.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Weekly backup of Minecraft server %i + +[Timer] +OnCalendar=weekly +Persistent=true + + +[Install] +WantedBy=multi-user.target + diff --git a/src/minecraftd@.service b/src/minecraftd@.service new file mode 100644 index 0000000..1df8413 --- /dev/null +++ b/src/minecraftd@.service @@ -0,0 +1,18 @@ +[Unit] +Description=Minecraft Container %I +After=docker.service +Requires=docker.service + +[Service] +TimeoutStartSec=0 +Restart=always +EnvironmentFile=-/etc/minecraft/%i +ExecStartPre=/usr/bin/minecraftctl start %i +ExecStart=/usr/bin/docker logs -f %i + +ExecStop=-/usr/bin/minecraftctl stop %i +ExecStopPost=-/usr/bin/minecraftctl stop %i + +[Install] +WantedBy=multi-user.target +