diff --git a/Dockerfile b/Dockerfile index 8d1fd7c5..fdddbf89 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,8 +72,9 @@ 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 +COPY --chmod=755 files/rconcmds /rconcmds -RUN dos2unix /start* /autopause/* /autostop/* +RUN dos2unix /start* /autopause/* /autostop/* /rconcmds/* ENTRYPOINT [ "/start" ] HEALTHCHECK --start-period=1m CMD mc-health diff --git a/README.md b/README.md index 99baf35f..8515131b 100644 --- a/README.md +++ b/README.md @@ -1657,6 +1657,50 @@ To also include the timestamp with each log, set `LOG_TIMESTAMP` to "true". The [init] 2022-02-05 16:58:33+00:00 Starting the Minecraft server... ``` +### Use RCON commands + +Feature is used run commands when the server starts, client connects, or client disconnects. +**Notes:** +* On clinet connect we only know there was a connection, and not who connected. RCON commands will need to be used for that. +* Using '|-' is preferred for yaml, this make sure only the correct new lines are in place for the commands. + +**On Server Start:** + +```yaml + RCON_CMDS_STARTUP: |- + /gamerule doFireTick false +``` + +**On Client Connection:** + +```yaml + RCON_CMDS_ON_CONNECT: |- + /team join New @a[team=] +``` + +**On Client Disconnect:** + +```yaml + RCON_CMDS_ON_DISCONNECT: |- + /gamerule doFireTick true +``` + +**Example of rules for new players** + +Uses team NEW and team OLD to track players on the server. So move player with no team to NEW, run a command, move them to team OLD. +[Reference Article](https://www.minecraftforum.net/forums/minecraft-java-edition/redstone-discussion-and/2213523-detect-players-first-join) + +```yaml + RCON_CMDS_STARTUP: |- + /gamerule doFireTick false + /team add New + /team add Old + RCON_CMDS_ON_CONNECT: |- + /team join New @a[team=] + /give @a[team=New] diamond_block + /team join Old @a[team=New] +``` + ## Autopause ### Description diff --git a/examples/docker-compose-rconcmd.yml b/examples/docker-compose-rconcmd.yml new file mode 100644 index 00000000..1ee09a74 --- /dev/null +++ b/examples/docker-compose-rconcmd.yml @@ -0,0 +1,24 @@ +version: '3' + +services: + minecraft: + image: ${IMAGE_TO_TEST:-itzg/minecraft-server} + ports: + - "25565:25565" + volumes: + - "mc:/data" + environment: + EULA: "TRUE" + # YAML Heredoc, be sure to use '|-' this will remove the first newline and final new line. + # This is versus '|' that will leaving with two empty strings at top and bottom. + RCON_CMDS_STARTUP: |- + /gamerule doFireTick false + /team add New + /team add Old + RCON_CMDS_ON_CONNECT: |- + /team join New @a[team=] + /give @a[team=New] diamond_block + /team join Old @a[team=New] + restart: unless-stopped +volumes: + mc: {} diff --git a/files/autopause/autopause-fcns.sh b/files/autopause/autopause-fcns.sh index a0466d1a..75411adb 100755 --- a/files/autopause/autopause-fcns.sh +++ b/files/autopause/autopause-fcns.sh @@ -20,12 +20,16 @@ mc_server_listening() { mc-monitor status --host localhost --port "$SERVER_PORT" --timeout 10s >& /dev/null } -java_clients_connected() { +java_clients_connections() { local connections if java_running ; then connections=$(mc-monitor status --host localhost --port "$SERVER_PORT" --show-player-count) else connections=0 fi - (( connections > 0 )) + echo $connections +} + +java_clients_connected() { + (( $(java_clients_connections) > 0 )) } diff --git a/files/rconcmds/rcon-cmds-daemon.sh b/files/rconcmds/rcon-cmds-daemon.sh new file mode 100644 index 00000000..2310c53f --- /dev/null +++ b/files/rconcmds/rcon-cmds-daemon.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +: "${RCON_CMDS_STARTUP:=}" +: "${RCON_CMDS_ON_CONNECT:=}" +: "${RCON_CMDS_ON_DISCONNECT:=}" +: "${RCON_CMDS_PERIOD:=10}" + +# needed for the clients connected function residing in autopause +# shellcheck source=/autopause/autopause-fcns.sh +. /autopause/autopause-fcns.sh + +# shellcheck source=start-utils +. ${SCRIPTS:-/}start-utils + +run_command(){ + rcon_cmd="$1" + logRcon "running - $rcon_cmd" + output=$(rcon-cli "$rcon_cmd") + logRcon "$output" +} + + +# wait for java process to be started +while : +do + if java_process_exists ; then + break + fi + sleep 0.1 +done + +CLIENTCONNECTIONS=0 +STATE=INIT + +while : +do + case X$STATE in + XINIT) + # Server startup + if mc_server_listening ; then + logRcon "MCServer is listening, running startup" + if [[ "$RCON_CMDS_STARTUP" ]]; then + while read -r cmd; do + run_command "$cmd" + done <<< "$RCON_CMDS_STARTUP" + fi + if [[ -z "$RCON_CMDS_ON_CONNECT" ]] && [[ -z "$RCON_CMDS_ON_DISCONNECT" ]]; then + logRcon "No addition rcon commands are given, stopping rcon cmd service" + exit 0 + fi + STATE=II + fi + ;; + XII) + # Main Loop looking for connections + CURR_CLIENTCONNECTIONS=$(java_clients_connections) + if (( CURR_CLIENTCONNECTIONS > CLIENTCONNECTIONS )) && [[ "$RCON_CMDS_ON_CONNECT" ]]; then + logRcon "Clients have Connected, running connect cmds" + while read -r cmd; do + run_command "$cmd" + done <<< "$RCON_CMDS_ON_CONNECT" + elif (( CURR_CLIENTCONNECTIONS < CLIENTCONNECTIONS )) && [[ "$RCON_CMDS_ON_DISCONNECT" ]]; then + logRcon "Clients have Disconnected, running disconnect cmds" + while read -r cmd; do + run_command "$cmd" + done <<< "$RCON_CMDS_ON_DISCONNECT" + fi + CLIENTCONNECTIONS=$CURR_CLIENTCONNECTIONS + ;; + *) + logRcon "Error: invalid state: $STATE" + ;; + esac + sleep "$RCON_CMDS_PERIOD" +done diff --git a/scripts/start-configuration b/scripts/start-configuration index e98d64bb..2c91a286 100755 --- a/scripts/start-configuration +++ b/scripts/start-configuration @@ -8,6 +8,10 @@ IFS=$'\n\t' : "${EULA:=}" : "${PROXY:=}" : "${RCON_PASSWORD_FILE:=}" +: "${RCON_CMDS_STARTUP:=}" +: "${RCON_CMDS_ON_CONNECT:=}" +: "${RCON_CMDS_ON_DISCONNECT:=}" +: "${RCON_CMDS_PERIOD:=10}" shopt -s nullglob @@ -124,6 +128,12 @@ if isTrue "${ENABLE_AUTOSTOP}"; then ${SCRIPTS:-/}start-autostop fi +if [[ "$RCON_CMDS_STARTUP" ]] || [[ "$RCON_CMDS_ON_CONNECT" ]] || [[ "$RCON_CMDS_ON_DISCONNECT" ]]; then + log "Starting RCON commands" + # shellcheck source=start-rconcmds + ${SCRIPTS:-/}start-rconcmds +fi + if versionLessThan 1.7; then echo " MC_HEALTH_EXTRA_ARGS=( diff --git a/scripts/start-rconcmds b/scripts/start-rconcmds new file mode 100644 index 00000000..ecdda81e --- /dev/null +++ b/scripts/start-rconcmds @@ -0,0 +1,32 @@ +#!/bin/bash + +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" + +: "${RCON_CMDS_STARTUP:=}" +: "${RCON_CMDS_ON_CONNECT:=}" +: "${RCON_CMDS_ON_DISCONNECT:=}" +: "${RCON_CMDS_PERIOD:=10}" +: "${SERVER_PORT:=25565}" +export RCON_CMDS_STARTUP +export RCON_CMDS_ON_CONNECT +export RCON_CMDS_ON_DISCONNECT +export RCON_CMDS_PERIOD +export SERVER_PORT + +log "Rcon cmds functionality enabled" + +isDebugging && set -x + +if ! [[ $RCON_CMDS_PERIOD =~ ^[0-9]+$ ]]; then + RCON_CMDS_PERIOD=10 + export RCON_CMDS_PERIOD + log "Warning: RCON_CMDS_PERIOD is not numeric, set to 10 (seconds)" +fi +if [ "$RCON_CMDS_PERIOD" -eq "0" ] ; then + RCON_CMDS_PERIOD=10 + export RCON_CMDS_PERIOD + log "Warning: RCON_CMDS_PERIOD must not be 0, set to 10 (seconds)" +fi + +/rconcmds/rcon-cmds-daemon.sh & diff --git a/scripts/start-utils b/scripts/start-utils index 5d1cf91a..eea451a3 100755 --- a/scripts/start-utils +++ b/scripts/start-utils @@ -117,6 +117,10 @@ function logAutostopAction() { echo "[$(date -Iseconds)] [Autostop] $*" } +function logRcon() { + echo "[Rcon loop] $*" +} + function normalizeMemSize() { local scale=1 case ${1,,} in