Add rcon to backup script
This commit is contained in:
parent
a943f093a6
commit
22a579a19c
5 changed files with 272 additions and 101 deletions
|
@ -10,7 +10,6 @@ Supports servers running in [screen](https://en.wikipedia.org/wiki/GNU_Screen),
|
||||||
- Manage deletion of old backups
|
- Manage deletion of old backups
|
||||||
- "thin" - keep last 24 hourly, last 30 daily, and use remaining space for weekly backups
|
- "thin" - keep last 24 hourly, last 30 daily, and use remaining space for weekly backups
|
||||||
- "sequential" - delete oldest backup
|
- "sequential" - delete oldest backup
|
||||||
- Choose your own compression algorithm (tested with: `gzip`, `xz`, `zstd`)
|
|
||||||
- Works on vanilla (no plugins required)
|
- Works on vanilla (no plugins required)
|
||||||
- Print backup status to the Minecraft chat
|
- Print backup status to the Minecraft chat
|
||||||
|
|
||||||
|
@ -66,11 +65,11 @@ tar -xzvf /path/to/backups/2019-04-09_02-15-01.tar.gz
|
||||||
Then you can move your restored world (`restored-world` in this case) to your Minecraft server folder and rename it (usually called `world`) so the Minecraft server uses it.
|
Then you can move your restored world (`restored-world` in this case) to your Minecraft server folder and rename it (usually called `world`) so the Minecraft server uses it.
|
||||||
|
|
||||||
## Why not use `tar` directly?
|
## Why not use `tar` directly?
|
||||||
If the Minecraft server is currently running, you need to disable world autosaving, or you will likely get an error like this:
|
If you use `tar` while the server is running, you will likely get an error like this because Minecraft autosaves the world periodically:
|
||||||
```
|
```
|
||||||
tar: /some/path/here/world/region/r.1.11.mca: file changed as we read it
|
tar: /some/path/here/world/region/r.1.11.mca: file changed as we read it
|
||||||
```
|
```
|
||||||
This script will take care of disabling and then re-enabling autosaving for you, and also alert players in the chat of successful backups or errors. This script also manages deleting old backups.
|
To fix this problem, the backup script disables autosaving with the `save-off` Minecraft command before running `tar` and then re-enables autosaving after `tar` is done.
|
||||||
|
|
||||||
## Help
|
## Help
|
||||||
- Make sure the compression algorithm you specify is installed on your system. (zstd is not installed by default)
|
- Make sure the compression algorithm you specify is installed on your system. (zstd is not installed by default)
|
||||||
|
|
125
backup.sh
125
backup.sh
|
@ -47,9 +47,9 @@ while getopts 'a:cd:e:f:hi:l:m:o:p:qs:vw:' FLAG; do
|
||||||
echo "-o Output directory"
|
echo "-o Output directory"
|
||||||
echo "-p Prefix that shows in Minecraft chat (default: Backup)"
|
echo "-p Prefix that shows in Minecraft chat (default: Backup)"
|
||||||
echo "-q Suppress warnings"
|
echo "-q Suppress warnings"
|
||||||
echo "-s Minecraft server screen name"
|
echo "-s Screen name, tmux session name, or hostname:port:password for rcon"
|
||||||
echo "-v Verbose mode"
|
echo "-v Verbose mode"
|
||||||
echo "-w Window manager: screen (default) or tmux"
|
echo "-w Window manager: screen (default), tmux, rcon"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
i) SERVER_WORLD=$OPTARG ;;
|
i) SERVER_WORLD=$OPTARG ;;
|
||||||
|
@ -71,6 +71,116 @@ log-warning () {
|
||||||
echo -e "\033[0;33mWARNING:\033[0m $*"
|
echo -e "\033[0;33mWARNING:\033[0m $*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcon-command () {
|
||||||
|
HOST="$(echo $1 | cut -d: -f1)"
|
||||||
|
PORT="$(echo $1 | cut -d: -f2)"
|
||||||
|
PASSWORD="$(echo $1 | cut -d: -f3-)"
|
||||||
|
COMMAND="$2"
|
||||||
|
|
||||||
|
reverse-hex-endian () {
|
||||||
|
# Given a 4-byte hex integer, reverse endianness
|
||||||
|
while read -r -d '' -N 8 INTEGER; do
|
||||||
|
echo "$INTEGER" | sed -E 's/(..)(..)(..)(..)/\4\3\2\1/'
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
decode-hex-int () {
|
||||||
|
# decode little-endian hex integer
|
||||||
|
while read -r -d '' -N 8 INTEGER; do
|
||||||
|
BIG_ENDIAN_HEX=$(echo "$INTEGER" | reverse-hex-endian)
|
||||||
|
echo "$((16#$BIG_ENDIAN_HEX))"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
stream-to-hex () {
|
||||||
|
xxd -ps
|
||||||
|
}
|
||||||
|
|
||||||
|
hex-to-stream () {
|
||||||
|
xxd -ps -r
|
||||||
|
}
|
||||||
|
|
||||||
|
encode-int () {
|
||||||
|
# Encode an integer as 4 bytes in little endian and return as hex
|
||||||
|
INT="$1"
|
||||||
|
# Source: https://stackoverflow.com/a/9955198
|
||||||
|
printf "%08x" "$INT" | sed -E 's/(..)(..)(..)(..)/\4\3\2\1/'
|
||||||
|
}
|
||||||
|
|
||||||
|
encode () {
|
||||||
|
# Encode a packet type and payload for the rcon protocol
|
||||||
|
TYPE="$1"
|
||||||
|
PAYLOAD="$2"
|
||||||
|
REQUEST_ID="$3"
|
||||||
|
PAYLOAD_LENGTH="${#PAYLOAD}"
|
||||||
|
TOTAL_LENGTH="$((4 + 4 + PAYLOAD_LENGTH + 1 + 1))"
|
||||||
|
|
||||||
|
OUTPUT=""
|
||||||
|
OUTPUT+=$(encode-int "$TOTAL_LENGTH")
|
||||||
|
OUTPUT+=$(encode-int "$REQUEST_ID")
|
||||||
|
OUTPUT+=$(encode-int "$TYPE")
|
||||||
|
OUTPUT+=$(echo -n "$PAYLOAD" | stream-to-hex)
|
||||||
|
OUTPUT+="0000"
|
||||||
|
|
||||||
|
echo -n "$OUTPUT" | hex-to-stream
|
||||||
|
}
|
||||||
|
|
||||||
|
read-response () {
|
||||||
|
# read next response packet and return the payload text
|
||||||
|
HEX_LENGTH=$(head -c4 <&3 | stream-to-hex | reverse-hex-endian)
|
||||||
|
LENGTH=$((16#$HEX_LENGTH))
|
||||||
|
|
||||||
|
RESPONSE_PAYLOAD=$(head -c $LENGTH <&3 | stream-to-hex)
|
||||||
|
echo -n "$RESPONSE_PAYLOAD"
|
||||||
|
}
|
||||||
|
|
||||||
|
response-request-id () {
|
||||||
|
echo -n "${1:0:8}" | decode-hex-int
|
||||||
|
}
|
||||||
|
|
||||||
|
response-type () {
|
||||||
|
echo -n "${1:8:8}" | decode-hex-int
|
||||||
|
}
|
||||||
|
|
||||||
|
response-payload () {
|
||||||
|
echo -n "${1:16:-4}" | hex-to-stream
|
||||||
|
}
|
||||||
|
|
||||||
|
login () {
|
||||||
|
PASSWORD="$1"
|
||||||
|
encode 3 "$PASSWORD" 12 >&3
|
||||||
|
|
||||||
|
RESPONSE=$(read-response "$IN_PIPE")
|
||||||
|
|
||||||
|
RESPONSE_REQUEST_ID=$(response-request-id "$RESPONSE")
|
||||||
|
if [ "$RESPONSE_REQUEST_ID" -eq -1 ] || [ "$RESPONSE_REQUEST_ID" -eq 4294967295 ]; then
|
||||||
|
log-warning "RCON connection failed: Wrong RCON password" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run-command () {
|
||||||
|
COMMAND="$1"
|
||||||
|
|
||||||
|
# encode 2 "$COMMAND" 13 >> "$OUT_PIPE"
|
||||||
|
encode 2 "$COMMAND" 13 >&3
|
||||||
|
|
||||||
|
RESPONSE=$(read-response "$IN_PIPE")
|
||||||
|
response-payload "$RESPONSE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open a TCP socket
|
||||||
|
# Source: https://www.xmodulo.com/tcp-udp-socket-bash-shell.html
|
||||||
|
exec 3<>/dev/tcp/"$HOST"/"$PORT"
|
||||||
|
|
||||||
|
login "$PASSWORD" || return 1
|
||||||
|
run-command "$COMMAND"
|
||||||
|
|
||||||
|
# Close the socket
|
||||||
|
exec 3<&-
|
||||||
|
exec 3>&-
|
||||||
|
}
|
||||||
|
|
||||||
if [[ $COMPRESSION_FILE_EXTENSION == "." ]]; then
|
if [[ $COMPRESSION_FILE_EXTENSION == "." ]]; then
|
||||||
COMPRESSION_FILE_EXTENSION=""
|
COMPRESSION_FILE_EXTENSION=""
|
||||||
fi
|
fi
|
||||||
|
@ -78,7 +188,7 @@ fi
|
||||||
# Check for missing encouraged arguments
|
# Check for missing encouraged arguments
|
||||||
if ! $SUPPRESS_WARNINGS; then
|
if ! $SUPPRESS_WARNINGS; then
|
||||||
if [[ $SCREEN_NAME == "" ]]; then
|
if [[ $SCREEN_NAME == "" ]]; then
|
||||||
log-warning "Minecraft screen name not specified (use -s)"
|
log-warning "Minecraft screen/tmux/rcon location not specified (use -s)"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# Check for required arguments
|
# Check for required arguments
|
||||||
|
@ -109,9 +219,11 @@ execute-command () {
|
||||||
local COMMAND=$1
|
local COMMAND=$1
|
||||||
if [[ $SCREEN_NAME != "" ]]; then
|
if [[ $SCREEN_NAME != "" ]]; then
|
||||||
case $WINDOW_MANAGER in
|
case $WINDOW_MANAGER in
|
||||||
"screen") screen -S $SCREEN_NAME -p 0 -X stuff "$COMMAND$(printf \\r)"
|
"screen") screen -S "$SCREEN_NAME" -p 0 -X stuff "$COMMAND$(printf \\r)"
|
||||||
;;
|
;;
|
||||||
"tmux") tmux send-keys -t $SCREEN_NAME "$COMMAND" ENTER
|
"tmux") tmux send-keys -t "$SCREEN_NAME" "$COMMAND" ENTER
|
||||||
|
;;
|
||||||
|
"rcon") rcon-command "$SCREEN_NAME" "$COMMAND"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
@ -238,6 +350,9 @@ delete-thinning () {
|
||||||
delete-sequentially
|
delete-sequentially
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Ensure directory exists
|
||||||
|
mkdir -p "$(dirname $ARCHIVE_PATH)"
|
||||||
|
|
||||||
# Disable world autosaving
|
# Disable world autosaving
|
||||||
execute-command "save-off"
|
execute-command "save-off"
|
||||||
|
|
||||||
|
|
188
rcon.sh
188
rcon.sh
|
@ -1,96 +1,102 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
reverse-hex-endian () {
|
|
||||||
# Given a 4-byte hex integer, reverse endianness
|
|
||||||
while read -r -d '' -N 8 INTEGER; do
|
|
||||||
echo "$INTEGER" | sed -E 's/(..)(..)(..)(..)/\4\3\2\1/'
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
decode-hex-int () {
|
|
||||||
# decode little-endian hex integer
|
|
||||||
while read -r -d '' -N 8 INTEGER; do
|
|
||||||
BIG_ENDIAN_HEX=$(echo "$INTEGER" | reverse-hex-endian)
|
|
||||||
echo "$((16#$BIG_ENDIAN_HEX))"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
encode-int () {
|
|
||||||
# Encode an integer as 4 bytes in little endian and return as hex
|
|
||||||
INT="$1"
|
|
||||||
# Source: https://stackoverflow.com/a/9955198
|
|
||||||
printf "%08x" "$INT" | sed -E 's/(..)(..)(..)(..)/\4\3\2\1/'
|
|
||||||
}
|
|
||||||
|
|
||||||
encode () {
|
|
||||||
# Encode a packet type and payload for the rcon protocol
|
|
||||||
TYPE="$1"
|
|
||||||
PAYLOAD="$2"
|
|
||||||
REQUEST_ID="$3"
|
|
||||||
PAYLOAD_LENGTH="${#PAYLOAD}"
|
|
||||||
TOTAL_LENGTH="$((4 + 4 + PAYLOAD_LENGTH + 1 + 1))"
|
|
||||||
|
|
||||||
OUTPUT=""
|
|
||||||
OUTPUT+=$(encode-int "$TOTAL_LENGTH")
|
|
||||||
OUTPUT+=$(encode-int "$REQUEST_ID")
|
|
||||||
OUTPUT+=$(encode-int "$TYPE")
|
|
||||||
OUTPUT+=$(echo -n "$PAYLOAD" | xxd -ps)
|
|
||||||
OUTPUT+="0000"
|
|
||||||
|
|
||||||
echo -n "$OUTPUT" | xxd -ps -r
|
|
||||||
}
|
|
||||||
|
|
||||||
read-response () {
|
|
||||||
# read next response packet and return the payload text
|
|
||||||
IN_PIPE="$1"
|
|
||||||
# HEX_LENGTH=$(head -c4 "$IN_PIPE" | xxd -ps | reverse-hex-endian)
|
|
||||||
HEX_LENGTH=$(head -c4 <&3 | xxd -ps | reverse-hex-endian)
|
|
||||||
LENGTH=$((16#$HEX_LENGTH))
|
|
||||||
|
|
||||||
RESPONSE_PAYLOAD=$(head -c $LENGTH <&3 | xxd -ps)
|
|
||||||
echo -n "$RESPONSE_PAYLOAD"
|
|
||||||
}
|
|
||||||
|
|
||||||
response-request-id () {
|
|
||||||
echo -n "${1:0:8}" | decode-hex-int
|
|
||||||
}
|
|
||||||
|
|
||||||
response-type () {
|
|
||||||
echo -n "${1:8:8}" | decode-hex-int
|
|
||||||
}
|
|
||||||
|
|
||||||
response-payload () {
|
|
||||||
echo -n "${1:16:-4}" | xxd -r -ps
|
|
||||||
}
|
|
||||||
|
|
||||||
login () {
|
|
||||||
PASSWORD="$1"
|
|
||||||
encode 3 "$PASSWORD" 12 >&3
|
|
||||||
|
|
||||||
RESPONSE=$(read-response "$IN_PIPE")
|
|
||||||
|
|
||||||
RESPONSE_REQUEST_ID=$(response-request-id "$RESPONSE")
|
|
||||||
if [ "$RESPONSE_REQUEST_ID" -eq -1 ] || [ "$RESPONSE_REQUEST_ID" -eq 4294967295 ]; then
|
|
||||||
echo "Authentication failed: Wrong RCON password" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
run-command () {
|
|
||||||
COMMAND="$1"
|
|
||||||
|
|
||||||
# encode 2 "$COMMAND" 13 >> "$OUT_PIPE"
|
|
||||||
encode 2 "$COMMAND" 13 >&3
|
|
||||||
|
|
||||||
RESPONSE=$(read-response "$IN_PIPE")
|
|
||||||
response-payload "$RESPONSE"
|
|
||||||
}
|
|
||||||
|
|
||||||
rcon-command () {
|
rcon-command () {
|
||||||
HOST="$1"
|
HOST="$(echo $1 | cut -d: -f1)"
|
||||||
PORT="$2"
|
PORT="$(echo $1 | cut -d: -f2)"
|
||||||
PASSWORD="$3"
|
PASSWORD="$(echo $1 | cut -d: -f3-)"
|
||||||
COMMAND="$4"
|
COMMAND="$2"
|
||||||
|
|
||||||
|
reverse-hex-endian () {
|
||||||
|
# Given a 4-byte hex integer, reverse endianness
|
||||||
|
while read -r -d '' -N 8 INTEGER; do
|
||||||
|
echo "$INTEGER" | sed -E 's/(..)(..)(..)(..)/\4\3\2\1/'
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
decode-hex-int () {
|
||||||
|
# decode little-endian hex integer
|
||||||
|
while read -r -d '' -N 8 INTEGER; do
|
||||||
|
BIG_ENDIAN_HEX=$(echo "$INTEGER" | reverse-hex-endian)
|
||||||
|
echo "$((16#$BIG_ENDIAN_HEX))"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
stream-to-hex () {
|
||||||
|
xxd -ps
|
||||||
|
}
|
||||||
|
|
||||||
|
hex-to-stream () {
|
||||||
|
xxd -ps -r
|
||||||
|
}
|
||||||
|
|
||||||
|
encode-int () {
|
||||||
|
# Encode an integer as 4 bytes in little endian and return as hex
|
||||||
|
INT="$1"
|
||||||
|
# Source: https://stackoverflow.com/a/9955198
|
||||||
|
printf "%08x" "$INT" | sed -E 's/(..)(..)(..)(..)/\4\3\2\1/'
|
||||||
|
}
|
||||||
|
|
||||||
|
encode () {
|
||||||
|
# Encode a packet type and payload for the rcon protocol
|
||||||
|
TYPE="$1"
|
||||||
|
PAYLOAD="$2"
|
||||||
|
REQUEST_ID="$3"
|
||||||
|
PAYLOAD_LENGTH="${#PAYLOAD}"
|
||||||
|
TOTAL_LENGTH="$((4 + 4 + PAYLOAD_LENGTH + 1 + 1))"
|
||||||
|
|
||||||
|
OUTPUT=""
|
||||||
|
OUTPUT+=$(encode-int "$TOTAL_LENGTH")
|
||||||
|
OUTPUT+=$(encode-int "$REQUEST_ID")
|
||||||
|
OUTPUT+=$(encode-int "$TYPE")
|
||||||
|
OUTPUT+=$(echo -n "$PAYLOAD" | stream-to-hex)
|
||||||
|
OUTPUT+="0000"
|
||||||
|
|
||||||
|
echo -n "$OUTPUT" | hex-to-stream
|
||||||
|
}
|
||||||
|
|
||||||
|
read-response () {
|
||||||
|
# read next response packet and return the payload text
|
||||||
|
HEX_LENGTH=$(head -c4 <&3 | stream-to-hex | reverse-hex-endian)
|
||||||
|
LENGTH=$((16#$HEX_LENGTH))
|
||||||
|
|
||||||
|
RESPONSE_PAYLOAD=$(head -c $LENGTH <&3 | stream-to-hex)
|
||||||
|
echo -n "$RESPONSE_PAYLOAD"
|
||||||
|
}
|
||||||
|
|
||||||
|
response-request-id () {
|
||||||
|
echo -n "${1:0:8}" | decode-hex-int
|
||||||
|
}
|
||||||
|
|
||||||
|
response-type () {
|
||||||
|
echo -n "${1:8:8}" | decode-hex-int
|
||||||
|
}
|
||||||
|
|
||||||
|
response-payload () {
|
||||||
|
echo -n "${1:16:-4}" | hex-to-stream
|
||||||
|
}
|
||||||
|
|
||||||
|
login () {
|
||||||
|
PASSWORD="$1"
|
||||||
|
encode 3 "$PASSWORD" 12 >&3
|
||||||
|
|
||||||
|
RESPONSE=$(read-response "$IN_PIPE")
|
||||||
|
|
||||||
|
RESPONSE_REQUEST_ID=$(response-request-id "$RESPONSE")
|
||||||
|
if [ "$RESPONSE_REQUEST_ID" -eq -1 ] || [ "$RESPONSE_REQUEST_ID" -eq 4294967295 ]; then
|
||||||
|
echo "Authentication failed: Wrong RCON password" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run-command () {
|
||||||
|
COMMAND="$1"
|
||||||
|
|
||||||
|
# encode 2 "$COMMAND" 13 >> "$OUT_PIPE"
|
||||||
|
encode 2 "$COMMAND" 13 >&3
|
||||||
|
|
||||||
|
RESPONSE=$(read-response "$IN_PIPE")
|
||||||
|
response-payload "$RESPONSE"
|
||||||
|
}
|
||||||
|
|
||||||
# Open a TCP socket
|
# Open a TCP socket
|
||||||
# Source: https://www.xmodulo.com/tcp-udp-socket-bash-shell.html
|
# Source: https://www.xmodulo.com/tcp-udp-socket-bash-shell.html
|
||||||
|
@ -110,4 +116,4 @@ PORT="$2"
|
||||||
PASSWORD="$3"
|
PASSWORD="$3"
|
||||||
COMMAND="$4"
|
COMMAND="$4"
|
||||||
|
|
||||||
rcon-command "$HOST" "$PORT" "$PASSWORD" "$COMMAND"
|
rcon-command "$HOST:$PORT:$PASSWORD" "$COMMAND"
|
||||||
|
|
31
test/mock_rcon.py
Normal file
31
test/mock_rcon.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Reference: https://docs.python.org/3/library/socketserver.html
|
||||||
|
import codecs
|
||||||
|
import socketserver
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class Handler(socketserver.BaseRequestHandler):
|
||||||
|
def handle(self):
|
||||||
|
while True:
|
||||||
|
length = int.from_bytes(self.request.recv(4), 'little')
|
||||||
|
if not length:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.request.recv(4)
|
||||||
|
type_ = int.from_bytes(self.request.recv(4), 'little')
|
||||||
|
self.data = self.request.recv(length - 8)[:-2].decode('utf-8')
|
||||||
|
if self.data:
|
||||||
|
if type_ == 2:
|
||||||
|
print(self.data)
|
||||||
|
sys.stdout.flush()
|
||||||
|
try:
|
||||||
|
if type_ == 3 and self.data != sys.argv[2]:
|
||||||
|
self.request.sendall(codecs.decode('0a000000ffffffff020000000000', 'hex'))
|
||||||
|
else:
|
||||||
|
self.request.sendall(codecs.decode('0a00000010000000020000000000', 'hex'))
|
||||||
|
except:
|
||||||
|
break
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
HOST, PORT = "localhost", int(sys.argv[1])
|
||||||
|
with socketserver.ThreadingTCPServer((HOST, PORT), Handler) as server:
|
||||||
|
server.serve_forever()
|
24
test/test.sh
24
test/test.sh
|
@ -5,6 +5,8 @@
|
||||||
TEST_DIR="test"
|
TEST_DIR="test"
|
||||||
TEST_TMP="$TEST_DIR/tmp"
|
TEST_TMP="$TEST_DIR/tmp"
|
||||||
SCREEN_TMP="tmp-screen"
|
SCREEN_TMP="tmp-screen"
|
||||||
|
RCON_PORT="8088"
|
||||||
|
RCON_PASSWORD="supersecret"
|
||||||
setUp () {
|
setUp () {
|
||||||
rm -rf "$TEST_TMP"
|
rm -rf "$TEST_TMP"
|
||||||
mkdir -p "$TEST_TMP/server/world"
|
mkdir -p "$TEST_TMP/server/world"
|
||||||
|
@ -13,6 +15,8 @@ setUp () {
|
||||||
echo "file2" > "$TEST_TMP/server/world/file2.txt"
|
echo "file2" > "$TEST_TMP/server/world/file2.txt"
|
||||||
echo "file3" > "$TEST_TMP/server/world/file3.txt"
|
echo "file3" > "$TEST_TMP/server/world/file3.txt"
|
||||||
|
|
||||||
|
python test/mock_rcon.py "$RCON_PORT" "$RCON_PASSWORD" > "$TEST_TMP/rcon-output" &
|
||||||
|
echo "$!" > "$TEST_TMP/rcon-pid"
|
||||||
screen -dmS "$SCREEN_TMP" bash
|
screen -dmS "$SCREEN_TMP" bash
|
||||||
screen -S "$SCREEN_TMP" -X stuff "cat > $TEST_TMP/screen-output\n"
|
screen -S "$SCREEN_TMP" -X stuff "cat > $TEST_TMP/screen-output\n"
|
||||||
tmux new-session -d -s "$SCREEN_TMP"
|
tmux new-session -d -s "$SCREEN_TMP"
|
||||||
|
@ -21,6 +25,8 @@ setUp () {
|
||||||
}
|
}
|
||||||
|
|
||||||
tearDown () {
|
tearDown () {
|
||||||
|
RCON_PID="$(cat "$TEST_TMP/rcon-pid")"
|
||||||
|
kill "$RCON_PID" >/dev/null 2>&1 || true
|
||||||
screen -S "$SCREEN_TMP" -X quit >/dev/null 2>&1 || true
|
screen -S "$SCREEN_TMP" -X quit >/dev/null 2>&1 || true
|
||||||
tmux kill-session -t "$SCREEN_TMP" >/dev/null 2>&1 || true
|
tmux kill-session -t "$SCREEN_TMP" >/dev/null 2>&1 || true
|
||||||
}
|
}
|
||||||
|
@ -88,7 +94,7 @@ test-missing-options () {
|
||||||
OUTPUT="$(./backup.sh 2>&1)"
|
OUTPUT="$(./backup.sh 2>&1)"
|
||||||
EXIT_CODE="$?"
|
EXIT_CODE="$?"
|
||||||
assertEquals 1 "$EXIT_CODE"
|
assertEquals 1 "$EXIT_CODE"
|
||||||
assertContains "$OUTPUT" "Minecraft screen name not specified"
|
assertContains "$OUTPUT" "Minecraft screen/tmux/rcon location not specified (use -s)"
|
||||||
assertContains "$OUTPUT" "Server world not specified"
|
assertContains "$OUTPUT" "Server world not specified"
|
||||||
assertContains "$OUTPUT" "Backup directory not specified"
|
assertContains "$OUTPUT" "Backup directory not specified"
|
||||||
}
|
}
|
||||||
|
@ -97,7 +103,7 @@ test-missing-options-suppress-warnings () {
|
||||||
OUTPUT="$(./backup.sh -q 2>&1)"
|
OUTPUT="$(./backup.sh -q 2>&1)"
|
||||||
EXIT_CODE="$?"
|
EXIT_CODE="$?"
|
||||||
assertEquals 1 "$EXIT_CODE"
|
assertEquals 1 "$EXIT_CODE"
|
||||||
assertNotContains "$OUTPUT" "Minecraft screen name not specified"
|
assertNotContains "$OUTPUT" "Minecraft screen/tmux/rcon location not specified (use -s)"
|
||||||
}
|
}
|
||||||
|
|
||||||
test-empty-world-warning () {
|
test-empty-world-warning () {
|
||||||
|
@ -129,6 +135,20 @@ test-tmux-interface () {
|
||||||
assertEquals "$SCREEN_CONTENTS" "$EXPECTED_CONTENTS"
|
assertEquals "$SCREEN_CONTENTS" "$EXPECTED_CONTENTS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test-rcon-interface () {
|
||||||
|
TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")"
|
||||||
|
./backup.sh -w rcon -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "localhost:$RCON_PORT:$RCON_PASSWORD" -f "$TIMESTAMP"
|
||||||
|
EXPECTED_CONTENTS=$(echo -e "save-off\nsave-on\nsave-all")
|
||||||
|
SCREEN_CONTENTS="$(head -n3 "$TEST_TMP/rcon-output")"
|
||||||
|
assertEquals "$SCREEN_CONTENTS" "$EXPECTED_CONTENTS"
|
||||||
|
}
|
||||||
|
|
||||||
|
test-rcon-interface-wrong-password () {
|
||||||
|
TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")"
|
||||||
|
OUTPUT="$(./backup.sh -w rcon -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "localhost:$RCON_PORT:wrong$RCON_PASSWORD" -f "$TIMESTAMP" 2>&1)"
|
||||||
|
assertContains "$OUTPUT" "Wrong RCON password"
|
||||||
|
}
|
||||||
|
|
||||||
test-sequential-delete () {
|
test-sequential-delete () {
|
||||||
for i in $(seq 0 99); do
|
for i in $(seq 0 99); do
|
||||||
TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01 +$i hour")"
|
TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01 +$i hour")"
|
||||||
|
|
Loading…
Add table
Reference in a new issue