From 9dee47cb6d69e79db5f240cd3667cb4e3a3d0739 Mon Sep 17 00:00:00 2001 From: Geoff Bourne Date: Mon, 13 Dec 2021 21:35:14 -0600 Subject: [PATCH 1/7] docs: exclude dependabot from changelog --- .github/release.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..ddb3e480 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,4 @@ +changelog: + exclude: + authors: + - dependabot From cc885276f5857f7a11141b9eb66dc3bdcd297bfb Mon Sep 17 00:00:00 2001 From: Daniel Hoffend Date: Mon, 20 Dec 2021 02:24:02 +0100 Subject: [PATCH 2/7] fix: verify that the downloaded resource is a valid plugin (#1210) --- scripts/start-spiget | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/start-spiget b/scripts/start-spiget index 451312ab..4dfa7575 100755 --- a/scripts/start-spiget +++ b/scripts/start-spiget @@ -22,6 +22,20 @@ containsJars() { return 1 } +containsPlugin() { + file=${1?} + + pat='plugin.yml$' + + while read -r line; do + if [[ $line =~ $pat ]]; then + return 0 + fi + done <<<$(unzip -l "$file") + + return 1 +} + getResourceFromSpiget() { resource=${1?} @@ -81,9 +95,12 @@ downloadResourceFromSpiget() { log "Extracting contents of resource ${resource} into plugins" unzip -o -q -d /data/plugins "${tmpfile}" rm "${tmpfile}" - else + elif containsPlugin "${tmpfile}"; then log "Moving resource ${resource} into plugins" mv "${tmpfile}" "/data/plugins/${resource}.jar" + else + log "ERROR downloaded resource '${resource}' seems to be not a valid plugin" + exit 2 fi } From c15e231d7e8bec6c909d74904e61e8deae65e7ba Mon Sep 17 00:00:00 2001 From: Geoff Bourne Date: Mon, 20 Dec 2021 07:09:29 -0600 Subject: [PATCH 3/7] fix: write to console pipe as minecraft user (#1216) --- bin/mc-send-to-console | 2 +- scripts/start | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/mc-send-to-console b/bin/mc-send-to-console index a83f0e81..7615c241 100755 --- a/bin/mc-send-to-console +++ b/bin/mc-send-to-console @@ -12,4 +12,4 @@ if [ ! -p "${CONSOLE_IN_NAMED_PIPE}" ]; then exit 1 fi -echo "$@" > "${CONSOLE_IN_NAMED_PIPE:-/tmp/minecraft-console-in}" \ No newline at end of file +gosu minecraft bash -c "echo $* > '${CONSOLE_IN_NAMED_PIPE:-/tmp/minecraft-console-in}'" \ No newline at end of file diff --git a/scripts/start b/scripts/start index 9e252956..95c2736e 100755 --- a/scripts/start +++ b/scripts/start @@ -40,7 +40,7 @@ if ! isTrue "${SKIP_SUDO:-false}" && [ $(id -u) = 0 ]; then echo 'hosts: files dns' > /etc/nsswitch.conf fi - exec gosu ${runAsUser}:${runAsGroup} ${SCRIPTS:-/}start-configuration $@ + exec gosu ${runAsUser}:${runAsGroup} ${SCRIPTS:-/}start-configuration "$@" else - exec ${SCRIPTS:-/}start-configuration $@ + exec ${SCRIPTS:-/}start-configuration "$@" fi From fa4a0e92cee6325d2a4865da5945288f2f82f1ef Mon Sep 17 00:00:00 2001 From: Michael Kirsch Date: Tue, 21 Dec 2021 01:27:27 +0100 Subject: [PATCH 4/7] Autostop feature (#1212) --- Dockerfile | 6 ++- README.md | 22 +++++++++ examples/docker-compose-autostop.yml | 20 ++++++++ files/autostop/autostop-daemon.sh | 69 ++++++++++++++++++++++++++++ files/autostop/stop.sh | 6 +++ scripts/start-autostop | 34 ++++++++++++++ scripts/start-configuration | 9 ++++ scripts/start-utils | 8 ++++ 8 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 examples/docker-compose-autostop.yml create mode 100755 files/autostop/autostop-daemon.sh create mode 100755 files/autostop/stop.sh create mode 100755 scripts/start-autostop diff --git a/Dockerfile b/Dockerfile index 1be78a00..7beb2c13 100644 --- a/Dockerfile +++ b/Dockerfile @@ -76,7 +76,8 @@ ENV UID=1000 GID=1000 \ TYPE=VANILLA VERSION=LATEST \ ENABLE_RCON=true RCON_PORT=25575 RCON_PASSWORD=minecraft \ ENABLE_AUTOPAUSE=false AUTOPAUSE_TIMEOUT_EST=3600 AUTOPAUSE_TIMEOUT_KN=120 AUTOPAUSE_TIMEOUT_INIT=600 \ - AUTOPAUSE_PERIOD=10 AUTOPAUSE_KNOCK_INTERFACE=eth0 + AUTOPAUSE_PERIOD=10 AUTOPAUSE_KNOCK_INTERFACE=eth0 \ + ENABLE_AUTOSTOP=false AUTOSTOP_TIMEOUT_EST=3600 AUTOSTOP_TIMEOUT_INIT=1800 AUTOSTOP_PERIOD=10 COPY --chmod=755 scripts/start* / COPY --chmod=755 bin/ /usr/local/bin/ @@ -84,8 +85,9 @@ COPY --chmod=755 bin/mc-health /health.sh COPY --chmod=644 files/server.properties /tmp/server.properties COPY --chmod=644 files/log4j2.xml /tmp/log4j2.xml COPY --chmod=755 files/autopause /autopause +COPY --chmod=755 files/autostop /autostop -RUN dos2unix /start* /autopause/* +RUN dos2unix /start* /autopause/* /autostop/* ENTRYPOINT [ "/start" ] HEALTHCHECK --start-period=1m CMD mc-health diff --git a/README.md b/README.md index ad18af7f..48a6dd4a 100644 --- a/README.md +++ b/README.md @@ -1459,6 +1459,28 @@ The following environment variables define the behaviour of auto-pausing: * `AUTOPAUSE_KNOCK_INTERFACE`, default `eth0`
Describes the interface passed to the `knockd` daemon. If the default interface does not work, run the `ifconfig` command inside the container and derive the interface receiving the incoming connection from its output. The passed interface must exist inside the container. Using the loopback interface (`lo`) does likely not yield the desired results. +## Autostop + +An option to stop the server after a specified time has been added for niche applications (e.g. billing saving on AWS Fargate). The function is incompatible with the Autopause functionality, as they basically cancel out each other. + +Note that the docker container variables have to be set accordingly (restart policy set to "no") and that the container has to be manually restarted. + +A starting, example compose file has been provided in [examples/docker-compose-autostop.yml](examples/docker-compose-autostop.yml). + +Enable the Autostop functionality by setting: + +``` +-e ENABLE_AUTOSTOP=TRUE +``` + +The following environment variables define the behaviour of auto-stopping: +* `AUTOSTOP_TIMEOUT_EST`, default `3600` (seconds) + describes the time between the last client disconnect and the stopping of the server (read as timeout established) +* `AUTOSTOP_TIMEOUT_INIT`, default `1800` (seconds) + describes the time between server start and the stopping of the server, when no client connects inbetween (read as timeout initialized) +* `AUTOSTOP_PERIOD`, default `10` (seconds) + describes period of the daemonized state machine, that handles the stopping of the server + ## Running on RaspberryPi To run this image on a RaspberryPi 3 B+, 4, or newer, use any of the image tags [list in the Java version section](#running-minecraft-server-on-different-java-version) that specify `armv7` for the architecture, which includes `itzg/minecraft-server:latest`. diff --git a/examples/docker-compose-autostop.yml b/examples/docker-compose-autostop.yml new file mode 100644 index 00000000..5d45421a --- /dev/null +++ b/examples/docker-compose-autostop.yml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + minecraft: + image: itzg/minecraft-server + ports: + - "25565:25565" + volumes: + - "mc:/data" + environment: + EULA: "TRUE" + ENABLE_AUTOSTOP: "TRUE" + # More aggressive settings for demo purposes + AUTOSTOP_TIMEOUT_INIT: "30" + AUTOSTOP_TIMEOUT_EST: "20" + # Important not to auto-restart the server!!! + restart: "no" + +volumes: + mc: {} diff --git a/files/autostop/autostop-daemon.sh b/files/autostop/autostop-daemon.sh new file mode 100755 index 00000000..0d6aa2ad --- /dev/null +++ b/files/autostop/autostop-daemon.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# needed for the clients connected function residing in autopause +. /autopause/autopause-fcns.sh + +. ${SCRIPTS:-/}start-utils + +# wait for java process to be started +while : +do + if java_process_exists ; then + break + fi + sleep 0.1 +done + +STATE=INIT + +while : +do + case X$STATE in + XINIT) + # Server startup + if mc_server_listening ; then + TIME_THRESH=$(($(current_uptime)+$AUTOSTOP_TIMEOUT_INIT)) + logAutostop "MC Server listening for connections - stopping in $AUTOSTOP_TIMEOUT_INIT seconds" + STATE=II + fi + ;; + XII) + # Initial idle + if java_clients_connected ; then + logAutostop "Client connected - waiting for disconnect" + STATE=E + else + if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then + logAutostop "No client connected since startup - stopping server" + /autostop/stop.sh + exit 0 + fi + fi + ;; + XE) + # Established + if ! java_clients_connected ; then + TIME_THRESH=$(($(current_uptime)+$AUTOSTOP_TIMEOUT_EST)) + logAutostop "All clients disconnected - stopping in $AUTOSTOP_TIMEOUT_EST seconds" + STATE=I + fi + ;; + XI) + # Idle + if java_clients_connected ; then + logAutostop "Client reconnected - waiting for disconnect" + STATE=E + else + if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then + logAutostop "No client reconnected - stopping" + /autostop/stop.sh + exit 0 + fi + fi + ;; + *) + logAutostop "Error: invalid state: $STATE" + ;; + esac + sleep $AUTOSTOP_PERIOD +done diff --git a/files/autostop/stop.sh b/files/autostop/stop.sh new file mode 100755 index 00000000..1a9ed2f4 --- /dev/null +++ b/files/autostop/stop.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +. /start-utils + +logAutostopAction "Stopping Java process" +kill -SIGTERM 1 diff --git a/scripts/start-autostop b/scripts/start-autostop new file mode 100755 index 00000000..b71bf395 --- /dev/null +++ b/scripts/start-autostop @@ -0,0 +1,34 @@ +#!/bin/bash + +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" + +: "${SERVER_PORT:=25565}" +export SERVER_PORT + +log "Autostop functionality enabled" + +isDebugging && set -x + +if ! [[ $AUTOSTOP_PERIOD =~ ^[0-9]+$ ]]; then + AUTOSTOP_PERIOD=10 + export AUTOSTOP_PERIOD + log "Warning: AUTOSTOP_PERIOD is not numeric, set to 10 (seconds)" +fi +if [ "$AUTOSTOP_PERIOD" -eq "0" ] ; then + AUTOSTOP_PERIOD=10 + export AUTOSTOP_PERIOD + log "Warning: AUTOSTOP_PERIOD must not be 0, set to 10 (seconds)" +fi +if ! [[ $AUTOSTOP_TIMEOUT_EST =~ ^[0-9]+$ ]] ; then + AUTOSTOP_TIMEOUT_EST=3600 + export AUTOSTOP_TIMEOUT_EST + log "Warning: AUTOSTOP_TIMEOUT_EST is not numeric, set to 3600 (seconds)" +fi +if ! [[ $AUTOSTOP_TIMEOUT_INIT =~ ^[0-9]+$ ]] ; then + AUTOSTOP_TIMEOUT_INIT=1800 + export AUTOSTOP_TIMEOUT_INIT + log "Warning: AUTOSTOP_TIMEOUT_INIT is not numeric, set to 1800 (seconds)" +fi + +/autostop/autostop-daemon.sh & diff --git a/scripts/start-configuration b/scripts/start-configuration index f1eed964..cfeb0fcb 100755 --- a/scripts/start-configuration +++ b/scripts/start-configuration @@ -37,6 +37,11 @@ if isTrue "${ENABLE_AUTOPAUSE}" && isTrue "${EXEC_DIRECTLY:-false}"; then exit 1 fi +if isTrue "${ENABLE_AUTOPAUSE}" && isTrue "${ENABLE_AUTOSTOP}"; then + log "ENABLE_AUTOPAUSE=true is incompatible with ENABLE_AUTOSTOP=true" + exit 1 +fi + if [[ $PROXY ]]; then export http_proxy="$PROXY" export https_proxy="$PROXY" @@ -96,6 +101,10 @@ if isTrue "${ENABLE_AUTOPAUSE}"; then ${SCRIPTS:-/}start-autopause fi +if isTrue "${ENABLE_AUTOSTOP}"; then + ${SCRIPTS:-/}start-autostop +fi + if versionLessThan 1.7; then echo " MC_HEALTH_EXTRA_ARGS=( diff --git a/scripts/start-utils b/scripts/start-utils index 1a2ed977..8c77cbca 100755 --- a/scripts/start-utils +++ b/scripts/start-utils @@ -93,6 +93,14 @@ function logAutopauseAction() { echo "[$(date -Iseconds)] [Autopause] $*" } +function logAutostop() { + echo "[Autostop loop] $*" +} + +function logAutostopAction() { + echo "[$(date -Iseconds)] [Autostop] $*" +} + function normalizeMemSize() { local scale=1 case ${1,,} in From 52bf2ae094c76b51387e7145ac25df12c4bc419d Mon Sep 17 00:00:00 2001 From: itzg Date: Tue, 21 Dec 2021 00:27:44 +0000 Subject: [PATCH 5/7] docs: Auto update markdown TOC --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 48a6dd4a..cba30e01 100644 --- a/README.md +++ b/README.md @@ -131,10 +131,11 @@ By default, the container will download the latest version of the "vanilla" [Min * [Autopause](#autopause) * [Description](#description) * [Enabling Autopause](#enabling-autopause) + * [Autostop](#autostop) * [Running on RaspberryPi](#running-on-raspberrypi) * [Contributing](#contributing) - + From bf5824b6e55e5ac9f0c148eb1eb24d54a69ad86f Mon Sep 17 00:00:00 2001 From: Caden Kriese <13630061+cadenkriese@users.noreply.github.com> Date: Wed, 22 Dec 2021 06:01:23 -0700 Subject: [PATCH 6/7] Added support for downloading and installing datapacks (#1214) --- README.md | 11 ++++++ scripts/start-setupDatapack | 77 +++++++++++++++++++++++++++++++++++++ scripts/start-setupWorld | 2 +- 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100755 scripts/start-setupDatapack diff --git a/README.md b/README.md index cba30e01..71dc0952 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ By default, the container will download the latest version of the "vanilla" [Min * [Downloadable world](#downloadable-world) * [Cloning world from a container path](#cloning-world-from-a-container-path) * [Overwrite world on start](#overwrite-world-on-start) + * [Datapacks](#datapacks) * [Server configuration](#server-configuration) * [Message of the Day](#message-of-the-day) * [Difficulty](#difficulty) @@ -793,6 +794,16 @@ The following diagram shows how this option can be used in a compose deployment ### Overwrite world on start The world will only be downloaded or copied if it doesn't exist already. Set `FORCE_WORLD_COPY=TRUE` to force overwrite the world on every server start. +### Datapacks +Datapacks can be installed in a similar manner to mods/plugins. There are many environment variables which function in the same way they do for [mods](#working-with-mods-and-plugins): +* `DATAPACKS` +* `DATAPACKS_FILE` +* `REMOVE_OLD_DATAPACKS` +* `REMOVE_OLD_DATAPACKS_DEPTH` +* `REMOVE_OLD_DATAPACKS_INCLUDE` +* `REMOVE_OLD_DATAPACKS_EXCLUDE` +Datapacks will be placed in `/data/$LEVEL/datapacks` + ## Server configuration By default, the server configuration will be created and set based on the following environment variables, but only the first time the server is started. If the `server.properties` file already exists, the values in them will not be changed. diff --git a/scripts/start-setupDatapack b/scripts/start-setupDatapack new file mode 100755 index 00000000..b8134191 --- /dev/null +++ b/scripts/start-setupDatapack @@ -0,0 +1,77 @@ +#!/bin/bash + +set -e -o pipefail + +: "${REMOVE_OLD_DATAPACKS:=false}" +: "${DATAPACKS_FILE:=}" +: "${REMOVE_OLD_DATAPACKS_DEPTH:=1} " +: "${REMOVE_OLD_DATAPACKS_INCLUDE:=*.zip}" + +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" +isDebugging && set -x + +out_dir=/data/${LEVEL:-world}/datapacks + +# Remove old datapacks +if isTrue "${REMOVE_OLD_DATAPACKS}" && [ -z "${DATAPACKS_FILE}" ]; then + if [ -d "$out_dir" ]; then + find "$out_dir" -mindepth 1 -maxdepth ${REMOVE_OLD_DATAPACKS_DEPTH:-16} -wholename "${REMOVE_OLD_DATAPACKS_INCLUDE:-*}" -not -wholename "${REMOVE_OLD_DATAPACKS_EXCLUDE:-}" -delete + fi +fi + +if [[ "$DATAPACKS" ]]; then + mkdir -p "$out_dir" + + for i in ${DATAPACKS//,/ } + do + if isURL "$i"; then + log "Downloading datapack $i ..." + if ! get -o "${out_dir}" "$i"; then + log "ERROR: failed to download from $i into $out_dir" + exit 2 + fi + elif [[ -f "$i" && "$i" =~ .*\.zip ]]; then + log "Copying datapack located at $i ..." + out_file=$(basename "$i") + if ! cp "$i" "${out_dir}/$out_file"; then + log "ERROR: failed to copy from $i into $out_dir" + exit 2 + fi + elif [[ -d "$i" ]]; then + log "Copying datapacks from $i ..." + cp "$i"/*.zip "${out_dir}" + else + log "ERROR Invalid URL or path given in DATAPACKS: $i" + exit 2 + fi + done + +elif [[ "$DATAPACKS_FILE" ]]; then + if [ ! -f "$DATAPACKS_FILE" ]; then + log "ERROR: given DATAPACKS_FILE file does not exist" + exit 2 + fi + + mkdir -p "$out_dir" + + args=( + -o "${out_dir}" + --log-progress-each + --skip-existing + --uris-file "${DATAPACKS_FILE}" + ) + if isTrue "${REMOVE_OLD_DATAPACKS}"; then + args+=( + --prune-others "${REMOVE_OLD_DATAPACKS_INCLUDE}" + --prune-depth "${REMOVE_OLD_DATAPACKS_DEPTH}" + ) + fi + + if ! get "${args[@]}" ; then + log "ERROR: failed to retrieve one or more datapacks" + exit 1 + fi +fi + +exec "${SCRIPTS:-/}start-setupModpack" "$@" diff --git a/scripts/start-setupWorld b/scripts/start-setupWorld index d2803f7c..3484b46d 100755 --- a/scripts/start-setupWorld +++ b/scripts/start-setupWorld @@ -69,4 +69,4 @@ if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); fi fi -exec "${SCRIPTS:-/}start-setupModpack" "$@" +exec "${SCRIPTS:-/}start-setupDatapack" "$@" From 2f1fed822c4a65332302789c5952aeca3899a116 Mon Sep 17 00:00:00 2001 From: itzg Date: Wed, 22 Dec 2021 13:01:44 +0000 Subject: [PATCH 7/7] docs: Auto update markdown TOC --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71dc0952..c1fe70d6 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ By default, the container will download the latest version of the "vanilla" [Min * [Running on RaspberryPi](#running-on-raspberrypi) * [Contributing](#contributing) - +