diff --git a/.gitignore b/.gitignore index 6c5156b..d8c21ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ data .env +screenlog.0 diff --git a/Dockerfile b/Dockerfile index 882898f..e018d7e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN groupadd -g 1000 minecraft && \ # Install scripting dependencies RUN apt-get update && \ - apt-get install -y curl gpg jq screen && \ + apt-get install -y curl gpg jq procps screen strace && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..991f1cd --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +CONTAINER = minecraft-minecraft-1 + +.PHONY: default build clean install +default: build + +build: + docker compose build + +clean: + rm -f screenlog.0 + docker compose down --rmi all + docker builder prune -f + +install: build + touch screenlog.0 + docker compose up -d && \ + docker logs -f $(CONTAINER) diff --git a/README.md b/README.md index 12a8e76..0256576 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,24 @@ # Minecraft Docker Image This Dockerfile sets up a Minecraft server based on the `debian-slim` image. +## Quick Start By running the following and building this image, you are agreeing to [Minecraft's EULA](https://www.minecraft.net/en-us/eula): ``` echo "EULA=true" > .env ``` -To build and test the image: +Build the image using the Makefile: +``` +make build +``` + +Optionally, build _and_ run to test it: +``` +make install +``` + +Feel free to use `docker compose` directly to build and test: ``` docker compose build docker compose up -d diff --git a/docker-compose.yml b/docker-compose.yml index bc34f4a..d827fe0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,3 @@ -volumes: - minecraft: - services: minecraft: build: @@ -12,12 +9,10 @@ services: image: ${IMAGE:-minecraft}:${TAG:-latest} ports: - "0.0.0.0:25565:25565" - #volumes: - # - minecraft:/app/world environment: EULA: "${EULA:-false}" DEBUG: "${DEBUG:-false}" - JVM_OPTS: "-Xms1G -Xmx2G" + JVM_OPTS: "${JAVA_OPTS:--Xms1G -Xmx2G}" SETTINGS_gamemode: "${GAMEMODE:-survival}" SETTINGS_hardcore: "${HARDCORE:-false}" SETTINGS_motd: "${MOTD:-A Minecraft Server}" diff --git a/entrypoint.sh b/entrypoint.sh index 6ea47b4..0ea2776 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,5 +1,44 @@ #!/bin/bash +# Check if the minecraft screen is still running +check_screen() { + if [ "$(screen -ls | grep -cE '[0-9]+\.minecraft')" -eq 1 ]; then + return 0 + else + return 1 + fi +} + +# Function to stop the server gracefully +stop_server() { + if check_screen; then + # Run 'stop' inside screen and wait for the screen to exit + /usr/bin/screen -p 0 -S minecraft -X eval 'stuff "stop"\015' + wait "$STRACE_PID" + + # Stop tail -f to stdout + if kill -0 "$TAIL_PID" 2>/dev/null; then + kill "$TAIL_PID" + fi + + # Check only this script is running (PID 1) and pgrep (2 PIDs total) + PGREP_OUTPUT="$(pgrep .)" + if ! [ "$(echo "$PGREP_OUTPUT" | wc -l)" -eq 2 ]; then + echo "[WARN] Some processes might not have exited:" + echo "$PGREP_OUTPUT" + exit 1 + fi + + # Exit cleanly + echo "[INFO] Server stopped gracefully" + exit 0 + else + echo "[ERROR]: Can't find which screen to use" + screen -ls + exit 1 + fi +} + # Enable debug mode DEBUG="${DEBUG:-false}" if [ "$DEBUG" = "true" ]; then @@ -32,8 +71,9 @@ while IFS='=' read -r ENVVAR VALUE ; do rm "${FILE}.bak" fi else - [ "$DEBUG" = "true" ] && \ + if [ "$DEBUG" = "true" ]; then echo "[DEBUG] \"$ENVVAR\" doesn't have the prefix \"$PREFIX\"" + fi fi done < <(env) @@ -43,17 +83,33 @@ if [ "$DEBUG" = "true" ]; then cat "$FILE" fi -# Pre-create the screen log -touch screenlog.0 +# Set up a SIGTERM signal trap to stop the server +trap 'stop_server' SIGTERM # Run server in screen (without attaching) +echo "[INFO] Starting Minecraft server" /usr/bin/screen -dmS minecraft -L \ - bash -c " - sleep 0.5 - [ $DEBUG = true ] && echo '[DEBUG] Starting server' - /usr/bin/java $JVM_OPTS -jar server.jar --nogui - " + bash -c "/usr/bin/java $JVM_OPTS -jar server.jar --nogui" -# Tail screen log to container stdout -[ "$DEBUG" = "true" ] && echo "[DEBUG] Tailing screenlog.0" -tail -f screenlog.0 +# Get screen PID +[ "$DEBUG" = "true" ] && screen -ls +SCREEN_PID="$( + screen -ls | grep -oE '[0-9]+\.minecraft' | cut -d. -f1 +)" + +# Check screen PID +if ! kill -0 "$SCREEN_PID" 2>/dev/null; then + echo "[ERROR] Cannot find Minecraft screen (PID: \"$SCREEN_PID\")" + exit 1 +fi + +# Output logs to stdout (touch in case slow to create) +touch screenlog.0 +tail -f screenlog.0 & +TAIL_PID="$!" + +# Wait for screen to exit +strace -e exit -e signal=none -p "$SCREEN_PID" 2>/dev/null & +STRACE_PID="$!" +[ "$DEBUG" = "true" ] && ps aux +wait "$STRACE_PID"