mirror of
https://github.com/MinecraftServerControl/mscs.git
synced 2024-11-14 22:38:18 -07:00
3553 lines
126 KiB
Bash
3553 lines
126 KiB
Bash
#!/bin/sh
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Copyright (c) 2011-2021, Jason M. Wood <sandain@hotmail.com>
|
|
#
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
#
|
|
# 1. Redistributions of source code must retain the above copyright notice,
|
|
# this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Minecraft Server Control Script
|
|
#
|
|
# A powerful command-line control script for Linux-powered Minecraft servers.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Get executable name
|
|
# ---------------------------------------------------------------------------
|
|
PROG=$(basename $0)
|
|
|
|
# Required Software
|
|
# ---------------------------------------------------------------------------
|
|
# Detect its presence and location for later.
|
|
JAVA=$(which java)
|
|
PERL=$(which perl)
|
|
PYTHON=$(which python3)
|
|
WGET=$(which wget)
|
|
RDIFF_BACKUP=$(which rdiff-backup)
|
|
RSYNC=$(which rsync)
|
|
SOCAT=$(which socat)
|
|
FLOCK=$(which flock)
|
|
|
|
# Script Usage
|
|
# ---------------------------------------------------------------------------
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $PROG [<options>] <action>
|
|
|
|
Actions:
|
|
|
|
start <world1> <world2> <...>
|
|
Start the Minecraft world server(s). Start all world servers by default.
|
|
|
|
stop <world1> <world2> <...>
|
|
Stop the Minecraft world server(s). Stop all world servers by default.
|
|
|
|
force-stop <world1> <world2> <...>
|
|
Forcibly stop the Minecraft world server(s). Forcibly stop all world
|
|
servers by default.
|
|
|
|
restart <world1> <world2> <...>
|
|
Restart the Minecraft world server(s). Restart all world servers by default.
|
|
|
|
force-restart <world1> <world2> <...>
|
|
Forcibly restart the Minecraft world server(s). Forcibly restart all world
|
|
servers by default.
|
|
|
|
create <world> <port> [<ip>]
|
|
Create a Minecraft world server. The world name and port must be
|
|
provided, the IP address is usually blank. Without arguments, create a
|
|
a default world at the default port.
|
|
|
|
import <directory> <world> <port> [<ip>]
|
|
Import an existing world server. The world name and port must be
|
|
provided, the IP address is usually blank.
|
|
|
|
rename <original world> <new world>
|
|
Rename an existing world server.
|
|
|
|
delete <world>
|
|
Delete a Minecraft world server.
|
|
|
|
disable <world1> <world2> <...>
|
|
Temporarily disables world server(s). Disables all world servers by default.
|
|
|
|
enable <world1> <world2> <...>
|
|
Enable disabled world server(s). Enables all world servers by default.
|
|
|
|
ls <option>
|
|
Display a list of worlds.
|
|
Options:
|
|
enabled Display a list of enabled worlds, default.
|
|
disabled Display a list of disabled worlds.
|
|
running Display a list of running worlds.
|
|
stopped Display a list of stopped worlds.
|
|
If no option, all available worlds are listed.
|
|
|
|
list <option>
|
|
Same as 'ls' but more detailed.
|
|
|
|
status <world1> <world2> <...>
|
|
Display the status of Minecraft world server(s). Display the status of
|
|
all world servers by default.
|
|
|
|
sync <world1> <world2> <...>
|
|
Synchronize the data stored in the mirror images of the Minecraft world
|
|
server(s). Synchronizes all of the world servers by default. This option
|
|
is only available when the mirror image option is enabled.
|
|
|
|
broadcast <command>
|
|
Broadcast a command to all running Minecraft world servers.
|
|
|
|
send <world> <command>
|
|
Send a command to a Minecraft world server.
|
|
|
|
console <world>
|
|
Connect to the Minecraft world server's console. Hit <Ctrl-D> to detach.
|
|
|
|
watch <world>
|
|
Watch the log file for the Minecraft world server.
|
|
|
|
logrotate <world1> <world2> <...>
|
|
Rotate the log file for the Minecraft world(s). Rotate the log file for
|
|
all worlds by default.
|
|
|
|
backup <world1> <world2> <...>
|
|
Backup the Minecraft world(s). Backup all worlds by default.
|
|
|
|
clean-backups <world>
|
|
Remove expired backups for the world. Removes old backups based on
|
|
mscs-backup-duration.
|
|
|
|
list-backups <world>
|
|
List the datetime of the backups for the world.
|
|
|
|
restore-backup <world> <datetime>
|
|
Restore a backup for a world that was taken at the datetime.
|
|
|
|
map <world1> <world2> <...>
|
|
Run the Minecraft Overviewer mapping software on the Minecraft world(s).
|
|
Map all worlds by default.
|
|
|
|
update <world1> <world2> <...>
|
|
Update the server software for the Minecraft world server(s). Update
|
|
server software for all worlds by default.
|
|
|
|
force-update <world1> <world2> <...>
|
|
Refresh version information prior to running update for the world
|
|
server(s), regardless of how recently the version information was updated.
|
|
Refreshes version information and updates all world servers by default.
|
|
|
|
query <world1> <world2> <...>
|
|
Run a detailed Query on the Minecraft world server(s). Run a detailed
|
|
query on all world servers by default.
|
|
|
|
Options:
|
|
|
|
-c <config_file>
|
|
Read configuration from <config_files> instead of default locations.
|
|
|
|
-l <location>
|
|
Uses <location> as the base path for data. Overrides configuration file
|
|
options.
|
|
EOF
|
|
}
|
|
|
|
mscs_defaults() {
|
|
cat <<EOF
|
|
; MSCS defaults file for adjusting global server properties.
|
|
|
|
; Default values in the script can be overridden by adding certain properties
|
|
; to one of the mscs.defaults files. The mscs.defaults files can be found
|
|
; found in one of three places depending on how the script is being used. When
|
|
; using the mscs script, the mscs.defaults file can be found at
|
|
; /opt/mscs/mscs.defaults. When using the msctl script in multi-user mode,
|
|
; the mscs.defaults file can be found at either \$HOME/mscs.defaults or
|
|
; \$HOME/.config/mscs/mscs.defaults.
|
|
|
|
; Uncomment key=value pairs (remove the #) to customize the value for your
|
|
; configuration. The values shown are the default values used in the script.
|
|
|
|
; Location of the mscs files.
|
|
# mscs-location=/opt/mscs
|
|
|
|
; Location of world files.
|
|
# mscs-worlds-location=/opt/mscs/worlds
|
|
|
|
; URL to download the version_manifest.json file.
|
|
# mscs-versions-url=https://launchermeta.mojang.com/mc/game/version_manifest.json
|
|
|
|
; Location of the version_manifest.json file.
|
|
# mscs-versions-json=/opt/mscs/version_manifest.json
|
|
|
|
; Length in minutes to keep the version_manifest.json file before updating.
|
|
# mscs-versions-duration=30
|
|
|
|
; Length in minutes to keep lock files before removing.
|
|
# mscs-lockfile-duration=1440
|
|
|
|
; Properties to return for detailed listings.
|
|
# mscs-detailed-listing=motd server-ip server-port max-players level-type online-mode
|
|
|
|
; Default world name.
|
|
# mscs-default-world=world
|
|
|
|
; Default Port.
|
|
# mscs-default-port=25565
|
|
|
|
; Default IP address. Leave this blank unless you want to bind all world
|
|
; servers to a single network interface by default.
|
|
# mscs-default-ip=
|
|
|
|
; Default version type (release or snapshot).
|
|
# mscs-default-version-type=release
|
|
|
|
; Default version of the client software. This sets the \$CLIENT_VERSION
|
|
; variable. You can use the \$CURRENT_VERSION variable to access the latest
|
|
; version based on the version type selected.
|
|
# mscs-default-client-version=\$CURRENT_VERSION
|
|
|
|
; Default .jar file for the client software. The \$CLIENT_VERSION variable
|
|
; allows access to the client version selected.
|
|
# mscs-default-client-jar=\$CLIENT_VERSION.jar
|
|
|
|
; Default download URL for the client software. The \$CLIENT_VERSION variable
|
|
; allows access to the client version selected.
|
|
# mscs-default-client-url=
|
|
|
|
; Default location of the client .jar file. The \$CLIENT_VERSION variable
|
|
; allows access to the client version selected.
|
|
# mscs-default-client-location=/opt/mscs/.minecraft/versions/\$CLIENT_VERSION
|
|
|
|
; Default version of the server software. This sets the \$SERVER_VERSION
|
|
; variable. You can use the \$CURRENT_VERSION variable to access the latest
|
|
; version based on the version type selected.
|
|
# mscs-default-server-version=\$CURRENT_VERSION
|
|
|
|
; Default arguments for the JVM. This sets the \$JVM_ARGS variable.
|
|
# mscs-default-jvm-args=
|
|
|
|
; Default .jar file for the server software. This sets the \$SERVER_JAR
|
|
; variable. The \$SERVER_VERSION variable allows access to the server version
|
|
; selected.
|
|
# mscs-default-server-jar=minecraft_server.\$SERVER_VERSION.jar
|
|
|
|
; Default download URL for the server software. The \$SERVER_VERSION variable
|
|
; allows access to the server version selected.
|
|
# mscs-default-server-url=
|
|
|
|
; Default arguments for a world server. This sets the \$SERVER_ARGS variable.
|
|
# mscs-default-server-args=nogui
|
|
|
|
; Default initial amount of memory for a world server. This sets the
|
|
; \$INITIAL_MEMORY variable.
|
|
# mscs-default-initial-memory=128M
|
|
|
|
; Default maximum amount of memory for a world server. This sets the
|
|
; \$MAXIMUM_MEMORY variable.
|
|
# mscs-default-maximum-memory=2048M
|
|
|
|
; Default location of the server .jar file. This sets the \$SERVER_LOCATION
|
|
; variable.
|
|
# mscs-default-server-location=/opt/mscs/server
|
|
|
|
; Default command to run for a world server. You can use the \$JAVA variable to
|
|
; access the results of \$(which java). The \$INITIAL_MEMORY and \$MAXIMUM_MEMORY
|
|
; variables provide access to the amounts of memory selected. The
|
|
; \$SERVER_LOCATION and \$SERVER_JAR variables provide access to the location
|
|
; and file name of the server software selected. The \$JVM_ARGS variable
|
|
; provides access to the Java virtual machine arguments for the world server
|
|
; selected. The \$SERVER_ARGS variable provides access to the server arguments
|
|
; for the world server selected.
|
|
# mscs-default-server-command=\$JAVA -Xms\$INITIAL_MEMORY -Xmx\$MAXIMUM_MEMORY \$JVM_ARGS -jar \$SERVER_LOCATION/\$SERVER_JAR \$SERVER_ARGS
|
|
|
|
; Default behavior if to restart the server after crash is detected (default disabled).
|
|
# mscs-default-restart-after-crash=false
|
|
|
|
; Location to store backup files.
|
|
# mscs-backup-location=/opt/mscs/backups
|
|
|
|
; Location of the backup log file.
|
|
# mscs-backup-log=/opt/mscs/backups/backup.log
|
|
|
|
; Files and directories excluded from backups. Each path is relative to the
|
|
; world/<world> directory. Separate each entry with commas.
|
|
# mscs-backup-excluded-files=
|
|
|
|
; Length in days that backups survive. A value less than 1 disables backup deletion.
|
|
# mscs-backup-duration=15
|
|
|
|
; Length in days that logs survive. A value less than 1 disables log deletion.
|
|
# mscs-log-duration=15
|
|
|
|
; Enable the mirror option by default for worlds (default disabled). Change
|
|
; to a 1 to enable.
|
|
# mscs-enable-mirror=0
|
|
|
|
; Default path for the mirror files.
|
|
# mscs-mirror-path=/dev/shm/mscs
|
|
|
|
; Location of Overviewer.
|
|
# mscs-overviewer-bin=/usr/bin/overviewer.py
|
|
|
|
; URL for Overviewer.
|
|
# mscs-overviewer-url=http://overviewer.org
|
|
|
|
; Location of Overviewer generated map files.
|
|
# mscs-maps-location=/opt/mscs/maps
|
|
|
|
; URL for accessing Overviewer generated maps.
|
|
# mscs-maps-url=http://minecraft.server.com/maps
|
|
EOF
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Internal Methods
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# NOTE: Nothing below this point should need to be edited directly.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the PID of the Java process for the world server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return The Java PID.
|
|
# ---------------------------------------------------------------------------
|
|
getJavaPID() {
|
|
local PID MCUSER
|
|
MCUSER=$(whoami)
|
|
PID=$(
|
|
ps -a -U $MCUSER -o pid,comm,args -ww | $PERL -sne '
|
|
if ($_ =~ /^\s*(\d+)\s+java.+mscs-world=\Q$world\E$/) { print $1; }
|
|
' -- -world="$1"
|
|
)
|
|
printf "%d\n" $PID
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the amount of memory used by the Java process for the world server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return The amount of memory used.
|
|
# ---------------------------------------------------------------------------
|
|
getJavaMemory() {
|
|
local PID
|
|
PID=$(getJavaPID "$1")
|
|
ps --no-headers -p $PID -o rss
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Evaluate true/false values.
|
|
#
|
|
# @param 1 The true or false value to evaluate.
|
|
# @return 0 (success) for true values, 1 (failure) for false values.
|
|
# ---------------------------------------------------------------------------
|
|
true_value() {
|
|
local VALUE
|
|
VALUE=$(echo "$1" | tr '[:upper:]' '[:lower:]')
|
|
# Look for TRUE values.
|
|
if [ "$VALUE" = "1" ] || [ "$VALUE" = "true" ] || [ "$VALUE" = "yes" ] || [ "$VALUE" = "on" ]; then
|
|
# Return a true value (success).
|
|
return 0
|
|
fi
|
|
# Look for FALSE values.
|
|
if [ "$VALUE" = "0" ] || [ "$VALUE" = "false" ] || [ "$VALUE" = "no" ] || [ "$VALUE" = "off" ]; then
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
# Assume FALSE if value is not defined.
|
|
if [ "$VALUE" = "" ]; then
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
printf "Invalid true/false value: \"$1\"\n" >&2
|
|
# Return a false value (failure) in case of invalid true/false input.
|
|
return 1
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Check to see if the world server is running.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return A 0 if the server is thought to be running, a 1 otherwise.
|
|
# ---------------------------------------------------------------------------
|
|
serverRunning() {
|
|
# Try to determine if the world is running.
|
|
if [ $(getJavaPID "$1") -gt 0 ]; then
|
|
# Return a true value (success).
|
|
return 0
|
|
else
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Send a command to the world server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @param 2 The command to send.
|
|
# ---------------------------------------------------------------------------
|
|
sendCommand() {
|
|
echo "$2" | $PERL -e '
|
|
while (<>) { $_ =~ s/[\r\n]+//g; $cmd .= $_; } print "$cmd\r";
|
|
' >>$WORLDS_LOCATION/$1/console.in
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Check whether the item is in the list.
|
|
#
|
|
# @param 1 The item being searched for.
|
|
# @param 2 The list being searched.
|
|
# @return A 0 if the list contains the item, a 1 otherwise.
|
|
# ---------------------------------------------------------------------------
|
|
listContains() {
|
|
local MATCH ITEM
|
|
MATCH=1
|
|
for ITEM in $2; do
|
|
if [ "$ITEM" = "$1" ]; then
|
|
MATCH=0
|
|
fi
|
|
done
|
|
return $MATCH
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Compare two datetime strings.
|
|
#
|
|
# @param 1 The first datetime string.
|
|
# @param 2 The second datetime string.
|
|
# @return The result of the comparison: -1, 0, 1.
|
|
# ---------------------------------------------------------------------------
|
|
compareTime() {
|
|
local T1 T2
|
|
T1=$(date --date="$1" +%s)
|
|
T2=$(date --date="$2" +%s)
|
|
printf $(($T1 < $T2 ? -1 : $T1 > $T2 ? 1 : 0))
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Compare two Minecraft version numbers.
|
|
#
|
|
# @param 1 The first Minecraft version number.
|
|
# @param 2 The second Minecraft version number.
|
|
# @return The result of the comparison: -1, 0, 1.
|
|
# ---------------------------------------------------------------------------
|
|
compareMinecraftVersions() {
|
|
local T1 T2
|
|
T1=$(getMinecraftVersionReleaseTime "$1")
|
|
T2=$(getMinecraftVersionReleaseTime "$2")
|
|
compareTime "$T1" "$T2"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Create a world.
|
|
#
|
|
# @param 1 The world server to create.
|
|
# @param 2 The port of the world server.
|
|
# @param 3 The IP address of the world server.
|
|
# ---------------------------------------------------------------------------
|
|
createWorld() {
|
|
# Create a basic server.properties file. Values not supplied here
|
|
# will use default values when the world server is first started.
|
|
mkdir -p "$WORLDS_LOCATION/$1"
|
|
setServerPropertiesValue "$1" "level-name" "$1"
|
|
setServerPropertiesValue "$1" "server-port" "$2"
|
|
setServerPropertiesValue "$1" "server-ip" "$3"
|
|
setServerPropertiesValue "$1" "enable-query" "true"
|
|
setServerPropertiesValue "$1" "query.port" "$2"
|
|
setMSCSValue "$1" "mscs-enabled" "true"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Delete a world.
|
|
#
|
|
# @param 1 The world server to delete.
|
|
# ---------------------------------------------------------------------------
|
|
deleteWorld() {
|
|
# Delete the world directory.
|
|
rm -Rf "$WORLDS_LOCATION/$1"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Disable a world.
|
|
#
|
|
# @param 1 The world server to disable.
|
|
# ---------------------------------------------------------------------------
|
|
disableWorld() {
|
|
# Disable the world.
|
|
setMSCSValue "$1" "mscs-enabled" "false"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Enable a world.
|
|
#
|
|
# @param 1 The world server to enable.
|
|
# ---------------------------------------------------------------------------
|
|
enableWorld() {
|
|
# Enable the world.
|
|
setMSCSValue "$1" "mscs-enabled" "true"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Import a world.
|
|
#
|
|
# @param 1 The location of the world to import.
|
|
# @param 2 The name of the new imported world.
|
|
# @param 3 The port of the new imported world.
|
|
# @param 4 The IP address of the new imported world.
|
|
# ---------------------------------------------------------------------------
|
|
importWorld() {
|
|
local NAME
|
|
mkdir -p "$WORLDS_LOCATION/$2"
|
|
cp -R $1/* $WORLDS_LOCATION/$2
|
|
chown $(id -un):$(id -gn) -R $WORLDS_LOCATION/$2
|
|
NAME=$(getServerPropertiesValue $2 'level-name')
|
|
if [ -d $WORLDS_LOCATION/$2/$NAME ]; then
|
|
mv $WORLDS_LOCATION/$2/$NAME $WORLDS_LOCATION/$2/$2
|
|
fi
|
|
createWorld "$2" "$3" "$4"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Grab the list of enabled worlds.
|
|
#
|
|
# @return The list of enabled worlds.
|
|
# ---------------------------------------------------------------------------
|
|
getEnabledWorlds() {
|
|
local WORLD WORLDS
|
|
mkdir -p "$WORLDS_LOCATION"
|
|
WORLDS=""
|
|
for WORLD in $(ls $WORLDS_LOCATION); do
|
|
if [ -d $WORLDS_LOCATION/$WORLD ]; then
|
|
if true_value "$(getMSCSValue $WORLD 'mscs-enabled' 'true')"; then
|
|
WORLDS="$WORLDS $WORLD"
|
|
fi
|
|
fi
|
|
done
|
|
printf "$WORLDS"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Grab the list of disabled worlds.
|
|
#
|
|
# @return The list of disabled worlds.
|
|
# ---------------------------------------------------------------------------
|
|
getDisabledWorlds() {
|
|
local WORLD WORLDS
|
|
WORLDS=""
|
|
for WORLD in $(ls $WORLDS_LOCATION); do
|
|
if [ -d $WORLDS_LOCATION/$WORLD ]; then
|
|
if ! true_value "$(getMSCSValue $WORLD 'mscs-enabled' 'true')"; then
|
|
WORLDS="$WORLDS $WORLD"
|
|
fi
|
|
fi
|
|
done
|
|
printf "$WORLDS"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Grab the list of all available worlds.
|
|
#
|
|
# @return The list of all available worlds.
|
|
# ---------------------------------------------------------------------------
|
|
getAvailableWorlds() {
|
|
printf "$(getEnabledWorlds) $(getDisabledWorlds)"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Check to see if the world is enabled.
|
|
#
|
|
# @param 1 The world of interest.
|
|
# @return A 0 if the world is enabled, a 1 otherwise.
|
|
# ---------------------------------------------------------------------------
|
|
isWorldEnabled() {
|
|
local WORLDS
|
|
WORLDS=$(getEnabledWorlds)
|
|
if [ -n "$1" ] && listContains "$1" "$WORLDS"; then
|
|
# Return a true value (success).
|
|
return 0
|
|
else
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Check to see if the world is disabled.
|
|
#
|
|
# @param 1 The world of interest.
|
|
# @return A 0 if the world is disabled, a 1 otherwise.
|
|
# ---------------------------------------------------------------------------
|
|
isWorldDisabled() {
|
|
local WORLDS
|
|
WORLDS=$(getDisabledWorlds)
|
|
if [ -n "$1" ] && listContains "$1" "$WORLDS"; then
|
|
# Return a true value (success).
|
|
return 0
|
|
else
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Check to see if the world is available (exists).
|
|
#
|
|
# @param 1 The world of interest.
|
|
# @return A 0 if the world is available, a 1 otherwise.
|
|
# ---------------------------------------------------------------------------
|
|
isWorldAvailable() {
|
|
local WORLDS
|
|
WORLDS=$(getAvailableWorlds)
|
|
if [ -n "$1" ] && listContains "$1" "$WORLDS"; then
|
|
# Return a true value (success).
|
|
return 0
|
|
else
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the value of a key from the provided file.
|
|
#
|
|
# @param 1 The file containing the key/value combo.
|
|
# @param 2 The key to get.
|
|
# @param 3 The default value.
|
|
# ---------------------------------------------------------------------------
|
|
getValue() {
|
|
local KEY VALUE
|
|
# Make sure the file exists.
|
|
if [ -e "$1" ]; then
|
|
# Find the key/value combo.
|
|
KEY=$(cat $1 | $PERL -sne '
|
|
# Remove single and double quotes plus CR and LF.
|
|
$_ =~ s/[\x22\x27\r\n]//g;
|
|
# Remove comments that begin with # or ;.
|
|
$_ =~ s/^\s*[\x23\x3B].*//;
|
|
# Extract the key.
|
|
if ($_ =~ /^\s*(\Q$key\E)\s*=\s*.*$/i) { print lc $1; }
|
|
' -- -key="$2")
|
|
VALUE=$(cat $1 | $PERL -sne '
|
|
# Remove single and double quotes plus CR and LF.
|
|
$_ =~ s/[\x22\x27\r\n]//g;
|
|
# Remove comments that begin with # or ;.
|
|
$_ =~ s/^\s*[\x23\x3B].*//;
|
|
# Extract the value.
|
|
if ($_ =~ /^\s*\Q$key\E\s*=\s*(.*)$/i) { print $1; }
|
|
' -- -key="$2")
|
|
fi
|
|
# Return the value if found, the default value if not.
|
|
if [ -n "$KEY" ] && [ -n "$VALUE" ]; then
|
|
# $VALUE may contains flag-like strings not intended for printf
|
|
printf -- "$VALUE"
|
|
else
|
|
printf -- "$3"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Modify the value of a key/value combo in the provided file.
|
|
#
|
|
# @param 1 The file containing the key/value combo.
|
|
# @param 2 The key to modify.
|
|
# @param 3 The value to assign to the key.
|
|
# ---------------------------------------------------------------------------
|
|
setValue() {
|
|
local KEY_VALUE
|
|
# Make sure that the file exists.
|
|
touch "$1"
|
|
# Replace the key/value combo if it already exists, otherwise just
|
|
# append it to the end of the file.
|
|
KEY_VALUE=$(cat $1 | $PERL -sne '
|
|
$_ =~ s/[\r]//;
|
|
if ($_ =~ /^(\Q$key\E=.*)$/) { print "$1"; }
|
|
' -- -key="$2")
|
|
if [ -n "$KEY_VALUE" ]; then
|
|
mv $1 $1.bak
|
|
cat $1.bak | $PERL -sne '
|
|
$_ =~ s/[\r]//;
|
|
if ($_ =~ /^\Q$key\E=.*$/) { print "$key=$value\n"; } else { print; }
|
|
' -- -key="$2" -value="$3" > $1
|
|
rm $1.bak
|
|
else
|
|
printf "$2=$3\n" >>"$1"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the value of a key in the mscs.defaults file.
|
|
#
|
|
# @param 1 The key to get.
|
|
# @param 2 The default value.
|
|
# ---------------------------------------------------------------------------
|
|
getDefaultsValue() {
|
|
getValue "$MSCS_DEFAULTS" "$1" "$2"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the value of the EULA boolean.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# ---------------------------------------------------------------------------
|
|
getEULAValue() {
|
|
local EULA_FILE
|
|
EULA_FILE=$WORLDS_LOCATION/$1/eula.txt
|
|
getValue "$EULA_FILE" "eula" "false" | $PERL -ne 'print lc'
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the value of a key in a mscs.properties file.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @param 2 The key to get.
|
|
# @param 3 The default value.
|
|
# ---------------------------------------------------------------------------
|
|
getMSCSValue() {
|
|
local PROPERTY_FILE
|
|
PROPERTY_FILE=$WORLDS_LOCATION/$1/mscs.properties
|
|
getValue "$PROPERTY_FILE" "$2" "$3"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Modify the value of a key/value combo in a mscs.properties file.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @param 2 The key to modify.
|
|
# @param 3 The value to assign to the key.
|
|
# ---------------------------------------------------------------------------
|
|
setMSCSValue() {
|
|
local PROPERTY_FILE
|
|
PROPERTY_FILE=$WORLDS_LOCATION/$1/mscs.properties
|
|
setValue "$PROPERTY_FILE" "$2" "$3"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the value of a key in a server.properties file.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @param 2 The key to get.
|
|
# @param 3 The default value.
|
|
# ---------------------------------------------------------------------------
|
|
getServerPropertiesValue() {
|
|
local PROPERTY_FILE
|
|
PROPERTY_FILE="$WORLDS_LOCATION/$1/server.properties"
|
|
getValue "$PROPERTY_FILE" "$2" "$3"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Modify the value of a key/value combo in a server.properties file.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @param 2 The key to modify.
|
|
# @param 3 The value to assign to the key.
|
|
# ---------------------------------------------------------------------------
|
|
setServerPropertiesValue() {
|
|
local PROPERTY_FILE
|
|
PROPERTY_FILE=$WORLDS_LOCATION/$1/server.properties
|
|
setValue "$PROPERTY_FILE" "$2" "$3"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Update the version_manifest.json file.
|
|
# ---------------------------------------------------------------------------
|
|
updateVersionsJSON() {
|
|
if [ -s $VERSIONS_JSON ]; then
|
|
# Make a backup copy of the version_manifest.json file.
|
|
cp -p "$VERSIONS_JSON" "$VERSIONS_JSON.bak"
|
|
# Delete the version_manifest.json file if it is old.
|
|
find "$VERSIONS_JSON" -mmin +"$VERSIONS_DURATION" -delete >/dev/null 2>&1
|
|
if [ -s $VERSIONS_JSON ]; then
|
|
printf "The cached copy of the version manifest is up to date.\n"
|
|
printf "Use the force-update option to ensure a new copy is downloaded.\n"
|
|
else
|
|
printf "The version manifest cache was out of date, it has been removed.\n"
|
|
fi
|
|
fi
|
|
# Download the version_manifest.json file if it does not exist.
|
|
if [ ! -s $VERSIONS_JSON ]; then
|
|
printf "Downloading current Minecraft version manifest.\n"
|
|
$WGET --no-use-server-timestamps -qO "$VERSIONS_JSON" "$MINECRAFT_VERSIONS_URL"
|
|
if [ $? -ne 0 ]; then
|
|
if [ -s $VERSIONS_JSON.bak ]; then
|
|
printf "Error downloading the version manifest, using a backup.\n"
|
|
cp -p "$VERSIONS_JSON.bak" "$VERSIONS_JSON"
|
|
else
|
|
printf "Error downloading the version manifest, exiting.\n"
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the current Minecraft version number.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return The current Minecraft version number.
|
|
# ---------------------------------------------------------------------------
|
|
getCurrentMinecraftVersion() {
|
|
local VERSION TYPE
|
|
# Determine the version type for the current world.
|
|
TYPE=$(getMSCSValue "$1" "mscs-version-type" "$DEFAULT_VERSION_TYPE")
|
|
# Extract the current version information.
|
|
VERSION=$(cat $VERSIONS_JSON | $PERL -0777 -sne '
|
|
use JSON;
|
|
$json = decode_json ($_);
|
|
$version = $json->{latest}{$type};
|
|
$version =~ s/[\s#%*+?^\${}()|[\]\\]/-/g;
|
|
print $version;
|
|
' -- -type="$TYPE")
|
|
# Print an error and exit if the version string is empty.
|
|
if [ -z "$VERSION" ]; then
|
|
printf "Error detecting the current Minecraft version.\n"
|
|
exit 1
|
|
fi
|
|
printf "$VERSION"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the sha1sum for a Minecraft client or server version.
|
|
#
|
|
# @param 1 The Minecraft version.
|
|
# @param 2 The type of sha1sum needed (client, server).
|
|
# @return The sha1sum for the Minecraft client or server.
|
|
# ---------------------------------------------------------------------------
|
|
getMinecraftVersionDownloadSHA1() {
|
|
cat $VERSIONS_JSON | $PERL -0777 -sne '
|
|
use JSON;
|
|
use LWP::Simple;
|
|
my $json = decode_json ($_);
|
|
my $v;
|
|
foreach $ver (@{$json->{versions}}) {
|
|
$id = $ver->{id};
|
|
$id =~ s/[\s#%*+?^\${}()|[\]\\]/-/g;
|
|
if ($id eq "$version") {
|
|
$v = $ver;
|
|
last;
|
|
}
|
|
}
|
|
$json = decode_json (get ($v->{url}));
|
|
print $json->{downloads}{$type}{sha1};
|
|
' -- -version="$1" -type="$2"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the download URL for a Minecraft client or server version.
|
|
#
|
|
# @param 1 The Minecraft version.
|
|
# @param 2 The type of download URL needed (client, server).
|
|
# @return The download URL for the Minecraft client or server.
|
|
# ---------------------------------------------------------------------------
|
|
getMinecraftVersionDownloadURL() {
|
|
cat $VERSIONS_JSON | $PERL -0777 -sne '
|
|
use JSON;
|
|
use LWP::Simple;
|
|
my $json = decode_json ($_);
|
|
my $v;
|
|
foreach $ver (@{$json->{versions}}) {
|
|
$id = $ver->{id};
|
|
$id =~ s/[\s#%*+?^\${}()|[\]\\]/-/g;
|
|
if ($id eq "$version") {
|
|
$v = $ver;
|
|
last;
|
|
}
|
|
}
|
|
$json = decode_json (get ($v->{url}));
|
|
print $json->{downloads}{$type}{url};
|
|
' -- -version="$1" -type="$2"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get the release time of the Minecraft version number.
|
|
#
|
|
# @param 1 The Minecraft version.
|
|
# @return The release time of the Minecraft version number.
|
|
# ---------------------------------------------------------------------------
|
|
getMinecraftVersionReleaseTime() {
|
|
cat $VERSIONS_JSON | $PERL -0777 -sne '
|
|
use JSON;
|
|
$json = decode_json ($_);
|
|
foreach $ver (@{$json->{versions}}) {
|
|
$id = $ver->{id};
|
|
$id =~ s/[\s#%*+?^\${}()|[\]\\]/-/g;
|
|
print $ver->{releaseTime} if ($id eq "$version");
|
|
}
|
|
' -- -version="$1"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the version of the client for the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return CLIENT_VERSION
|
|
# ---------------------------------------------------------------------------
|
|
getClientVersion() {
|
|
local CURRENT_VERSION
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the client version, use the default version if not provided.
|
|
getMSCSValue "$1" "mscs-client-version" "$DEFAULT_CLIENT_VERSION" |
|
|
$PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/[\s#%*+?^\${}()|[\]\\]/-/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the .jar file name for the client for the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return CLIENT_JAR
|
|
# ---------------------------------------------------------------------------
|
|
getClientJar() {
|
|
local CURRENT_VERSION CLIENT_VERSION
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
CLIENT_VERSION=$(getClientVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CLIENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the client jar, use the default value if not provided.
|
|
getMSCSValue "$1" "mscs-client-jar" "$DEFAULT_CLIENT_JAR" |
|
|
$PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/\$CLIENT_VERSION/$client_version/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION" -client_version="$CLIENT_VERSION"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the location of the client files for the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return CLIENT_LOCATION
|
|
# ---------------------------------------------------------------------------
|
|
getClientLocation() {
|
|
local CURRENT_VERSION CLIENT_VERSION
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
CLIENT_VERSION=$(getClientVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CLIENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the client location, use the default value if not provided.
|
|
getMSCSValue "$1" "mscs-client-location" "$DEFAULT_CLIENT_LOCATION" |
|
|
$PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/\$CLIENT_VERSION/$client_version/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION" -client_version="$CLIENT_VERSION"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the URL to download the client for the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return CLIENT_URL
|
|
# ---------------------------------------------------------------------------
|
|
getClientURL() {
|
|
local CURRENT_VERSION CLIENT_VERSION URL
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
CLIENT_VERSION=$(getClientVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CLIENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the client download URL, use the default value if not provided.
|
|
URL=$(getMSCSValue "$1" "mscs-client-url" "$DEFAULT_CLIENT_URL" | $PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/\$CLIENT_VERSION/$client_version/g;
|
|
$_ =~ s/\\\:/\:/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION" -client_version="$CLIENT_VERSION")
|
|
# If the download URL was not specified, extract it from the version
|
|
# manifest.
|
|
if [ -z "$URL" ]; then
|
|
URL=$(getMinecraftVersionDownloadURL $CLIENT_VERSION 'client')
|
|
fi
|
|
printf "$URL"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the version of the server running the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return SERVER_VERSION
|
|
# ---------------------------------------------------------------------------
|
|
getServerVersion() {
|
|
local CURRENT_VERSION
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the server version, use the default version if not provided.
|
|
getMSCSValue "$1" "mscs-server-version" "$DEFAULT_SERVER_VERSION" |
|
|
$PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/[\s#%*+?^\${}()|[\]\\]/-/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the .jar file name for the server running the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return SERVER_JAR
|
|
# ---------------------------------------------------------------------------
|
|
getServerJar() {
|
|
local CURRENT_VERSION SERVER_VERSION SERVER_JAR
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
SERVER_VERSION=$(getServerVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the server jar, use the default value if not provided.
|
|
getMSCSValue "$1" "mscs-server-jar" "$DEFAULT_SERVER_JAR" |
|
|
$PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/\$SERVER_VERSION/$server_version/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION" -server_version="$SERVER_VERSION"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the location of the server files for the server running the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return SERVER_LOCATION
|
|
# ---------------------------------------------------------------------------
|
|
getServerLocation() {
|
|
local CURRENT_VERSION SERVER_VERSION
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
SERVER_VERSION=$(getServerVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the server location, use the default value if not provided.
|
|
getMSCSValue "$1" "mscs-server-location" "$DEFAULT_SERVER_LOCATION" |
|
|
$PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/\$SERVER_VERSION/$server_version/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION" -server_version="$SERVER_VERSION"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the URL to download the server running the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return SERVER_URL
|
|
# ---------------------------------------------------------------------------
|
|
getServerURL() {
|
|
local CURRENT_VERSION SERVER_VERSION URL
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
SERVER_VERSION=$(getServerVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Get the server download URL, use the default value if not provided.
|
|
URL=$(getMSCSValue "$1" "mscs-server-url" "$DEFAULT_SERVER_URL" | $PERL -sne '
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/\$SERVER_VERSION/$server_version/g;
|
|
$_ =~ s/\\\:/\:/g;
|
|
print;
|
|
' -- -current_version="$CURRENT_VERSION" -server_version="$SERVER_VERSION")
|
|
# If the download URL was not specified, extract it from the version
|
|
# manifest.
|
|
if [ -z "$URL" ]; then
|
|
URL=$(getMinecraftVersionDownloadURL $SERVER_VERSION 'server')
|
|
fi
|
|
printf "$URL"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the command to start the server running the world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @return SERVER_COMMAND
|
|
# ---------------------------------------------------------------------------
|
|
getServerCommand() {
|
|
local CURRENT_VERSION SERVER_VERSION SERVER_JAR SERVER_LOCATION
|
|
local SERVER_ARGS INITIAL_MEMORY MAXIMUM_MEMORY
|
|
CURRENT_VERSION=$(getCurrentMinecraftVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CURRENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
SERVER_VERSION=$(getServerVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_VERSION\n"
|
|
exit 1
|
|
fi
|
|
SERVER_JAR=$(getServerJar "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_JAR\n"
|
|
exit 1
|
|
fi
|
|
SERVER_LOCATION=$(getServerLocation "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_LOCATION\n"
|
|
exit 1
|
|
fi
|
|
# Get the jvm arguments, use the default value if not provided.
|
|
JVM_ARGS=$(
|
|
getMSCSValue "$1" "mscs-jvm-args" "$DEFAULT_JVM_ARGS"
|
|
)
|
|
# Get the server arguments, use the default value if not provided.
|
|
SERVER_ARGS=$(
|
|
getMSCSValue "$1" "mscs-server-args" "$DEFAULT_SERVER_ARGS"
|
|
)
|
|
# Get the initial memory, use the default value if not provided.
|
|
INITIAL_MEMORY=$(
|
|
getMSCSValue "$1" "mscs-initial-memory" "$DEFAULT_INITIAL_MEMORY"
|
|
)
|
|
# Get the maximum memory, use the default value if not provided.
|
|
MAXIMUM_MEMORY=$(
|
|
getMSCSValue "$1" "mscs-maximum-memory" "$DEFAULT_MAXIMUM_MEMORY"
|
|
)
|
|
# Get the server command, use the default value if not provided.
|
|
getMSCSValue "$1" "mscs-server-command" "$DEFAULT_SERVER_COMMAND" |
|
|
$PERL -sne '
|
|
$_ =~ s/\$JAVA/$java/g;
|
|
$_ =~ s/\$CURRENT_VERSION/$current_version/g;
|
|
$_ =~ s/\$SERVER_VERSION/$server_version/g;
|
|
$_ =~ s/\$JVM_ARGS/$jvm_args/;
|
|
$_ =~ s/\$SERVER_JAR/$server_jar/g;
|
|
$_ =~ s/\$SERVER_LOCATION/$server_location/g;
|
|
$_ =~ s/\$SERVER_ARGS/$server_args/g;
|
|
$_ =~ s/\$INITIAL_MEMORY/$initial_memory/g;
|
|
$_ =~ s/\$MAXIMUM_MEMORY/$maximum_memory/g;
|
|
print;
|
|
' -- \
|
|
-java="$JAVA" \
|
|
-current_version="$CURRENT_VERSION" \
|
|
-server_version="$SERVER_VERSION" \
|
|
-jvm_args="$JVM_ARGS" \
|
|
-server_jar="$SERVER_JAR" \
|
|
-server_location="$SERVER_LOCATION" \
|
|
-server_args="$SERVER_ARGS" \
|
|
-initial_memory="$INITIAL_MEMORY" \
|
|
-maximum_memory="$MAXIMUM_MEMORY"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Create a lock file for a world. This will help avoid having multiple long
|
|
# running processes running at the same time. This code was inspired by
|
|
# http://bencane.com/2015/09/22/preventing-duplicate-cron-job-executions/
|
|
#
|
|
# @param 1 The world server.
|
|
# @param 2 The type of lock file.
|
|
# @return TRUE if lock file created, FALSE otherwise.
|
|
# ---------------------------------------------------------------------------
|
|
createLockFile() {
|
|
local PID LOCKFILE
|
|
LOCKFILE=$WORLDS_LOCATION/$1/lock-$2.pid
|
|
# Delete the old LOCKFILE.
|
|
find $LOCKFILE -mmin +"$LOCKFILE_DURATION" -delete >/dev/null 2>&1
|
|
# Check to see if the LOCKFILE exists.
|
|
if [ -s $LOCKFILE ]; then
|
|
PID=$(cat $LOCKFILE)
|
|
# LOCKFILE exists, check to see if its process is running.
|
|
ps -p $PID >/dev/null 2>&1
|
|
if [ $? -eq 0 ]; then
|
|
# LOCKFILE exists and the process is running.
|
|
# Return a false value (failure).
|
|
return 1
|
|
else
|
|
# Process not found assume it is not running.
|
|
echo $$ >$LOCKFILE
|
|
if [ $? -ne 0 ]; then
|
|
# Error creating LOCKFILE.
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
fi
|
|
else
|
|
# LOCKFILE does not exists.
|
|
echo $$ >$LOCKFILE
|
|
if [ $? -ne 0 ]; then
|
|
# Error creating LOCKFILE.
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
fi
|
|
# Success creating LOCKFILE.
|
|
# Return a true value (success).
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Remove a lock file for a world.
|
|
#
|
|
# @param 1 The world server.
|
|
# @param 2 The type of lock file.
|
|
# ---------------------------------------------------------------------------
|
|
removeLockFile() {
|
|
rm -f $WORLDS_LOCATION/$1/lock-$2.pid
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Remove old world log files and rotate log files from old servers (v < 1.7).
|
|
#
|
|
# @param 1 The world server generating the log to rotate.
|
|
# ---------------------------------------------------------------------------
|
|
rotateLog() {
|
|
local WORLD_DIR DATE LOG NUM
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
# Make sure the log directory exists.
|
|
mkdir -p "$WORLD_DIR/logs"
|
|
if [ "$LOG_DURATION" -gt 0 ]; then
|
|
# Delete old log files.
|
|
find "$WORLD_DIR/logs" -type f -mtime +"$LOG_DURATION" -delete
|
|
fi
|
|
# Archive and rotate the log files for old Minecraft servers (Versions 1.7
|
|
# and greater do this automatically).
|
|
if [ -e "$WORLD_DIR/server.log" ] &&
|
|
[ $(wc -l <$WORLD_DIR/server.log) -ge 1 ]; then
|
|
# Make a copy of the log file in the world's logs directory.
|
|
DATE=$(date +%F)
|
|
cp "$WORLD_DIR/server.log" "$WORLD_DIR/logs/$DATE-0.log"
|
|
gzip "$WORLD_DIR/logs/$DATE-0.log"
|
|
# Empty the contents of the worlds log file.
|
|
cp /dev/null "$WORLD_DIR/server.log"
|
|
# Rotate the files in the world's logs directory.
|
|
for LOG in $(ls -r $WORLD_DIR/logs/$DATE-*.log.gz); do
|
|
NUM=$(echo $LOG | $PERL -sne 'print ($1 + 1) if ($_ =~ /\Q$date\E-(\d+)/)' -- -date="$DATE")
|
|
mv -f $LOG "$WORLD_DIR/logs/$DATE-$NUM.log.gz"
|
|
done
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Watch the world latest.log file.
|
|
#
|
|
# @param 1 The world server generating the log to watch.
|
|
# ---------------------------------------------------------------------------
|
|
watchLog() {
|
|
local WORLD_DIR
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
# Make sure that the latest.log file exists.
|
|
if [ -e "$WORLD_DIR/logs/latest.log" ]; then
|
|
# Watch the log file of worlds running Minecraft 1.7 +.
|
|
tail -n0 -f --pid=$(getJavaPID "$1") $WORLD_DIR/logs/latest.log
|
|
elif [ -e "$WORLD_DIR/server.log" ]; then
|
|
# Watch the log file of worlds running older Minecraft servers.
|
|
tail -n0 -f --pid=$(getJavaPID "$1") $WORLD_DIR/server.log
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Synchronizes the data stored in the mirror images.
|
|
#
|
|
# @param 1 The world server to sync.
|
|
# ---------------------------------------------------------------------------
|
|
syncMirrorImage() {
|
|
# Sync the world server.
|
|
$RSYNC -a --delete "$WORLDS_LOCATION/$1/$1/" "$WORLDS_LOCATION/$1/$1-original"
|
|
if [ $? -ne 0 ]; then
|
|
printf "Error synchronizing mirror images for world $1.\n"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Display the server console.
|
|
#
|
|
# @param 1 The world server to connect to.
|
|
# ---------------------------------------------------------------------------
|
|
serverConsole() {
|
|
local LINE WORLD_DIR
|
|
# Make sure that the world's directory exists.
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
# Follow the console output buffer file.
|
|
tail -f --pid=$$ $WORLD_DIR/console.out &
|
|
# Copy user input to the console input buffer file.
|
|
while read LINE; do
|
|
echo "$LINE" >>$WORLD_DIR/console.in
|
|
done
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retrieve the timestamp.
|
|
#
|
|
# @return The current date and time.
|
|
# ---------------------------------------------------------------------------
|
|
timestamp() {
|
|
date +"%Y-%m-%d_%H-%M-%S"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Stop the server monitor.
|
|
#
|
|
# @param 1 The world server to stop.
|
|
# ---------------------------------------------------------------------------
|
|
stopServerMonitor() {
|
|
local WORLD_DIR MONITOR_LOG MONITOR_PID MONITOR_LOCK_FILE ACQUIRED_LOCK
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
MONITOR_LOG="$WORLD_DIR/logs/mscs.monitor.log"
|
|
MONITOR_LOCK_FILE="$WORLD_DIR/monitor.lock"
|
|
# Check if server monitor instance currently running.
|
|
MONITOR_PID_PATH="$WORLD_DIR/monitor.pid"
|
|
if [ ! -f "$MONITOR_PID_PATH" ]; then
|
|
return
|
|
fi
|
|
MONITOR_PID=$(cat "$MONITOR_PID_PATH")
|
|
(
|
|
$FLOCK -n 9
|
|
ACQUIRED_LOCK=$?
|
|
if [ "$ACQUIRED_LOCK" -eq 1 ]; then # Server monitor is running.
|
|
printf "[$(timestamp)] [INFO]: Stop command received for server monitor. Attempting to kill server monitor...\n" >> "$MONITOR_LOG"
|
|
# Kill the server monitor.
|
|
kill -9 "$MONITOR_PID"
|
|
# Verify it was actually killed.
|
|
if [ $? -eq 1 ]; then
|
|
printf "[$(timestamp)] [ERROR]: Unable to kill monitor process.\n" >> "$MONITOR_LOG"
|
|
exit 1
|
|
else
|
|
printf "[$(timestamp)] [INFO]: Server monitor process killed successfully.\n" >> "$MONITOR_LOG"
|
|
# Remove the monitor PID file.
|
|
rm -f "$WORLD_DIR/monitor.pid"
|
|
fi
|
|
fi
|
|
) 9>"$MONITOR_LOCK_FILE"
|
|
}
|
|
# ---------------------------------------------------------------------------
|
|
# Run the server monitor.
|
|
#
|
|
# @param 1 The world server to monitor.
|
|
# ---------------------------------------------------------------------------
|
|
serverMonitor() {
|
|
local WORLD_DIR MONITOR_LOG SERVER_LOG LAST_START_STATUS_LOG MONITOR_PID
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
MONITOR_LOG="$WORLD_DIR/logs/mscs.monitor.log"
|
|
SERVER_LOG="$WORLD_DIR/logs/latest.log"
|
|
LAST_START_STATUS_LOG="$WORLD_DIR/logs/last-start-status.log"
|
|
MONITOR_PID=$(cat "$WORLD_DIR/monitor.pid")
|
|
touch $LAST_START_STATUS_LOG
|
|
|
|
printf "[$(timestamp)] [INFO]: Server monitoring started for $1. Server PID: $(getJavaPID $1). Monitor PID: $MONITOR_PID.\n"
|
|
# Run monitor until the server is stopped and the PID file is removed (i.e. clean shutdown).
|
|
until ! serverRunning $1 && [ ! -f "$WORLDS_LOCATION/$1.pid" ]; do
|
|
# If server isn't running and server PID file exists, server crashed.
|
|
if ! serverRunning $1 && [ -f "$WORLDS_LOCATION/$1.pid" ]; then
|
|
printf "[$(timestamp)] [WARN]: Server crash detected. Attempting to restart $1...\n"
|
|
start $1
|
|
# Verify that the server restarted successfully.
|
|
if [ $? -eq 0 ]; then
|
|
printf "[$(timestamp)] [INFO]: Server monitoring resumed for $1. Server PID: $(getJavaPID $1). Monitor PID: $MONITOR_PID.\n"
|
|
printf " $1 automatically restarted from a crash (or in-game stop command)\n" > "$LAST_START_STATUS_LOG"
|
|
printf " on $(timestamp). See\n" >> "$LAST_START_STATUS_LOG"
|
|
printf " $WORLD_DIR/logs/mscs.monitor.log and\n" >> "$LAST_START_STATUS_LOG"
|
|
printf " $WORLD_DIR/crash_reports/ and\n" >> "$LAST_START_STATUS_LOG"
|
|
printf " $WORLD_DIR/logs/ for more information.\n" >> "$LAST_START_STATUS_LOG"
|
|
else
|
|
printf "[$(timestamp)] [ERROR]: Failed to restart $1.\n"
|
|
stopServerMonitor $1
|
|
fi
|
|
# If server is running and server PID file doesn't exist, error occurred.
|
|
elif serverRunning $1 && [ ! -f "$WORLDS_LOCATION/$1.pid" ]; then
|
|
printf "[$(timestamp)] [ERROR]: PID file doesn't exist.\n"
|
|
stopServerMonitor $1
|
|
fi
|
|
done
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Start the server monitor.
|
|
#
|
|
# @param 1 The world server to monitor.
|
|
# ---------------------------------------------------------------------------
|
|
startServerMonitor() {
|
|
local WORLD_DIR MONITOR_LOG MONITOR_PID MONITOR_LOCK_FILE ACQUIRED_LOCK
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
MONITOR_LOG="$WORLD_DIR/logs/mscs.monitor.log"
|
|
MONITOR_PID="$WORLD_DIR/monitor.pid"
|
|
MONITOR_LOCK_FILE="$WORLD_DIR/monitor.lock"
|
|
RESTART_AFTER_CRASH=$(getMSCSValue "$1" "mscs-restart-after-crash" "$DEFAULT_RESTART_AFTER_CRASH")
|
|
|
|
# Verify option is enabled.
|
|
if true_value "$RESTART_AFTER_CRASH"; then
|
|
# Verify that there is no monitor instance currently running.
|
|
(
|
|
$FLOCK -n 9
|
|
ACQUIRED_LOCK="$?"
|
|
if [ "$ACQUIRED_LOCK" -eq 0 ]; then # Server monitor doesn't exist.
|
|
# Delete old log file greater than $LOG_DURATION, if it exists.
|
|
if [ -f "$MONITOR_LOG" ]; then
|
|
if [ "$LOG_DURATION" -gt 0 ]; then
|
|
find "$MONITOR_LOG" -type f -mtime +"$LOG_DURATION" -delete >/dev/null 2>&1
|
|
fi
|
|
fi
|
|
# Run the server monitor.
|
|
# Nohup does not allow you to pass functions. However, the code below mimics nohup behavior by doing the following:
|
|
# Start subshell, ignore HUP signal, redirect stdin to /dev/null, redirect stdout and stderr to log file, run in background.
|
|
# Also store the PID of this process for later use.
|
|
( trap "" HUP ; echo $(exec sh -c 'echo "$PPID"') > "$MONITOR_PID"; serverMonitor $1 ) </dev/null 2>&1 1>>"$MONITOR_LOG" &
|
|
fi
|
|
) 9>"$MONITOR_LOCK_FILE"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Start the world server. Generate the appropriate environment for the
|
|
# server if it doesn't already exist.
|
|
#
|
|
# @param 1 The world server to start.
|
|
# ---------------------------------------------------------------------------
|
|
start() {
|
|
local EULA PID SERVER_COMMAND WORLD_DIR
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
|
|
# Pre-start Hook script execution
|
|
PRE_START_HOOK="$WORLD_DIR/hooks/pre-start.sh"
|
|
if [ -x "$PRE_START_HOOK" ]; then
|
|
printf "Executing pre-start hook script for world %s...\n" "$1"
|
|
"$PRE_START_HOOK"
|
|
if [ $? -ne 0 ]; then
|
|
printf "Warning: pre-start hook script for world %s exited with a non-zero status.\n" "$1"
|
|
fi
|
|
fi
|
|
|
|
# Make sure that the server software exists.
|
|
updateServerSoftware "$1"
|
|
# Make sure that the world's directory exists.
|
|
mkdir -p "$WORLD_DIR"
|
|
# If the original level exists but the actual level doesn't,
|
|
# we probably restored a backup.
|
|
if [ -d "$WORLD_DIR/$1-original" ] && [ ! -e "$WORLD_DIR/$1" ] && [ ! -L "$WORLD_DIR/$1" ]; then
|
|
# Restore the original world files.
|
|
mv "$WORLD_DIR/$1-original" "$WORLD_DIR/$1"
|
|
fi
|
|
# Make sure that the level directory exists.
|
|
if [ ! -e "$WORLD_DIR/$1" ] && [ ! -L "$WORLD_DIR/$1" ]; then
|
|
mkdir -p "$WORLD_DIR/$1"
|
|
fi
|
|
# Make sure the EULA has been set to true.
|
|
EULA=$(getEULAValue "$1")
|
|
if ! true_value "$EULA"; then
|
|
printf "\nWarning, the EULA has not been accepted for world %s.\n" "$1"
|
|
printf "To accept the EULA, modify %s/eula.txt file.\n" "$WORLD_DIR"
|
|
fi
|
|
# Rotate the world's log files.
|
|
rotateLog "$1"
|
|
# Make a mirror image of the world directory if requested.
|
|
if true_value $ENABLE_MIRROR; then
|
|
# Make sure the main mirror folder exists.
|
|
mkdir -p "$MIRROR_PATH"
|
|
# If the symlink exists but the target doesn't, the
|
|
# mirror was removed somehow. This is bad.
|
|
if [ -L "$WORLD_DIR/$1" ] && [ ! -d "$MIRROR_PATH/$1" ]; then
|
|
# Remove the symlink to the mirror image.
|
|
rm -f "$WORLD_DIR/$1"
|
|
# Restore the original world files.
|
|
mv "$WORLD_DIR/$1-original" "$WORLD_DIR/$1"
|
|
# Send notification.
|
|
printf "Warning, the mirror for %s was removed, restored latest world.\n" $1
|
|
fi
|
|
# If the symlink still exists, the server was stopped outside of mscs.
|
|
# SO DONT RESET THE MIRROR, just keep using it.
|
|
if [ ! -L "$WORLD_DIR/$1" ]; then
|
|
# Remove the mirror directory just in case.
|
|
rm -Rf "$MIRROR_PATH/$1"
|
|
# Copy the world files over to the mirror.
|
|
cp -a "$WORLD_DIR/$1" "$MIRROR_PATH/$1"
|
|
if [ $? -ne 0 ]; then
|
|
printf "Error copying world data, could not copy to %s.\n" $MIRROR_PATH/$1
|
|
exit 1
|
|
fi
|
|
# Remove the world file backup directory just in case.
|
|
rm -Rf "$WORLD_DIR/$1-original"
|
|
# Rename the original world file directory.
|
|
mv "$WORLD_DIR/$1" "$WORLD_DIR/$1-original"
|
|
# Create a symlink from the world file directory's original name to the mirrored files.
|
|
ln -s "$MIRROR_PATH/$1" "$WORLD_DIR/$1"
|
|
fi
|
|
fi
|
|
# Change to the world's directory.
|
|
cd $WORLD_DIR
|
|
# Delete any old console.in or console.out buffer files.
|
|
rm -f "$WORLD_DIR/console.in" "$WORLD_DIR/console.out"
|
|
# Initialize the console.in buffer file.
|
|
printf '' >$WORLD_DIR/console.in
|
|
# Get the server command for this world.
|
|
SERVER_COMMAND=$(getServerCommand "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_COMMAND\n"
|
|
exit 1
|
|
fi
|
|
# Start the server.
|
|
nohup sh -c "tail -f --pid=\$$ $WORLD_DIR/console.in | {
|
|
$SERVER_COMMAND mscs-world=$1 > $WORLD_DIR/console.out 2>&1; kill \$$;
|
|
}" >/dev/null 2>&1 &
|
|
# Verify the server is running.
|
|
if [ $? -ne 0 ]; then
|
|
printf "Error starting the server.\n"
|
|
exit 1
|
|
fi
|
|
# Turn on terminal echo to work around issue with some server software.
|
|
tty -s && tset
|
|
# Retrieve the process ID for the server.
|
|
sleep 1
|
|
PID=$(getJavaPID "$1")
|
|
if [ $PID -eq 0 ]; then
|
|
printf "Error starting the server: couldn't retrieve the server's process ID.\n"
|
|
exit 1
|
|
fi
|
|
# Start the Query handler if enabled.
|
|
if true_value "$(getServerPropertiesValue $1 'enable-query')"; then
|
|
queryStart "$1"
|
|
# Sleep for a second to workaround issue with ssh using the forced
|
|
# pseudo-terminal allocation command line option.
|
|
sleep 1
|
|
fi
|
|
# Create a PID file for the world server.
|
|
echo $PID >"$WORLDS_LOCATION/$1.pid"
|
|
# Start the server crash monitor, if enabled.
|
|
startServerMonitor $1
|
|
|
|
# Hook script execution
|
|
HOOK_SCRIPT="$WORLD_DIR/hooks/post-start.sh"
|
|
if [ -x "$HOOK_SCRIPT" ]; then
|
|
printf "Executing post-start hook script for world %s...\n" "$1"
|
|
"$HOOK_SCRIPT"
|
|
if [ $? -ne 0 ]; then
|
|
printf "Warning: post-start hook script for world %s exited with a non-zero status.\n" "$1"
|
|
fi
|
|
fi
|
|
}
|
|
# ---------------------------------------------------------------------------
|
|
# Stop the world server.
|
|
#
|
|
# @param 1 The world server to stop.
|
|
# ---------------------------------------------------------------------------
|
|
stop() {
|
|
# Pre-stop Hook script execution
|
|
PRE_STOP_HOOK="$WORLDS_LOCATION/$1/hooks/pre-stop.sh"
|
|
if [ -x "$PRE_STOP_HOOK" ]; then
|
|
printf "Executing pre-stop hook script for world %s...\n" "$1"
|
|
"$PRE_STOP_HOOK"
|
|
if [ $? -ne 0 ]; then
|
|
printf "Warning: pre-stop hook script for world %s exited with a non-zero status.\n" "$1"
|
|
fi
|
|
fi
|
|
|
|
# Stop the server monitor if it is running.
|
|
stopServerMonitor $1
|
|
# Tell the server to stop.
|
|
sendCommand $1 "stop"
|
|
sendCommand $1 "end"
|
|
# Wait for the server to shut down.
|
|
while serverRunning $1; do
|
|
sleep 1
|
|
done
|
|
# Synchronize the mirror image of the world prior to closing, if required.
|
|
if true_value $ENABLE_MIRROR && [ -L "$WORLDS_LOCATION/$1/$1" ] && [ -d "$MIRROR_PATH/$1" ]; then
|
|
syncMirrorImage $1
|
|
# Remove the symlink to the world-file mirror image.
|
|
rm -f "$WORLDS_LOCATION/$1/$1"
|
|
# Remove the world-file mirror image folder.
|
|
rm -Rf "$MIRROR_PATH/$1"
|
|
# Move the world files back to their original path name.
|
|
mv "$WORLDS_LOCATION/$1/$1-original" "$WORLDS_LOCATION/$1/$1"
|
|
fi
|
|
# Remove the PID file for the world server.
|
|
rm -f "$WORLDS_LOCATION/$1.pid"
|
|
|
|
# Hook script execution
|
|
HOOK_SCRIPT="$WORLDS_LOCATION/$1/hooks/post-stop.sh"
|
|
if [ -x "$HOOK_SCRIPT" ]; then
|
|
printf "Executing post-stop hook script for world %s...\n" "$1"
|
|
"$HOOK_SCRIPT"
|
|
if [ $? -ne 0 ]; then
|
|
printf "Warning: post-stop hook script for world %s exited with a non-zero status.\n" "$1"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Forcibly stop the world server.
|
|
#
|
|
# @param 1 The world server to forcibly stop.
|
|
# ---------------------------------------------------------------------------
|
|
forceStop() {
|
|
local WAIT
|
|
# Stop the server monitor if it is running.
|
|
stopServerMonitor $1
|
|
# Try to stop the server cleanly first.
|
|
sendCommand $1 "stop"
|
|
sendCommand $1 "end"
|
|
# Wait for the server to shut down.
|
|
WAIT=0
|
|
while serverRunning $1 && [ $WAIT -le 20 ]; do
|
|
WAIT=$(($WAIT + 1))
|
|
sleep 1
|
|
done
|
|
# Kill the process id of the world server.
|
|
kill -9 $(getJavaPID "$1") >/dev/null 2>&1
|
|
# Properly clean up.
|
|
stop $1
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Backup the world server.
|
|
#
|
|
# @param 1 The world server to backup.
|
|
# @return A 0 if backup successful, a 1 otherwise
|
|
# ---------------------------------------------------------------------------
|
|
worldBackup() {
|
|
local EXCLUDE_OPTION
|
|
# Make sure that the backup location exists.
|
|
if ! mkdir -p "$BACKUP_LOCATION"; then
|
|
echo "Error creating backup dir $BACKUP_LOCATION"
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
# Synchronize the mirror image of the world prior to closing, if required.
|
|
if true_value $ENABLE_MIRROR && [ -L "$WORLDS_LOCATION/$1/$1" ] && [ -d "$MIRROR_PATH/$1" ]; then
|
|
syncMirrorImage $1
|
|
fi
|
|
EXCLUDE_OPTION=" \
|
|
--exclude $WORLDS_LOCATION/$1/console.out \
|
|
--exclude $WORLDS_LOCATION/$1/query.out \
|
|
--exclude $WORLDS_LOCATION/$1/logs/latest.log \
|
|
"
|
|
# Add all user-specified excluded files
|
|
OLD_IFS=$IFS
|
|
IFS=,
|
|
# Check for world-specific excluded files
|
|
WORLD_BACKUP_EXCLUDED_FILES=$(getMSCSValue $1 "mscs-backup-excluded-files" "$BACKUP_EXCLUDED_FILES")
|
|
#For each comma separated value, evaluate the $WORLD_NAME variable, and add to the list of rdiff_backup excluded files
|
|
for EXCLUDED_USER_LOCATION in $WORLD_BACKUP_EXCLUDED_FILES; do
|
|
EVALUATED_EXCLUDED_USER_LOCATION=$(echo "$EXCLUDED_USER_LOCATION" | $PERL -sne '
|
|
$_ =~ s/\$WORLD_NAME/$world_name/g;
|
|
print;
|
|
' -- -world_name="$1")
|
|
EXCLUDE_OPTION="$EXCLUDE_OPTION --exclude $WORLDS_LOCATION/$1/$EVALUATED_EXCLUDED_USER_LOCATION \
|
|
"
|
|
done
|
|
IFS=$OLD_IFS
|
|
# Determine if we need to exclude the mirrored symlink or not
|
|
if [ -L "$WORLDS_LOCATION/$1/$1" ]; then
|
|
EXCLUDE_OPTION="$EXCLUDE_OPTION --exclude $WORLDS_LOCATION/$1/$1"
|
|
fi
|
|
# Create the backup.
|
|
if ! $RDIFF_BACKUP -v5 --print-statistics $EXCLUDE_OPTION "$WORLDS_LOCATION/$1" "$BACKUP_LOCATION/$1" >>"$BACKUP_LOG"; then
|
|
echo "Error doing backup of world $1"
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
# Return a true value (success).
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Cleanup old backups for the world server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# ---------------------------------------------------------------------------
|
|
worldBackupCleanup() {
|
|
# Cleanup old backups.
|
|
if [ $BACKUP_DURATION -gt 0 ]; then
|
|
if ! $RDIFF_BACKUP --remove-older-than ${BACKUP_DURATION}D --force "$BACKUP_LOCATION/$1" >>"$BACKUP_LOG"; then
|
|
echo "Error cleaning old backups of world $1"
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
fi
|
|
# Return a true value (success).
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# List the backups for the world server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# ---------------------------------------------------------------------------
|
|
worldBackupList() {
|
|
local SEC TYPE
|
|
if [ -d $BACKUP_LOCATION/$1 ]; then
|
|
# Grab the list of backups for the world server.
|
|
$RDIFF_BACKUP --parsable-output -l $BACKUP_LOCATION/$1 |
|
|
while read SEC TYPE; do
|
|
date --date=\@$SEC --rfc-3339=seconds | tr ' ' T
|
|
done
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Restore a backup for the world server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @param 2 The datetime of the backup to restore.
|
|
# @return A 0 on success, a 1 otherwise.
|
|
# ---------------------------------------------------------------------------
|
|
worldBackupRestore() {
|
|
local TARGET TARGET_TMP
|
|
TARGET="$WORLDS_LOCATION/$1"
|
|
TARGET_TMP="${TARGET}_bktmp"
|
|
rm -rf "$TARGET_TMP"
|
|
if [ -d $BACKUP_LOCATION/$1 ]; then
|
|
if $RDIFF_BACKUP -r $2 "$BACKUP_LOCATION/$1" "$TARGET_TMP"; then
|
|
rm -r "$TARGET"
|
|
mv "$TARGET_TMP" "$TARGET"
|
|
else
|
|
echo "Restoring backup failed: $1 $2"
|
|
rm -rf "$TARGET_TMP"
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
else
|
|
echo "No backups found for world $1"
|
|
# Return a false value (failure).
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Update the Minecraft client software.
|
|
#
|
|
# @param 1 The world to update.
|
|
# ---------------------------------------------------------------------------
|
|
updateClientSoftware() {
|
|
local CLIENT_JAR CLIENT_LOCATION CLIENT_URL CLIENT_VERSION SHA1 SHA1_FILE
|
|
CLIENT_JAR=$(getClientJar "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CLIENT_JAR\n"
|
|
exit 1
|
|
fi
|
|
CLIENT_LOCATION=$(getClientLocation "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CLIENT_LOCATION\n"
|
|
exit 1
|
|
fi
|
|
CLIENT_URL=$(getClientURL "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CLIENT_URL\n"
|
|
exit 1
|
|
fi
|
|
CLIENT_VERSION=$(getClientVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$CLIENT_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Make sure the client software directory exists.
|
|
mkdir -p "$CLIENT_LOCATION"
|
|
# Download the client jar if it is missing.
|
|
if [ ! -s "$CLIENT_LOCATION/$CLIENT_JAR" ]; then
|
|
# Download the Minecraft client software.
|
|
$WGET -qO "$CLIENT_LOCATION/$CLIENT_JAR" "$CLIENT_URL"
|
|
# Report any errors.
|
|
if [ $? -ne 0 ]; then
|
|
printf "Error downloading the Minecraft client software.\n"
|
|
exit 1
|
|
fi
|
|
SHA1=$(getMinecraftVersionDownloadSHA1 $CLIENT_VERSION 'client')
|
|
if [ -z $SHA1 ]; then
|
|
printf "Error retrieving the sha1 for the Minecraft client software.\n"
|
|
exit 1
|
|
fi
|
|
SHA1_FILE=$(sha1sum $CLIENT_LOCATION/$CLIENT_JAR | cut -d ' ' -f1)
|
|
if [ $SHA1 != "$SHA1_FILE" ]; then
|
|
printf "Error verifying the sha1 for the Minecraft client software.\n"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Update the Minecraft server software.
|
|
#
|
|
# @param 1 The world server to update.
|
|
# ---------------------------------------------------------------------------
|
|
updateServerSoftware() {
|
|
local SERVER_JAR SERVER_LOCATION SERVER_URL SERVER_VERSION SHA1 SHA1_FILE
|
|
SERVER_JAR=$(getServerJar "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_JAR\n"
|
|
exit 1
|
|
fi
|
|
SERVER_LOCATION=$(getServerLocation "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_LOCATION\n"
|
|
exit 1
|
|
fi
|
|
SERVER_URL=$(getServerURL "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_URL\n"
|
|
exit 1
|
|
fi
|
|
SERVER_VERSION=$(getServerVersion "$1")
|
|
if [ $? -ne 0 ]; then
|
|
printf "$SERVER_VERSION\n"
|
|
exit 1
|
|
fi
|
|
# Make sure the server software directory exists.
|
|
mkdir -p "$SERVER_LOCATION"
|
|
# Download the server jar if it is missing.
|
|
if [ ! -s "$SERVER_LOCATION/$SERVER_JAR" ]; then
|
|
# Download the Minecraft server software.
|
|
$WGET -qO "$SERVER_LOCATION/$SERVER_JAR" "$SERVER_URL"
|
|
# Report any errors.
|
|
if [ $? -ne 0 ]; then
|
|
printf "Error updating the Minecraft server software.\n"
|
|
exit 1
|
|
fi
|
|
SHA1=$(getMinecraftVersionDownloadSHA1 $SERVER_VERSION 'server')
|
|
if [ -z $SHA1 ]; then
|
|
printf "Error retrieving the sha1 for the Minecraft server software.\n"
|
|
exit 1
|
|
fi
|
|
SHA1_FILE=$(sha1sum $SERVER_LOCATION/$SERVER_JAR | cut -d ' ' -f1)
|
|
if [ $SHA1 != "$SHA1_FILE" ]; then
|
|
printf "Error verifying the sha1 for the Minecraft server software.\n"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Run Minecraft Overviewer mapping software on the world. Generates an
|
|
# index.html file using the Google Maps API.
|
|
#
|
|
# @param 1 The world server to map with Overviewer.
|
|
# ---------------------------------------------------------------------------
|
|
overviewer() {
|
|
local SETTINGS_FILE LOG_FILE VERSION_TEST
|
|
SETTINGS_FILE="$WORLDS_LOCATION/$1/overviewer-settings.py"
|
|
LOG_FILE="$WORLDS_LOCATION/$1/logs/overviewer.log"
|
|
VERSION_TEST=$(compareMinecraftVersions $(getServerVersion $1) 1.7.2)
|
|
# Make sure this world has a server.properties file before mapping.
|
|
if [ ! -e "$WORLDS_LOCATION/$1/server.properties" ]; then
|
|
return
|
|
fi
|
|
# Make sure that the backup of the world files are there before mapping.
|
|
if [ ! -e "$BACKUP_LOCATION/$1/server.properties" ]; then
|
|
printf "\nError finding the backup for world $1. To save server "
|
|
printf "down time, mapping is run from the backup location.\n"
|
|
printf "Run '$PROG backup $1' before mapping.\n"
|
|
return
|
|
fi
|
|
# Make sure the maps directory exists.
|
|
mkdir -p "$MAPS_LOCATION/$1"
|
|
# Make sure the Minecraft client is available.
|
|
updateClientSoftware "$1"
|
|
# Create a default Overviewer settings file if it is missing.
|
|
if [ ! -e $SETTINGS_FILE ]; then
|
|
# Use the backup location so we minimize the time the server isn't saving data.
|
|
printf "import os\n\n" >$SETTINGS_FILE
|
|
|
|
printf "__world_name = os.environ['MSCS_WORLD_NAME']\n" >>$SETTINGS_FILE
|
|
printf "__world_path = '/'.join([\n" >>$SETTINGS_FILE
|
|
printf " os.environ['MSCS_BACKUP_LOCATION'], __world_name, __world_name])\n" >>$SETTINGS_FILE
|
|
printf "__world_path_original = __world_path + '-original'\n\n" >>$SETTINGS_FILE
|
|
|
|
printf "worlds[__world_name] = (\n" >>$SETTINGS_FILE
|
|
printf " __world_path_original\n" >>$SETTINGS_FILE
|
|
printf " if os.path.exists(__world_path_original)\n" >>$SETTINGS_FILE
|
|
printf " else __world_path)\n\n" >>$SETTINGS_FILE
|
|
|
|
printf "renders['overworld-render'] = {\n" >>$SETTINGS_FILE
|
|
printf " 'world': __world_name,\n" >>$SETTINGS_FILE
|
|
printf " 'title': 'Overworld',\n" >>$SETTINGS_FILE
|
|
printf " 'dimension': 'overworld',\n" >>$SETTINGS_FILE
|
|
printf " 'rendermode': 'normal'\n" >>$SETTINGS_FILE
|
|
printf "}\n\n" >>$SETTINGS_FILE
|
|
printf "renders['overworld-caves-render'] = {\n" >>$SETTINGS_FILE
|
|
printf " 'world': __world_name,\n" >>$SETTINGS_FILE
|
|
printf " 'title': 'Caves',\n" >>$SETTINGS_FILE
|
|
printf " 'dimension': 'overworld',\n" >>$SETTINGS_FILE
|
|
printf " 'rendermode': 'cave'\n" >>$SETTINGS_FILE
|
|
printf "}\n\n" >>$SETTINGS_FILE
|
|
printf "renders['nether-render'] = {\n" >>$SETTINGS_FILE
|
|
printf " 'world': __world_name,\n" >>$SETTINGS_FILE
|
|
printf " 'title': 'Nether',\n" >>$SETTINGS_FILE
|
|
printf " 'dimension': 'nether',\n" >>$SETTINGS_FILE
|
|
printf " 'rendermode': 'nether'\n" >>$SETTINGS_FILE
|
|
printf "}\n\n" >>$SETTINGS_FILE
|
|
printf "renders['end-render'] = {\n" >>$SETTINGS_FILE
|
|
printf " 'world': __world_name,\n" >>$SETTINGS_FILE
|
|
printf " 'title': 'End',\n" >>$SETTINGS_FILE
|
|
printf " 'dimension': 'end',\n" >>$SETTINGS_FILE
|
|
printf " 'rendermode': 'normal'\n" >>$SETTINGS_FILE
|
|
printf "}\n\n" >>$SETTINGS_FILE
|
|
printf "processes = 2\n" >>$SETTINGS_FILE
|
|
printf "outputdir = '/'.join([os.environ['MSCS_MAPS_LOCATION'], __world_name])\n" >>$SETTINGS_FILE
|
|
fi
|
|
# Announce the mapping of the world to players if the world is running.
|
|
if serverRunning $1; then
|
|
if [ $VERSION_TEST -ge 0 ]; then
|
|
sendCommand $1 'tellraw @a {
|
|
"text" : "",
|
|
"extra" : [
|
|
{
|
|
"text" : "[Server] The world is about to be mapped with "
|
|
},
|
|
{
|
|
"text" : "Overviewer",
|
|
"color" : "aqua",
|
|
"clickEvent" : {
|
|
"action" : "open_url",
|
|
"value" : "'$OVERVIEWER_URL'"
|
|
}
|
|
},
|
|
{
|
|
"text" : "."
|
|
}
|
|
]
|
|
}'
|
|
else
|
|
sendCommand $1 "say The world is about to be mapped with Overviewer."
|
|
fi
|
|
fi
|
|
# ensure these variables are visible inside python/overview-settings.py
|
|
export MSCS_BACKUP_LOCATION="$BACKUP_LOCATION"
|
|
export MSCS_MAPS_LOCATION="$MAPS_LOCATION"
|
|
export MSCS_WORLD_NAME="$1"
|
|
# Generate the map and POI.
|
|
$OVERVIEWER_BIN --config=$SETTINGS_FILE >>$LOG_FILE 2>&1
|
|
$OVERVIEWER_BIN --config=$SETTINGS_FILE --genpoi >>$LOG_FILE 2>&1
|
|
# Announce the location to access the world map to players.
|
|
if serverRunning $1; then
|
|
if [ $VERSION_TEST -ge 0 ]; then
|
|
sendCommand $1 'tellraw @a {
|
|
"text" : "",
|
|
"extra" : [
|
|
{
|
|
"text" : "[Server] Mapping is complete. You can access the maps "
|
|
},
|
|
{
|
|
"text" : "here",
|
|
"color" : "aqua",
|
|
"clickEvent" : {
|
|
"action" : "open_url",
|
|
"value" : "'$MAPS_URL'/'$1'"
|
|
}
|
|
},
|
|
{
|
|
"text" : "."
|
|
}
|
|
]
|
|
}'
|
|
else
|
|
sendCommand $1 "say Mapping is complete. You can access the maps at:"
|
|
sendCommand $1 "say $MAPS_URL/$1"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Start a Query handler for a world server.
|
|
#
|
|
# @param 1 The world server to start a Query handler for.
|
|
# ---------------------------------------------------------------------------
|
|
queryStart() {
|
|
local WORLD_DIR SERVER_IP SERVER_PID QUERY_PORT
|
|
# Grab the location of the world's directory.
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
# Determine the IP address and port used by the query server.
|
|
SERVER_IP=$(getServerPropertiesValue $1 'server-ip' '127.0.0.1')
|
|
QUERY_PORT=$(getServerPropertiesValue $1 'query.port')
|
|
SERVER_PID=$(getJavaPID $1)
|
|
# Delete any old query.in or query.out buffer files.
|
|
rm -Rf "$WORLD_DIR/query.in" "$WORLD_DIR/query.out"
|
|
# Start a tail process to watch for changes to the query.in file to pipe
|
|
# to the Minecraft query server via socat. The response from the query
|
|
# server is piped into the query.out file.
|
|
# Wait until the query port is open before creating the query files
|
|
# and establishing the tail process.
|
|
nohup sh -c "
|
|
while ! echo | $SOCAT - UDP-CONNECT:$SERVER_IP:$QUERY_PORT >/dev/null 2>&1; do
|
|
sleep 0.1
|
|
done;
|
|
printf '' >\"$WORLD_DIR/query.in\"
|
|
printf '' >\"$WORLD_DIR/query.out\"
|
|
tail -f --pid=$SERVER_PID \"$WORLD_DIR/query.in\" |
|
|
$SOCAT - UDP:$SERVER_IP:$QUERY_PORT > \"$WORLD_DIR/query.out\"
|
|
" >/dev/null 2>&1 &
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Pack a hex string into a buffer file that is piped to the Minecraft query
|
|
# server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @param 2 The packet type.
|
|
# @param 3 The packet payload.
|
|
# @param 4 The response format.
|
|
# @return The response from the Query server in the requested format.
|
|
# ---------------------------------------------------------------------------
|
|
querySendPacket() {
|
|
local ID PACKET RESPONSE WORLD_DIR
|
|
# The packet identifier.
|
|
ID="00000001"
|
|
# The world's directory.
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
# Make sure the query.in and query.out buffer files exist before
|
|
# preparing and sending the packet to the Query server.
|
|
if [ -e "$WORLD_DIR/query.in" ] && [ -e "$WORLD_DIR/query.out" ]; then
|
|
# Add the magic bytes and create the hex string packet.
|
|
PACKET=$(printf "FEFD%s%s%s" "$2" "$ID" "$3")
|
|
# Remove any old responses from the query.out buffer file.
|
|
printf '' >"$WORLD_DIR/query.out"
|
|
# Pack the hex string packet and write it to the query.in buffer file.
|
|
$PERL -se '
|
|
print map { pack ("C", hex($_)) } ($packet =~ /(..)/g);
|
|
' -- -packet="$PACKET" >>"$WORLD_DIR/query.in"
|
|
# Give the Query server a moment to respond.
|
|
timeout 1 sh -c "while [ ! -s '$WORLD_DIR/query.out' ]; do :; done"
|
|
# Unpack the response packet from the query.out buffer file. There are a
|
|
# variable amount of null bytes at the start of the response packet, so
|
|
# find the start of the response by searching for the packet type and ID.
|
|
RESPONSE=$(cat $WORLD_DIR/query.out | $PERL -0777 -sne '
|
|
foreach (unpack "C*", $_) {
|
|
$char = sprintf ("%.2x", $_);
|
|
$char =~ s/0a/5c6e/;
|
|
$hex .= $char;
|
|
}
|
|
$hex =~ s/^0*$type$id/$type$id/;
|
|
print $hex;
|
|
' -- -type="$2" -id="$ID")
|
|
fi
|
|
# If the response is not empty, return the response
|
|
# in the format requested.
|
|
if [ -n "$RESPONSE" ]; then
|
|
# Return the response in the format requested.
|
|
$PERL -se '
|
|
$packed = join "", map { pack ("C", hex($_)) } ($response =~ /(..)/g);
|
|
printf "%s\n", join "\t", unpack ($format, $packed);
|
|
' -- -response="$RESPONSE" -format="$4"
|
|
|
|
# If the response is empty and the query.in and query.out files have NOT been created,
|
|
# the world is still starting up and the status query will return as such.
|
|
# See lines 1985-1987.
|
|
|
|
# If the response is empty and the query.in and query.out files HAVE been created,
|
|
# something went wrong. We try restarting the query handler again.
|
|
elif [ -z "$RESPONSE" ] && [ -e "$WORLD_DIR/query.in" ] && [ -e "$WORLD_DIR/query.out" ]; then
|
|
ps a | grep socat | grep "$1" | grep tail >/dev/null || queryStart "$1"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Send a challenge packet to the Minecraft query server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return Tab separated values:
|
|
# type - The packet type.
|
|
# id - The packet identifier.
|
|
# token - The token.
|
|
# ---------------------------------------------------------------------------
|
|
querySendChallengePacket() {
|
|
local PACKET
|
|
# Use an empty packet.
|
|
PACKET="00000000"
|
|
# Send the challenge packet to the Minecraft query server.
|
|
querySendPacket "$1" "09" "$PACKET" "Cl>Z*"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Send a status query to the Minecraft query server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return Tab separated values:
|
|
# type - The packet type.
|
|
# id - The packet identifier.
|
|
# MOTD - The world's message of the day.
|
|
# gametype - The world's game type.
|
|
# map - The name of the world.
|
|
# numplayers - The current number of players.
|
|
# maxplayers - The maximum number of players.
|
|
# hostport - The host's port
|
|
# hostip - The host's IP address.
|
|
# ---------------------------------------------------------------------------
|
|
queryStatus() {
|
|
local PACKET TOKEN
|
|
if true_value "$(getServerPropertiesValue $1 'enable-query')"; then
|
|
# Send a challenge packet to the Minecraft query server.
|
|
TOKEN=$(querySendChallengePacket $1 | cut -f 3)
|
|
if [ -n "$TOKEN" ]; then
|
|
# Use the challenge token for the packet.
|
|
PACKET=$(printf "%08x" $TOKEN)
|
|
# Send the information request packet to the Minecraft query server.
|
|
querySendPacket "$1" "00" "$PACKET" "Cl>Z*Z*Z*Z*Z*s<Z*"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Send a detailed status query to the Minecraft query server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return Tab separated values:
|
|
# type - The packet type.
|
|
# id - The packet identifier.
|
|
# * - The string 'splitnum'.
|
|
# * - The value 128.
|
|
# * - The value 0.
|
|
# * - The string 'hostname'.
|
|
# MOTD - The world's message of the day.
|
|
# * - The string 'gametype'.
|
|
# gametype - The world's game type, hardcoded to 'SMP'.
|
|
# * - The string 'game_id'.
|
|
# gameid - The world's game ID, hardcoded to 'MINECRAFT'.
|
|
# * - The string 'version'.
|
|
# version - The world's Minecraft version.
|
|
# * - The string 'plugins'.
|
|
# plugins - The world's plugins.
|
|
# * - The string 'map'.
|
|
# map - The world's name.
|
|
# * - The string 'numplayers'.
|
|
# numplayers - The world's current number of players.
|
|
# * - The string 'maxplayers'.
|
|
# maxplayers - The world's maximum number of players.
|
|
# * - The string 'hostport'.
|
|
# hostport - The world's host port.
|
|
# * - The string 'hostip'.
|
|
# hostip - The world's host IP address.
|
|
# * - The value 0.
|
|
# * - The value 1.
|
|
# * - The string 'player_'.
|
|
# * - The value 0.
|
|
# players - The players currently logged onto the world.
|
|
# ---------------------------------------------------------------------------
|
|
queryDetailedStatus() {
|
|
local CHALLENGE ID PACKET TOKEN
|
|
if true_value "$(getServerPropertiesValue $1 'enable-query')"; then
|
|
# Send a challenge packet to the Minecraft query server.
|
|
CHALLENGE=$(querySendChallengePacket $1)
|
|
ID=$(echo "$CHALLENGE" | cut -f 2)
|
|
TOKEN=$(echo "$CHALLENGE" | cut -f 3)
|
|
if [ -n "$ID" ] && [ -n "$TOKEN" ]; then
|
|
# Use the challenge token for the packet, with the ID on the end.
|
|
PACKET=$(printf "%08x%08x" $TOKEN $ID)
|
|
# Send the information request packet to the Minecraft query server.
|
|
querySendPacket "$1" "00" "$PACKET" \
|
|
"Cl>Z*CCZ*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*Z*CCZ*C(Z*)*"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Send a detailed status query to the Minecraft query server and return the
|
|
# data in JSON format.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return Query values in JSON format.
|
|
# ---------------------------------------------------------------------------
|
|
queryDetailedStatusJSON() {
|
|
local STATUS JSON MOTD GAMETYPE GAMEID VERSION PLUGINS MAP NUMPLAYERS MAXPLAYERS HOSTPORT HOSTIP PLAYERS COUNTER
|
|
STATUS=$(queryDetailedStatus $1)
|
|
if [ -n "$STATUS" ]; then
|
|
MOTD=$(printf "%s" "$STATUS" | cut -f 7)
|
|
GAMETYPE=$(printf "%s" "$STATUS" | cut -f 9)
|
|
GAMEID=$(printf "%s" "$STATUS" | cut -f 11)
|
|
VERSION=$(printf "%s" "$STATUS" | cut -f 13)
|
|
PLUGINS=$(printf "%s" "$STATUS" | cut -f 15)
|
|
MAP=$(printf "%s" "$STATUS" | cut -f 17)
|
|
NUMPLAYERS=$(printf "%s" "$STATUS" | cut -f 19)
|
|
MAXPLAYERS=$(printf "%s" "$STATUS" | cut -f 21)
|
|
HOSTPORT=$(printf "%s" "$STATUS" | cut -f 23)
|
|
HOSTIP=$(printf "%s" "$STATUS" | cut -f 25)
|
|
if [ $NUMPLAYERS -gt 0 ]; then
|
|
PLAYERS=$(printf "%s\"" "$PLAYERS" $(printf "%s" "$STATUS" | cut -f $((30))))
|
|
COUNTER=1
|
|
while [ $COUNTER -lt $NUMPLAYERS ]; do
|
|
PLAYERS=$(printf "%s, \"%s\"" "$PLAYERS" $(printf "%s" "$STATUS" | cut -f $((30 + $COUNTER))))
|
|
COUNTER=$(($COUNTER + 1))
|
|
done
|
|
fi
|
|
JSON="{\"motd\":\"$MOTD\",\"gametype\":\"$GAMETYPE\",\"gameid\":\"$GAMEID\",\"version\":\"$VERSION\",\"plugins\":\"$PLUGINS\",\"map\":\"$MAP\",\"numplayers\":$NUMPLAYERS,\"maxplayers\":$MAXPLAYERS,\"hostport\":\"$HOSTPORT\",\"hostip\":\"$HOSTIP\",\"players\":[$PLAYERS]}"
|
|
else
|
|
JSON="{}"
|
|
fi
|
|
printf "%s" "$JSON"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Query the number of active users from the query server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return Number of active users.
|
|
# ---------------------------------------------------------------------------
|
|
queryNumUsers() {
|
|
local NUM_USERS
|
|
NUM_USERS=$(queryStatus $1 | cut -f6)
|
|
# Return 0 users if query server not available.
|
|
[ -z "$NUM_USERS" ] && NUM_USERS=0
|
|
printf "$NUM_USERS"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Display the status of a Minecraft world server.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# ---------------------------------------------------------------------------
|
|
worldStatus() {
|
|
local WORLD_DIR LAST_START_STATUS_LOG MONITOR_PID STATUS NUM MAX PLAYERS COUNTER VERSION
|
|
WORLD_DIR="$WORLDS_LOCATION/$1"
|
|
MONITOR_PID="$WORLD_DIR/monitor.pid"
|
|
LAST_START_STATUS_LOG="$WORLD_DIR/logs/last-start-status.log"
|
|
if serverRunning $1; then
|
|
STATUS=$(queryDetailedStatus $1)
|
|
if [ -n "$STATUS" ]; then
|
|
NUM=$(printf "%s" "$STATUS" | cut -f 19)
|
|
MAX=$(printf "%s" "$STATUS" | cut -f 21)
|
|
VERSION=$(printf "%s" "$STATUS" | cut -f 13)
|
|
printf "running version %s (%d of %d users online).\n" "$VERSION" $NUM $MAX
|
|
printf " Port: %d.\n" $(getServerPropertiesValue "$1" "server-port" "$DEFAULT_PORT")
|
|
if [ $NUM -gt 0 ]; then
|
|
PLAYERS=$(printf "%s" $(printf "%s" "$STATUS" | cut -f 30))
|
|
COUNTER=1
|
|
while [ $COUNTER -lt $NUM ]; do
|
|
PLAYERS=$(printf "%s, %s" "$PLAYERS" $(printf "%s" "$STATUS" | cut -f $((30 + $COUNTER))))
|
|
COUNTER=$(($COUNTER + 1))
|
|
done
|
|
printf " Players: %s.\n" "$PLAYERS"
|
|
fi
|
|
elif true_value "$(getServerPropertiesValue $1 'enable-query')" &&
|
|
[ -e "$WORLD_DIR/query.in" ] && [ -e "$WORLD_DIR/query.out" ]; then
|
|
printf "running (query server offline).\n"
|
|
elif true_value "$(getServerPropertiesValue $1 'enable-query')" &&
|
|
[ ! -e "$WORLD_DIR/query.in" ] && [ ! -e "$WORLD_DIR/query.out" ]; then
|
|
printf "world starting up.\n"
|
|
else
|
|
printf "running.\n"
|
|
fi
|
|
printf " Memory used: $(getJavaMemory "$1" | awk '{$1=int(100 * $1/1024/1024)/100"GB";}{ print;}')"
|
|
printf " ($(getMSCSValue "$1" "mscs-maximum-memory" "$DEFAULT_MAXIMUM_MEMORY" | rev | cut -c 2- | rev | awk '{$1=int($1/1024)"GB";}{ print;}') allocated).\n"
|
|
printf " Process ID: %d.\n" $(getJavaPID "$1")
|
|
# Display crash monitor PID if it's running (i.e. monitor.pid file exists and not empty).
|
|
if [ -f "$MONITOR_PID" ] && [ -s "$MONITOR_PID" ]; then
|
|
printf " Crash Monitor PID: $(cat $MONITOR_PID)\n"
|
|
fi
|
|
# If the last-status log exists and not empty, then last restart was from a crash.
|
|
# Display notice once.
|
|
if [ -f "$LAST_START_STATUS_LOG" ] && [ -s "$LAST_START_STATUS_LOG" ]; then
|
|
printf "$(cat $LAST_START_STATUS_LOG)\n"
|
|
# Remove it so user doesn't see it next time they run the status command.
|
|
rm -f $LAST_START_STATUS_LOG
|
|
fi
|
|
elif ! true_value "$(getMSCSValue $1 'mscs-enabled')"; then
|
|
printf "disabled.\n"
|
|
else
|
|
printf "not running.\n"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Display the status of a Minecraft world server in JSON format.
|
|
#
|
|
# @param 1 The world server of interest.
|
|
# @return The status of the world in JSON format.
|
|
# ---------------------------------------------------------------------------
|
|
worldStatusJSON() {
|
|
local RUNNING QUERY PID MEMORY
|
|
if serverRunning $1; then
|
|
RUNNING="true"
|
|
QUERY="$(queryDetailedStatusJSON $1)"
|
|
PID="$(getJavaPID $1)"
|
|
MEMORY="$(getJavaMemory $1)"
|
|
else
|
|
RUNNING="false"
|
|
QUERY="null"
|
|
PID="null"
|
|
MEMORY="null"
|
|
fi
|
|
printf "{\"running\":%s,\"query\":%s,\"pid\":%s,\"memory\":%s}" "$RUNNING" "$QUERY" "$PID" "$MEMORY"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Begin.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Make sure that Java, Perl, libjson-perl, libwww-perl, Python, Wget,
|
|
# Rdiff-backup, Rsync, Socat and flock are installed.
|
|
# ---------------------------------------------------------------------------
|
|
if [ ! -e "$JAVA" ]; then
|
|
echo "ERROR: Java not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install default-jre"
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$PERL" ]; then
|
|
echo "ERROR: Perl not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install perl"
|
|
exit 1
|
|
fi
|
|
$PERL -e 'use JSON;' >/dev/null 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
echo "ERROR: libjson-perl not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install libjson-perl"
|
|
exit 1
|
|
fi
|
|
$PERL -e 'use LWP::Simple;' >/dev/null 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
echo "ERROR: libwww-perl not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install libwww-perl"
|
|
exit 1
|
|
fi
|
|
$PERL -e 'use LWP::Protocol::https;' >/dev/null 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
echo "ERROR: liblwp-protocol-https-perl not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install liblwp-protocol-https-perl"
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$PYTHON" ]; then
|
|
echo "ERROR: Python not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install python3"
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$WGET" ]; then
|
|
echo "ERROR: GNU Wget not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install wget"
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$RDIFF_BACKUP" ]; then
|
|
echo "ERROR: rdiff-backup not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install rdiff-backup"
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$RSYNC" ]; then
|
|
echo "ERROR: rsync not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install rsync"
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$SOCAT" ]; then
|
|
echo "ERROR: socat not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install socat"
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$FLOCK" ]; then
|
|
echo "ERROR: flock not found!"
|
|
echo "Try installing this with:"
|
|
echo "sudo apt-get install util-linux"
|
|
exit 1
|
|
fi
|
|
|
|
# Parse command-line options
|
|
# ---------------------------------------------------------------------------
|
|
while [ "$1" != "${1#-}" ]; do
|
|
case "$1" in
|
|
-p)
|
|
if [ -n "$2" ]; then
|
|
PROG="$2"
|
|
shift
|
|
else
|
|
echo "$PROG: '-p' needs a program name argument."
|
|
exit 1
|
|
fi
|
|
;;
|
|
-c)
|
|
if [ -n "$2" ]; then
|
|
MSCS_DEFAULTS_CL="$2"
|
|
shift
|
|
else
|
|
echo "$PROG: '-c' needs a config file argument."
|
|
exit 1
|
|
fi
|
|
;;
|
|
-l)
|
|
if [ -n "$2" ]; then
|
|
LOCATION_CL="$2"
|
|
shift
|
|
else
|
|
echo "$PROG: '-l' needs a location path argument."
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "$PROG: unknown option '$1'."
|
|
echo "Execute '$PROG' without arguments for usage instructions."
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# Override Default Values
|
|
# ---------------------------------------------------------------------------
|
|
# Override the default values by adding them to one of the following files. If
|
|
# none of the files exist, the default values in the script will be used (see
|
|
# below) and an example file will be created at `$HOME/mscs.defaults`.
|
|
# Possible files are checked in the following order:
|
|
# command line option "-c".
|
|
# $HOME/mscs.defaults
|
|
# $HOME/.config/mscs/mscs.defaults
|
|
if [ -n "$MSCS_DEFAULTS_CL" ]; then
|
|
MSCS_DEFAULTS="$MSCS_DEFAULTS_CL"
|
|
elif [ -r "$HOME/mscs.defaults" ]; then
|
|
MSCS_DEFAULTS="$HOME/mscs.defaults"
|
|
elif [ -r "$HOME/.config/mscs/mscs.defaults" ]; then
|
|
MSCS_DEFAULTS="$HOME/.config/mscs/mscs.defaults"
|
|
else
|
|
MSCS_DEFAULTS="$HOME/mscs.defaults"
|
|
fi
|
|
# Generate the example `mscs.defaults` if it is missing or empty.
|
|
if [ ! -s "$MSCS_DEFAULTS" ]; then
|
|
mscs_defaults >$MSCS_DEFAULTS
|
|
fi
|
|
# Default values in the script can be overridden by adding certain key/value
|
|
# pairs to one of the mscs.defaults files mentioned above. Default values in
|
|
# the script will be used unless overridden in one these files.
|
|
#
|
|
# The following keys are available:
|
|
# mscs-location - Location of the mscs files.
|
|
# mscs-worlds-location - Location of world files.
|
|
# mscs-versions-url - URL to download the version_manifest.json file.
|
|
# mscs-versions-json - Location of the version_manifest.json file.
|
|
# mscs-versions-duration - Duration (in minutes) to keep the version_manifest.json file before updating.
|
|
# mscs-lockfile-duration - Duration (in minutes) to keep lock files before removing.
|
|
# mscs-default-world - Default world name.
|
|
# mscs-default-port - Default Port.
|
|
# mscs-default-ip - Default IP address.
|
|
# mscs-default-version-type - Default version type (release or snapshot).
|
|
# mscs-default-client-version - Default version of the client software.
|
|
# mscs-default-client-jar - Default .jar file for the client software.
|
|
# mscs-default-client-url - Default download URL for the client software.
|
|
# mscs-default-client-location - Default location of the client .jar file.
|
|
# mscs-default-server-version - Default version of the server software.
|
|
# mscs-default-jvm-args - Default arguments for the JVM.
|
|
# mscs-default-server-jar - Default .jar file for the server software.
|
|
# mscs-default-server-url - Default download URL for the server software.
|
|
# mscs-default-server-args - Default arguments for a world server.
|
|
# mscs-default-initial-memory - Default initial amount of memory for a world server.
|
|
# mscs-default-maximum-memory - Default maximum amount of memory for a world server.
|
|
# mscs-default-server-location - Default location of the server .jar file.
|
|
# mscs-default-server-command - Default command to run for a world server.
|
|
# mscs-default-restart-after-crash - Default behavior if to restart the server after crash is detected (default disabled).
|
|
# mscs-backup-location - Location to store backup files.
|
|
# mscs-backup-log - Location of the backup log file.
|
|
# mscs-backup-excluded-files - Comma separated list of files and directories excluded from backups.
|
|
# mscs-backup-duration - Length in days that backups survive.
|
|
# mscs-log-duration - Length in days that logs survive.
|
|
# mscs-detailed-listing - Properties to return for detailed listings.
|
|
# mscs-enable-mirror - Enable the mirror option by default for worlds (default disabled).
|
|
# mscs-mirror-path - Default path for the mirror files.
|
|
# mscs-overviewer-bin - Location of Overviewer.
|
|
# mscs-overviewer-url - URL for Overviewer.
|
|
# mscs-maps-location - Location of Overviewer generated map files.
|
|
# mscs-maps-url - URL for accessing Overviewer generated maps.
|
|
#
|
|
# The following variables may be used in some of the key values:
|
|
# $JAVA - The Java virtual machine.
|
|
# $CURRENT_VERSION - The current Mojang Minecraft release version.
|
|
# $CLIENT_VERSION - The version of the client software.
|
|
# $SERVER_VERSION - The version of the server software.
|
|
# $JVM_ARGS - The arguments to the JVM.
|
|
# $SERVER_JAR - The .jar file to run for the server.
|
|
# $SERVER_ARGS - The arguments to the server.
|
|
# $INITIAL_MEMORY - The initial amount of memory for the server.
|
|
# $MAXIMUM_MEMORY - The maximum amount of memory for the server.
|
|
# $SERVER_LOCATION - The location of the server .jar file.
|
|
# $WORLD_NAME - The name of the world.
|
|
#
|
|
# The following example key/value pairs are equivalent to the default values:
|
|
# mscs-location=/opt/mscs
|
|
# mscs-worlds-location=/opt/mscs/worlds
|
|
# mscs-versions-url=https://launchermeta.mojang.com/mc/game/version_manifest.json
|
|
# mscs-versions-json=/opt/mscs/version_manifest.json
|
|
# mscs-versions-duration=30
|
|
# mscs-lockfile-duration=1440
|
|
# mscs-default-world=world
|
|
# mscs-default-port=25565
|
|
# mscs-default-ip=
|
|
# mscs-default-version-type=release
|
|
# mscs-default-client-version=$CURRENT_VERSION
|
|
# mscs-default-client-jar=$CLIENT_VERSION.jar
|
|
# mscs-default-client-url=
|
|
# mscs-default-client-location=/opt/mscs/.minecraft/versions/$CLIENT_VERSION
|
|
# mscs-default-server-version=$CURRENT_VERSION
|
|
# mscs-default-jvm-args=
|
|
# mscs-default-server-jar=minecraft_server.$SERVER_VERSION.jar
|
|
# mscs-default-server-url=
|
|
# mscs-default-server-args=nogui
|
|
# mscs-default-initial-memory=128M
|
|
# mscs-default-maximum-memory=2048M
|
|
# mscs-default-server-location=/opt/mscs/server
|
|
# mscs-default-server-command=$JAVA -Xms$INITIAL_MEMORY -Xmx$MAXIMUM_MEMORY $JVM_ARGS -jar $SERVER_LOCATION/$SERVER_JAR $SERVER_ARGS
|
|
# mscs-default-restart-after-crash=false
|
|
# mscs-backup-location=/opt/mscs/backups
|
|
# mscs-backup-log=/opt/mscs/backups/backup.log
|
|
# mscs-backup-excluded_files=
|
|
# mscs-backup-duration=15
|
|
# mscs-log-duration=15
|
|
# mscs-detailed-listing=motd server-ip server-port max-players level-type online-mode
|
|
# mscs-enable-mirror=0
|
|
# mscs-mirror-path=/dev/shm/mscs
|
|
# mscs-overviewer-bin=/usr/bin/overviewer.py
|
|
# mscs-overviewer-url=http://overviewer.org
|
|
# mscs-maps-location=/opt/mscs/maps
|
|
# mscs-maps-url=http://minecraft.server.com/maps
|
|
|
|
# Server Location
|
|
# ---------------------------------------------------------------------------
|
|
# The default location of server software and data.
|
|
LOCATION=$(getDefaultsValue 'mscs-location' $HOME'/mscs')
|
|
# Override with command-line location option.
|
|
[ -n "$LOCATION_CL" ] && LOCATION="$LOCATION_CL"
|
|
|
|
# Global Server Configuration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Minecraft Versions information
|
|
# ---------------------------------------------------------------------------
|
|
MINECRAFT_VERSIONS_URL=$(getDefaultsValue 'mscs-versions-url' 'https://launchermeta.mojang.com/mc/game/version_manifest.json')
|
|
|
|
# Minecraft Server Settings
|
|
# ---------------------------------------------------------------------------
|
|
# Settings used if not provided in the world's mscs.properties file.
|
|
DEFAULT_WORLD=$(getDefaultsValue 'mscs-default-world' 'world')
|
|
DEFAULT_PORT=$(getDefaultsValue 'mscs-default-port' '25565')
|
|
DEFAULT_IP=$(getDefaultsValue 'mscs-default-ip' '')
|
|
DEFAULT_VERSION_TYPE=$(getDefaultsValue 'mscs-default-version-type' 'release')
|
|
DEFAULT_CLIENT_VERSION=$(getDefaultsValue 'mscs-default-client-version' '$CURRENT_VERSION')
|
|
DEFAULT_CLIENT_JAR=$(getDefaultsValue 'mscs-default-client-jar' '$CLIENT_VERSION.jar')
|
|
DEFAULT_CLIENT_URL=$(getDefaultsValue 'mscs-default-client-url' '')
|
|
DEFAULT_CLIENT_LOCATION=$(getDefaultsValue 'mscs-default-client-location' $HOME'/.minecraft/versions/$CLIENT_VERSION')
|
|
DEFAULT_SERVER_VERSION=$(getDefaultsValue 'mscs-default-server-version' '$CURRENT_VERSION')
|
|
DEFAULT_JVM_ARGS=$(getDefaultsValue 'mscs-default-jvm-args' '')
|
|
DEFAULT_SERVER_JAR=$(getDefaultsValue 'mscs-default-server-jar' 'minecraft_server.$SERVER_VERSION.jar')
|
|
DEFAULT_SERVER_URL=$(getDefaultsValue 'mscs-default-server-url' '')
|
|
DEFAULT_SERVER_ARGS=$(getDefaultsValue 'mscs-default-server-args' 'nogui')
|
|
DEFAULT_INITIAL_MEMORY=$(getDefaultsValue 'mscs-default-initial-memory' '128M')
|
|
DEFAULT_MAXIMUM_MEMORY=$(getDefaultsValue 'mscs-default-maximum-memory' '2048M')
|
|
DEFAULT_SERVER_LOCATION=$(getDefaultsValue 'mscs-default-server-location' $LOCATION'/server')
|
|
DEFAULT_SERVER_COMMAND=$(getDefaultsValue 'mscs-default-server-command' '$JAVA -Xms$INITIAL_MEMORY -Xmx$MAXIMUM_MEMORY $JVM_ARGS -jar $SERVER_LOCATION/$SERVER_JAR $SERVER_ARGS')
|
|
DEFAULT_RESTART_AFTER_CRASH=$(getDefaultsValue 'mscs-default-restart-after-crash' 'false')
|
|
# Each world server can override the default values in a similar manner by
|
|
# adding certain key/value pairs to the world's mscs.properties file.
|
|
#
|
|
# The following keys are available:
|
|
# mscs-enabled - Enable or disable the world server.
|
|
# mscs-version-type - Assign the version type (release or snapshot).
|
|
# mscs-client-version - Assign the version of the client software.
|
|
# mscs-client-jar - Assign the .jar file for the client software.
|
|
# mscs-client-url - Assign the download URL for the client software.
|
|
# mscs-client-location - Assign the location of the client .jar file.
|
|
# mscs-server-version - Assign the version of the server software.
|
|
# mscs-jvm-args - Assign the arguments to the JVM.
|
|
# mscs-server-jar - Assign the .jar file for the server software.
|
|
# mscs-server-url - Assign the download URL for the server software.
|
|
# mscs-server-args - Assign the arguments to the server.
|
|
# mscs-initial-memory - Assign the initial amount of memory for the server.
|
|
# mscs-maximum-memory - Assign the maximum amount of memory for the server.
|
|
# mscs-server-location - Assign the location of the server .jar file.
|
|
# mscs-server-command - Assign the command to run for the server.
|
|
# mscs-restart-after-crash - Restart the server after a crash (default disabled).
|
|
#
|
|
# Like above, the following variables may be used in some of the key values:
|
|
# $JAVA - The Java virtual machine.
|
|
# $CURRENT_VERSION - The current Mojang Minecraft release version.
|
|
# $CLIENT_VERSION - The version of the client software.
|
|
# $SERVER_VERSION - The version of the server software.
|
|
# $JVM_ARGS - The arguments to the JVM.
|
|
# $SERVER_JAR - The .jar file to run for the server.
|
|
# $SERVER_ARGS - The arguments to the server.
|
|
# $INITIAL_MEMORY - The initial amount of memory for the server.
|
|
# $MAXIMUM_MEMORY - The maximum amount of memory for the server.
|
|
# $SERVER_LOCATION - The location of the server .jar file.
|
|
#
|
|
# The following example key/value pairs are equivalent to the default values:
|
|
# mscs-enabled=true
|
|
# mscs-version-type=release
|
|
# mscs-client-version=$CURRENT_VERSION
|
|
# mscs-client-jar=$CLIENT_VERSION.jar
|
|
# mscs-client-url=
|
|
# mscs-client-location=/opt/mscs/.minecraft/versions/$CLIENT_VERSION
|
|
# mscs-server-version=$CURRENT_VERSION
|
|
# mscs-jvm-args=
|
|
# mscs-server-jar=minecraft_server.$SERVER_VERSION.jar
|
|
# mscs-server-url=
|
|
# mscs-server-args=nogui
|
|
# mscs-initial-memory=128M
|
|
# mscs-maximum-memory=2048M
|
|
# mscs-server-location=/opt/mscs/server
|
|
# mscs-server-command=$JAVA -Xms$INITIAL_MEMORY -Xmx$MAXIMUM_MEMORY $JVM_ARGS -jar $SERVER_LOCATION/$SERVER_JAR $SERVER_ARGS
|
|
# mscs-restart-after-crash=false
|
|
|
|
# World (Server Instance) Configuration
|
|
# ---------------------------------------------------------------------------
|
|
# The location to store files for each world server.
|
|
WORLDS_LOCATION=$(getDefaultsValue 'mscs-worlds-location' $LOCATION'/worlds')
|
|
# The location to store the version_manifest.json file.
|
|
VERSIONS_JSON=$(getDefaultsValue 'mscs-versions-json' $LOCATION'/version_manifest.json')
|
|
# The duration (in minutes) to keep the version_manifest.json file before updating.
|
|
VERSIONS_DURATION=$(getDefaultsValue 'mscs-versions-duration' '30')
|
|
# The duration (in minutes) to keep lock files before removing.
|
|
LOCKFILE_DURATION=$(getDefaultsValue 'mscs-lockfile-duration' '1440')
|
|
# Enable the option to restart the server after a crash is detected (default disabled).
|
|
RESTART_AFTER_CRASH=$(getDefaultsValue 'mscs-restart-after-crash' 'false')
|
|
|
|
# Backup Configuration
|
|
# ---------------------------------------------------------------------------
|
|
# Location to store backups.
|
|
BACKUP_LOCATION=$(getDefaultsValue 'mscs-backup-location' $LOCATION'/backups')
|
|
# Location of the backup log file.
|
|
BACKUP_LOG=$(getDefaultsValue 'mscs-backup-log' $BACKUP_LOCATION'/backup.log')
|
|
# Comma separated list of files and directories excluded from backups.
|
|
BACKUP_EXCLUDED_FILES=$(getDefaultsValue 'mscs-backup-excluded-files' '')
|
|
# Length in days that backups survive.
|
|
BACKUP_DURATION=$(getDefaultsValue 'mscs-backup-duration' '15')
|
|
|
|
# Server Log Configuration
|
|
# ---------------------------------------------------------------------------
|
|
# Length in days that logs survive.
|
|
LOG_DURATION=$(getDefaultsValue 'mscs-log-duration' '15')
|
|
|
|
# Listing options
|
|
# ---------------------------------------------------------------------------
|
|
# Server properties for detailed listing (list).
|
|
DETAILED_LISTING_PROPERTIES=$(getDefaultsValue 'mscs-detailed-listing' 'motd server-ip server-port max-players level-type online-mode')
|
|
|
|
# Mirror Image Options
|
|
# ---------------------------------------------------------------------------
|
|
# Create a mirror image of the world data on system startup, and
|
|
# update that mirror image on system shutdown.
|
|
#
|
|
# IMPORTANT: If using this option, the admin should schedule
|
|
# periodic synchronizations of the mirror image using cron
|
|
# to avoid data loss. To do this, add a cron task to call
|
|
# the "sync" option on a VERY regular basis (e.g.,
|
|
# every 5-10 minutes).
|
|
#
|
|
# 0 - Do not use a mirror image, default.
|
|
# 1 - Use a mirror image.
|
|
ENABLE_MIRROR=$(getDefaultsValue 'mscs-enable-mirror' '0')
|
|
# The location to store the mirror image.
|
|
#
|
|
# NOTE: This is usually a ramdisk, e.g. /dev/shm on Debian/Ubuntu.
|
|
MIRROR_PATH=$(getDefaultsValue 'mscs-mirror-path' '/dev/shm/mscs')
|
|
|
|
# Minecraft Overviewer Mapping Software Options
|
|
# ---------------------------------------------------------------------------
|
|
OVERVIEWER_BIN=$(getDefaultsValue 'mscs-overviewer-bin' $(which overviewer.py))
|
|
OVERVIEWER_URL=$(getDefaultsValue 'mscs-overviewer-url' 'http://overviewer.org')
|
|
MAPS_LOCATION=$(getDefaultsValue 'mscs-maps-location' $LOCATION'/maps')
|
|
MAPS_URL=$(getDefaultsValue 'mscs-maps-url' 'http://minecraft.server.com/maps')
|
|
|
|
# Allow function importing via source command for tests.
|
|
if printf $0 | grep -qs "runtests\.sh"; then
|
|
# File was sourced, don't execute below here.
|
|
# Return a true value (success).
|
|
return 0
|
|
fi
|
|
|
|
# Respond to the command line arguments.
|
|
# ---------------------------------------------------------------------------
|
|
# Stores the user-issued command.
|
|
COMMAND=$1
|
|
if [ "$#" -ge 1 ]; then
|
|
shift 1
|
|
fi
|
|
case "$COMMAND" in
|
|
start)
|
|
# Grab the latest version information.
|
|
updateVersionsJSON
|
|
# Figure out which worlds to start.
|
|
WORLDS=$(getEnabledWorlds)
|
|
if [ -z "$WORLDS" ]; then
|
|
echo "There are no worlds to start."
|
|
echo "You may want to enable a world:"
|
|
echo " $PROG enable <world>"
|
|
echo "Or create a new world:"
|
|
echo " $PROG create <world> <port>"
|
|
echo "Or create the default world at the default port:"
|
|
echo " $PROG create"
|
|
exit 1
|
|
elif [ "$#" -ge 1 ]; then
|
|
WORLDS=''
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
if serverRunning "$arg"; then
|
|
printf "Unable to start the requested worlds: world '$arg' already running.\n"
|
|
exit 1
|
|
else
|
|
WORLDS="$WORLDS $arg"
|
|
fi
|
|
elif isWorldDisabled "$arg"; then
|
|
printf "Unable to start the requested worlds: world '$arg' is disabled.\n"
|
|
printf "Please enable the world first with '$PROG enable $arg'.\n"
|
|
exit 1
|
|
else
|
|
printf "Unable to start the requested worlds: world '$arg' not recognized.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
fi
|
|
# Start each world requested, if not already running.
|
|
printf "Starting Minecraft Server:"
|
|
for WORLD in $WORLDS; do
|
|
if ! serverRunning $WORLD; then
|
|
printf " $WORLD"
|
|
start $WORLD
|
|
fi
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
stop | force-stop)
|
|
# Figure out which worlds to stop.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
if ! serverRunning "$arg"; then
|
|
printf "Unable to stop the requested worlds: world '$arg' already stopped.\n"
|
|
exit 1
|
|
else
|
|
WORLDS="$WORLDS $arg"
|
|
fi
|
|
elif isWorldDisabled "$arg"; then
|
|
printf "Unable to stop the requested worlds: world '$arg' is disabled.\n"
|
|
printf "Please enable the world first with '$PROG enable $arg'.\n"
|
|
exit 1
|
|
else
|
|
printf "Unable to stop the requested worlds: world '$arg' not recognized.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
# Stop each world requested, if running.
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to stop worlds: no running worlds found.\n"
|
|
exit 1
|
|
fi
|
|
printf "Stopping Minecraft Server:"
|
|
for WORLD in $WORLDS; do
|
|
# Try to stop the world cleanly.
|
|
if serverRunning $WORLD; then
|
|
printf " $WORLD"
|
|
if [ $(queryNumUsers $WORLD) -gt 0 ]; then
|
|
sendCommand $WORLD "say The server admin has initiated a server shut down."
|
|
sendCommand $WORLD "say The server will shut down in 1 minute..."
|
|
sleep 60
|
|
sendCommand $WORLD "say The server is now shutting down."
|
|
fi
|
|
sendCommand $WORLD "save-all"
|
|
sendCommand $WORLD "save-off"
|
|
if [ "$COMMAND" = "force-stop" ]; then
|
|
forceStop $WORLD
|
|
else
|
|
stop $WORLD
|
|
fi
|
|
elif [ "$COMMAND" = "force-stop" ]; then
|
|
printf " $WORLD"
|
|
forceStop $WORLD
|
|
fi
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
restart | reload | force-restart | force-reload)
|
|
# Grab the latest version information.
|
|
updateVersionsJSON
|
|
# Figure out which worlds to restart.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
elif isWorldDisabled "$arg"; then
|
|
printf "Unable to start the requested worlds: world '$arg' is disabled.\n"
|
|
printf "Please enable the world first with '$PROG enable $arg'.\n"
|
|
exit 1
|
|
else
|
|
printf "Unable to restart the requested worlds: world '$arg' not recognized.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to restart worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
# Restart each world requested, start those not already
|
|
# running.
|
|
printf "Restarting Minecraft Server:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
if serverRunning $WORLD; then
|
|
if [ $(queryNumUsers $WORLD) -gt 0 ]; then
|
|
sendCommand $WORLD "say The server admin has initiated a server restart."
|
|
sendCommand $WORLD "say The server will restart in 1 minute..."
|
|
sleep 60
|
|
sendCommand $WORLD "say The server is now restarting."
|
|
fi
|
|
sendCommand $WORLD "save-all"
|
|
sendCommand $WORLD "save-off"
|
|
if [ "$(echo \"$COMMAND\" | cut -d '-' -f1)" = "force" ]; then
|
|
forceStop $WORLD
|
|
else
|
|
stop $WORLD
|
|
fi
|
|
sleep 5
|
|
fi
|
|
start $WORLD
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
create | new)
|
|
if [ -n "$1" ]; then
|
|
if [ -z "$2" ]; then
|
|
printf 'Creating world at default port %s' \
|
|
$DEFAULT_PORT
|
|
createWorld "$1" "$DEFAULT_PORT"
|
|
printf ".\n"
|
|
else
|
|
printf "Creating Minecraft world: $1"
|
|
createWorld "$1" "$2" "$3"
|
|
printf ".\n"
|
|
fi
|
|
elif [ -z "$(getAvailableWorlds)" ]; then
|
|
printf 'Creating default world "%s" at default port %s' \
|
|
$DEFAULT_WORLD $DEFAULT_PORT
|
|
createWorld "$DEFAULT_WORLD" "$DEFAULT_PORT"
|
|
printf ".\n"
|
|
fi
|
|
;;
|
|
import | copy | cp)
|
|
# Verify the directory argument.
|
|
if [ ! -d "$1" ] || [ ! -r "$1/server.properties" ]; then
|
|
printf "Directory containing a Minecraft world was not provided.\n"
|
|
printf " Usage: $PROG import <directory> <world> <port> [<ip>]\n"
|
|
exit 1
|
|
fi
|
|
# Verify the name and port arguments.
|
|
if [ -z "$2" ] || [ -z "$3" ] || isWorldAvailable "$2"; then
|
|
printf "An unused name and port for the world must be supplied.\n"
|
|
printf "An IP address is optional and usually not needed.\n"
|
|
printf " Usage: $PROG import <directory> <world> <port> [<ip>]\n"
|
|
exit 1
|
|
fi
|
|
# Import the world.
|
|
printf "Importing Minecraft world: $2"
|
|
importWorld "$1" "$2" "$3" "$4"
|
|
printf ".\n"
|
|
;;
|
|
rename)
|
|
# Verify the original world argument.
|
|
if [ -z "$1" ] || ! isWorldAvailable "$1"; then
|
|
printf "A Minecraft world was not provided.\n"
|
|
printf " Usage: $PROG rename <original world> <new world>\n"
|
|
exit 1
|
|
fi
|
|
# Verify the new world argument.
|
|
if [ -z "$2" ] || isWorldAvailable "$2"; then
|
|
printf "Invalid name for the new world provided.\n"
|
|
printf " Usage: $PROG rename <original world> <new world>\n"
|
|
exit 1
|
|
fi
|
|
# Make sure the world is stopped.
|
|
if serverRunning "$1"; then
|
|
# If the world server has users logged in, announce that the world is
|
|
# being modified.
|
|
if [ $(queryNumUsers "$1") -gt 0 ]; then
|
|
sendCommand "$1" "say The server admin is modifying this world."
|
|
sendCommand "$1" "say The server will be restarted in 1 minute..."
|
|
sleep 60
|
|
sendCommand "$1" "say The server is now shutting down."
|
|
fi
|
|
# Stop the world server.
|
|
stop "$1"
|
|
sleep 5
|
|
fi
|
|
# Save the port and IP for the world.
|
|
PORT=$(getServerPropertiesValue "$1" "server-port" "$DEFAULT_PORT")
|
|
IP=$(getServerPropertiesValue "$1" "server-ip" "$DEFAULT_IP")
|
|
# Rename the world.
|
|
printf "Renaming Minecraft world: $1"
|
|
mkdir -p "$WORLDS_LOCATION/$2"
|
|
mv $WORLDS_LOCATION/$1/* $WORLDS_LOCATION/$2
|
|
rm -r $WORLDS_LOCATION/$1
|
|
if [ -d $WORLDS_LOCATION/$2/$1 ]; then
|
|
mkdir -p $WORLDS_LOCATION/$2/$2
|
|
mv $WORLDS_LOCATION/$2/$1/* $WORLDS_LOCATION/$2/$2
|
|
rm -r $WORLDS_LOCATION/$2/$1
|
|
fi
|
|
createWorld "$2" "$PORT" "$IP"
|
|
printf ".\n"
|
|
;;
|
|
delete | remove)
|
|
# Get list of enabled worlds.
|
|
if ! isWorldAvailable "$1"; then
|
|
printf "World not found, unable to delete world '$1'.\n"
|
|
exit 1
|
|
fi
|
|
printf "Are you sure you wish to delete world '$1'? (y/n): "
|
|
read answer
|
|
if [ "$answer" = "${answer#[Yy]}" ]; then
|
|
printf "Aborted.\n"
|
|
exit 1
|
|
else
|
|
printf "Deleting Minecraft world: $1"
|
|
if serverRunning "$1"; then
|
|
# If the world server has users logged in, announce that the world is
|
|
# being deleted.
|
|
if [ $(queryNumUsers "$1") -gt 0 ]; then
|
|
sendCommand "$1" "say The server admin is deleting this world."
|
|
sendCommand "$1" "say The server will be deleted in 1 minute..."
|
|
sleep 60
|
|
sendCommand "$1" "say The server is now shutting down."
|
|
fi
|
|
# Stop the world server.
|
|
stop "$1"
|
|
sleep 5
|
|
fi
|
|
# Delete the world.
|
|
deleteWorld "$1"
|
|
printf ".\n"
|
|
fi
|
|
;;
|
|
disable)
|
|
# Get list of enabled worlds.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
elif isWorldDisabled "$arg"; then
|
|
printf "Unable to disable already disabled world '$arg'.\n"
|
|
exit 1
|
|
else
|
|
printf "World not found, unable to disable world '$arg'.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to disable worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
printf "Disabling Minecraft world:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
if serverRunning "$WORLD"; then
|
|
# If the world server has users logged in, announce that the world is
|
|
# being disabled.
|
|
if [ $(queryNumUsers "$WORLD") -gt 0 ]; then
|
|
sendCommand "$WORLD" "say The server admin is disabling this world."
|
|
sendCommand "$WORLD" "say The server will be disabled in 1 minute..."
|
|
sleep 60
|
|
sendCommand "$WORLD" "say The server is now shutting down."
|
|
fi
|
|
# Stop the world server.
|
|
stop "$WORLD"
|
|
sleep 5
|
|
fi
|
|
# Disable the world.
|
|
disableWorld "$WORLD"
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
enable)
|
|
# Grab the latest version information.
|
|
updateVersionsJSON
|
|
# Get list of disabled worlds.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldDisabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
elif isWorldEnabled "$arg"; then
|
|
printf "Unable to enable already enabled world '$arg'.\n"
|
|
exit 1
|
|
else
|
|
printf "World not found, unable to enable world '$arg'.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getDisabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to enable worlds: no disabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
printf "Enabling Minecraft world:"
|
|
# Enable the world(s).
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
# Enable the world.
|
|
enableWorld "$WORLD"
|
|
# Start the world.
|
|
start "$WORLD"
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
ls | list)
|
|
# Grab the desired list of worlds.
|
|
WORLDS=""
|
|
case "$1" in
|
|
enabled)
|
|
WORLDS=$(getEnabledWorlds)
|
|
;;
|
|
disabled)
|
|
WORLDS=$(getDisabledWorlds)
|
|
;;
|
|
running)
|
|
for WORLD in $(getEnabledWorlds); do
|
|
if serverRunning $WORLD; then
|
|
WORLDS="$WORLDS $WORLD"
|
|
fi
|
|
done
|
|
;;
|
|
stopped)
|
|
for WORLD in $(getEnabledWorlds); do
|
|
if ! serverRunning $WORLD; then
|
|
WORLDS="$WORLDS $WORLD"
|
|
fi
|
|
done
|
|
;;
|
|
"")
|
|
WORLDS=$(getAvailableWorlds)
|
|
;;
|
|
*)
|
|
echo "Unknown list option: $1."
|
|
echo " Try '$PROG help' for help."
|
|
exit 1
|
|
;;
|
|
esac
|
|
case "$COMMAND" in
|
|
ls)
|
|
# Simple list
|
|
for WORLD in $WORLDS; do
|
|
WPORT=$(getServerPropertiesValue "$WORLD" server-port "")
|
|
printf " $WORLD: $WPORT"
|
|
if isWorldEnabled "$WORLD"; then
|
|
printf '\n'
|
|
else
|
|
printf ' (disabled)\n'
|
|
fi
|
|
done
|
|
;;
|
|
list)
|
|
# Detailed list
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD:"
|
|
if isWorldEnabled "$WORLD"; then
|
|
printf '\n'
|
|
else
|
|
printf ' (disabled)\n'
|
|
fi
|
|
for PROP in $DETAILED_LISTING_PROPERTIES; do
|
|
printf " $PROP="
|
|
getServerPropertiesValue "$WORLD" "$PROP" ""
|
|
done
|
|
printf '\n'
|
|
done
|
|
;;
|
|
esac
|
|
;;
|
|
status | show | status-json | show-json)
|
|
# Figure out which worlds to show the status for.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldAvailable "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
else
|
|
printf "World '$arg' not recognized.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getAvailableWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to get world status: no available worlds found.\n"
|
|
exit 1
|
|
fi
|
|
case "$COMMAND" in
|
|
status-json | show-json)
|
|
# Show the status of each world requested in JSON format.
|
|
JSON=""
|
|
for WORLD in $WORLDS; do
|
|
if [ "$JSON" ]; then
|
|
JSON="$JSON, "
|
|
fi
|
|
JSON="$JSON\"$WORLD\":$(worldStatusJSON $WORLD)"
|
|
done
|
|
printf "{%s}" "$JSON"
|
|
;;
|
|
*)
|
|
# Show the status of each world requested.
|
|
printf "Minecraft Server Status:\n"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD: "
|
|
worldStatus $WORLD
|
|
done
|
|
;;
|
|
esac
|
|
;;
|
|
sync | synchronize)
|
|
# Figure out which worlds to synchronize.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
else
|
|
printf "World '$arg' not recognized.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to sync worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
# Synchronize the images for each world.
|
|
printf "Synchronizing Minecraft Server:"
|
|
for WORLD in $WORLDS; do
|
|
if serverRunning $WORLD; then
|
|
printf " $WORLD"
|
|
sendCommand $WORLD "save-all"
|
|
if true_value $ENABLE_MIRROR; then
|
|
sendCommand $WORLD "save-off"
|
|
sleep 20
|
|
syncMirrorImage $WORLD
|
|
sendCommand $WORLD "save-on"
|
|
fi
|
|
fi
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
broadcast)
|
|
# Get list of enabled worlds.
|
|
WORLDS=$(getEnabledWorlds)
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to broadcast: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
if [ -n "$1" ]; then
|
|
# Broadcast the message to all of the enabled worlds.
|
|
printf "Broadcasting command to world:"
|
|
for WORLD in $WORLDS; do
|
|
if serverRunning $WORLD; then
|
|
printf " $WORLD"
|
|
sendCommand $WORLD "$*"
|
|
fi
|
|
done
|
|
printf ".\n"
|
|
else
|
|
printf "Usage: $PROG $COMMAND <command>\n"
|
|
printf " ie: $PROG $COMMAND say Hello World!\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
send)
|
|
# Check for the world command line argument.
|
|
if isWorldEnabled "$1" && [ -n "$2" ]; then
|
|
WORLD=$1
|
|
shift 1
|
|
printf "Sending command to world: $WORLD - '$*'.\n"
|
|
sendCommand $WORLD "$*"
|
|
else
|
|
printf "Minecraft world '$1' not enabled or not found!\n"
|
|
printf "Usage: $PROG $COMMAND <world> <command>\n"
|
|
printf " ie: $PROG $COMMAND world say Hello World!\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
console)
|
|
# Check for the world command line argument.
|
|
if isWorldEnabled "$1"; then
|
|
printf "Connecting to server console: $1.\n"
|
|
printf " Hit <Ctrl-D> to detach.\n"
|
|
sleep 3
|
|
serverConsole $1
|
|
else
|
|
if [ -n "$1" ]; then
|
|
printf "Minecraft world '$1' not enabled or not found!\n"
|
|
else
|
|
printf "Minecraft world not provided!\n"
|
|
fi
|
|
printf " Usage: $PROG $COMMAND <world>\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
watch)
|
|
# Check for the world command line argument.
|
|
if isWorldEnabled "$1"; then
|
|
printf "Monitoring Minecraft Server: $1.\n"
|
|
watchLog $1
|
|
else
|
|
if [ -n "$1" ]; then
|
|
printf "Minecraft world '$1' not enabled or not found!\n"
|
|
else
|
|
printf "Minecraft world not provided!\n"
|
|
fi
|
|
printf " Usage: $PROG $COMMAND <world>\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
logrotate)
|
|
# Figure out which worlds to rotate the log.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
else
|
|
printf "World '$arg' does not exist or not enabled.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to logrotate worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
# Rotate the log for each world requested.
|
|
printf "Rotating Minecraft Server Log:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
rotateLog $WORLD
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
backup)
|
|
# Figure out which worlds to backup.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
else
|
|
printf "World '$arg' does not exist or not enabled.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to backup worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
# Backup each world requested.
|
|
printf "Backing up Minecraft Server:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
# Create a lock file so that we don't create duplicate processes.
|
|
if $(createLockFile $WORLD "backup"); then
|
|
if serverRunning $WORLD; then
|
|
sendCommand $WORLD "say Backing up the world."
|
|
sendCommand $WORLD "save-all"
|
|
sendCommand $WORLD "save-off"
|
|
sleep 20
|
|
worldBackup $WORLD
|
|
sendCommand $WORLD "save-on"
|
|
sendCommand $WORLD "say Backup complete."
|
|
else
|
|
worldBackup $WORLD
|
|
fi
|
|
# Remove the lock file.
|
|
removeLockFile $WORLD "backup"
|
|
# Cleanup old backup files.
|
|
worldBackupCleanup $WORLD
|
|
else
|
|
printf "\nError lock file for world $WORLD could not be created."
|
|
printf " Is the world already running a backup?\n"
|
|
fi
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
clean-backups)
|
|
if isWorldEnabled "$1"; then
|
|
worldBackupCleanup "$1"
|
|
elif [ -n "$1" ]; then
|
|
printf "World '$1' does not exist or not enabled.\n"
|
|
printf " Usage: $PROG $COMMAND <world>\n"
|
|
exit 1
|
|
else
|
|
printf "World not supplied.\n"
|
|
printf " Usage: $PROG $COMMAND <world>\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
list-backups)
|
|
if isWorldEnabled "$1"; then
|
|
worldBackupList "$1"
|
|
elif [ -n "$1" ]; then
|
|
printf "World '$1' does not exist or not enabled.\n"
|
|
printf " Usage: $PROG $COMMAND <world>\n"
|
|
exit 1
|
|
else
|
|
printf "World not supplied.\n"
|
|
printf " Usage: $PROG $COMMAND <world>\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
restore-backup)
|
|
if [ -z $1 ]; then
|
|
printf "World not supplied.\n"
|
|
printf " Usage: $PROG $COMMAND <world> <datetime>\n"
|
|
exit 1
|
|
elif ! isWorldAvailable "$1"; then
|
|
printf "World '$1' not recognized.\n"
|
|
printf " Usage: $PROG $COMMAND <world> <datetime>\n"
|
|
exit 1
|
|
elif serverRunning "$1"; then
|
|
printf "World '$1' is running. Stop it first\n"
|
|
printf " $PROG stop $1\n"
|
|
exit 1
|
|
elif worldBackup "$1"; then
|
|
worldBackupRestore "$1" "$2"
|
|
else
|
|
echo "Current world's state backup failed. Restore canceled."
|
|
exit 1
|
|
fi
|
|
;;
|
|
update | force-update)
|
|
# Handle the force option.
|
|
if [ "$COMMAND" = "force-update" ]; then
|
|
if [ -s $VERSIONS_JSON ]; then
|
|
# Make a backup copy of the version_manifest.json file.
|
|
cp -fp "$VERSIONS_JSON" "$VERSIONS_JSON.bak"
|
|
printf "Removing cached version manifest.\n"
|
|
fi
|
|
rm -f $VERSIONS_JSON
|
|
fi
|
|
# Grab the latest version information.
|
|
updateVersionsJSON
|
|
# Figure out which worlds to update.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
else
|
|
printf "World '$arg' does not exist or not enabled.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to update worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
# Stop all of the world servers and backup the worlds.
|
|
RUNNING=
|
|
printf "Stopping Minecraft Server:"
|
|
for WORLD in $WORLDS; do
|
|
if serverRunning $WORLD; then
|
|
RUNNING="$RUNNING $WORLD"
|
|
printf " $WORLD"
|
|
if [ $(queryNumUsers $WORLD) -gt 0 ]; then
|
|
sendCommand $WORLD "say The server admin has initiated a software update."
|
|
sendCommand $WORLD "say The server will restart and update in 1 minute..."
|
|
sleep 60
|
|
sendCommand $WORLD "say The server is now restarting."
|
|
fi
|
|
sendCommand $WORLD "save-all"
|
|
sendCommand $WORLD "save-off"
|
|
stop $WORLD
|
|
fi
|
|
done
|
|
printf ".\n"
|
|
printf "Backing up Minecraft Server:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
worldBackup $WORLD
|
|
done
|
|
printf ".\n"
|
|
# Handle the force option.
|
|
if [ "$COMMAND" = "force-update" ]; then
|
|
printf "Removing Minecraft Server software:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
rm -f $(getServerLocation "$WORLD")/$(getServerJar "$WORLD")
|
|
done
|
|
printf ".\n"
|
|
fi
|
|
# Update the server software.
|
|
printf "Updating Server Software:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
updateServerSoftware "$WORLD"
|
|
done
|
|
printf ".\n"
|
|
printf "Restarting Minecraft Server:"
|
|
for WORLD in $RUNNING; do
|
|
printf " $WORLD"
|
|
start $WORLD
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
map | overviewer)
|
|
# Grab the latest version information.
|
|
updateVersionsJSON
|
|
# Make sure that the Minecraft Overviewer software exists.
|
|
if [ ! -e "$OVERVIEWER_BIN" ]; then
|
|
printf "Minecraft Overviewer software not found.\n"
|
|
exit 1
|
|
fi
|
|
# Figure out which worlds to map.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
else
|
|
printf "World '$arg' does not exist or not enabled.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to map worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
# Run Minecraft Overviewer on each world requested.
|
|
printf "Running Minecraft Overviewer mapping:"
|
|
for WORLD in $WORLDS; do
|
|
printf " $WORLD"
|
|
# Create a lock file so that we don't create duplicate processes.
|
|
if $(createLockFile $WORLD "overviewer"); then
|
|
overviewer "$WORLD"
|
|
# Remove the lock file.
|
|
removeLockFile $WORLD "overviewer"
|
|
else
|
|
printf "\nError lock file for world $WORLD could not be created."
|
|
printf " Is the world already running Overviewer?\n"
|
|
fi
|
|
done
|
|
printf ".\n"
|
|
;;
|
|
query | query-raw | query-json)
|
|
# Figure out which worlds to show the status for.
|
|
WORLDS=""
|
|
if [ "$#" -ge 1 ]; then
|
|
for arg in "$@"; do
|
|
if isWorldEnabled "$arg"; then
|
|
WORLDS="$WORLDS $arg"
|
|
else
|
|
printf "World '$arg' does not exist or not enabled.\n"
|
|
printf " Usage: $PROG $COMMAND <world1> <world2> <...>\n"
|
|
exit 1
|
|
fi
|
|
done
|
|
else
|
|
WORLDS=$(getEnabledWorlds)
|
|
fi
|
|
if [ -z "$WORLDS" ]; then
|
|
printf "Unable to query worlds: no enabled worlds found.\n"
|
|
exit 1
|
|
fi
|
|
case "$COMMAND" in
|
|
query | query-raw)
|
|
for WORLD in $WORLDS; do
|
|
queryDetailedStatus "$WORLD"
|
|
printf "\n"
|
|
done
|
|
;;
|
|
query-json)
|
|
for WORLD in $WORLDS; do
|
|
queryDetailedStatusJSON "$WORLD"
|
|
printf "\n"
|
|
done
|
|
;;
|
|
esac
|
|
;;
|
|
usage | help)
|
|
printf "Minecraft Server Control Script\n"
|
|
printf "\n"
|
|
usage
|
|
;;
|
|
*)
|
|
printf "Error in command line usage.\n"
|
|
printf "\n"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
exit 0
|