msm/init/msm

2262 lines
68 KiB
Plaintext
Raw Normal View History

#!/bin/bash
### BEGIN INIT INFO
# Provides: msm
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start: $network
# Should-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description:
# Description:
### END INIT INFO
# See http://www.debian.org/doc/debian-policy/ch-opersys.html#s-sysvinit for
# more information on debain init.d scripts, which may help you understand
# this script.
### The configuration file
# Get the MSM_CONF environment variable or use the default location
CONF="${MSM_CONF:-/etc/msm.conf}"
2012-05-20 11:48:13 +00:00
2012-06-02 20:02:58 +00:00
### The Minecraft Server Manager version, use "msm version" to check yours.
VERSION="0.2.4 Beta"
2012-06-02 20:02:58 +00:00
### Config variables the user should not need/want to change
2012-05-20 12:40:46 +00:00
# Jar group file which contains the download target URL
declare -r JARGROUP_TARGET="target.txt"
# Jar group directory name to download new jars to, is deleted afterwards
declare -r JARGROUP_DOWNLOAD_DIR="downloads"
# The flag which indicates the world should be put in RAM
declare -r WORLD_FLAG_INRAM="inram"
# The flag which indicates the server is active
declare -r SERVER_FLAG_ACTIVE="active"
# The start of a regex to find a log line
declare -r LOG_REGEX="^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[.*\]"
### Script State Variables
# "true" whilst the script is counting down a delay to stop the server
declare STOP_COUNTDOWN
# "true" whilst the script is counting down a delay to restart the server
declare RESTART_COUNTDOWN
2012-05-20 12:40:46 +00:00
### Utility Functions
# Executes the command "$2" as user "$1"
# $1: The user to execute the command as
# $2: The command to execute
2012-05-20 12:40:46 +00:00
as_user() {
local user="$(whoami)"
if [ "$user" == "$1" ]; then
bash -c "$2"
2012-05-20 12:40:46 +00:00
else
if [ "$user" == "root" ]; then
su - "$1" -s /bin/bash -c "$2"
else
echoerr "This command must be executed as the user \"$1\" or \"root\"."
exit 1
fi
2012-05-20 12:40:46 +00:00
fi
}
# Executes the command "$1" as SERVER_USER but returns stderr instead
as_user_stderr() {
as_user "$@" > /dev/null 2>&1
}
# Echo to stderr
echoerr() {
echo "$@" 1>&2
}
# A function used to print debug messages to stdout. Prevents messages from
# appearing unless in debug mode, and allows debug statements to be easily
# distinguished from necessary echo statements.
# $1: The message to output
debug() {
if [[ "$DEBUG" == "true" ]]; then
echoerr "$1"
fi
}
# Determines whether "$1" is a valid name for a server or jar group directory
# It must only contain upper or lower case letters, digits, dashes or
# underscores.
# It must also not be one of a list of reserved names.
is_valid_name() {
local valid="^[a-zA-Z0-9\_\-]+$"
local invalid="^(start|stop|restart|version|server|jargroup|all)$"
if [[ "$1" =~ $valid ]]; then
if [[ "$1" =~ $invalid ]]; then
2012-06-02 20:02:58 +00:00
echoerr "Invalid name \"$1\": A name may not be any of the following reserved worlds \"start\", \"stop\", \"restart\", \"server\", \"version\", \"jargroup\" or \"all\"."
return 1
else
return 0
fi
else
echoerr "Invalid name \"$1\": A name may only contain letters, numbers, dashes and unscores."
return 1
fi
}
# Gets the latest jar from a jar group, based upon the date and time encoded
# in the file name.
2012-05-30 19:44:42 +00:00
# $1: The directory to search
get_latest_file() {
local best_time=0
local best_file=""
2012-05-30 19:44:42 +00:00
while IFS= read -r -d $'\0' file; do
# Remove the path, leaving just the file name
local date_time="$(basename "$file" | awk -F '-' '{print $1 "-" $2 "-" $3 " " $4 ":" $5 ":" $6}')"
# Get the time in seconds since 1970 from file name
local seconds="$(date -d "$date_time" "+%s" 2> /dev/null)"
# If that is newer than the current best, override variables
if [[ "$seconds" -gt "$best_time" ]]; then
best_time="$seconds"
best_file="$file"
fi
2012-05-30 19:44:42 +00:00
done < <(find "$1" -maxdepth 1 -type f -print0)
echo "$best_file"
}
# Returns the current time as a UNIX timestamp (in seconds since 1970)
now() {
date +%s
}
### Log Utility Functions
# Gets the UNIX timestamp for a server log line
# $1: A server log line
# returns: Time in seconds since 1970-01-01 00:00:00 UTC
log_line_get_time() {
time_string="$(echo "$1" | awk '{print $1 " " $2}')"
date -d "$time_string" "+%s" 2> /dev/null
2012-05-21 17:34:12 +00:00
}
### World Utility Functions
# Moves a world to RAM
# $1: the ID of the world to move
world_to_ram() {
if [ ! -z "$RAMDISK_STORAGE_PATH" ]; then
as_user "${server_user_name[${world_server_id[$1]}]}" "mkdir -p \"${world_ramdisk_path[$1]}\" && rsync -rt --exclude '$WORLD_FLAG_INRAM' \"${world_path[$1]}/\" \"${world_ramdisk_path[$1]}\""
fi
}
2012-05-24 01:29:20 +00:00
# Moves a world in RAM to disk
# $1: the ID of the world to move
world_to_disk() {
as_user "${server_user_name[${world_server_id[$1]}]}" "rsync -rt --exclude '$WORLD_FLAG_INRAM' \"${world_ramdisk_path[$1]}/\" \"${world_path[$1]}\""
2012-05-24 01:29:20 +00:00
}
# Toggles a worlds ramdisk state
# $1: The ID of the world
world_toggle_ramdisk_state() {
if [ -f "${world_flag_inram[$1]}" ]; then
echo -n "Removing RAM flag from world \"${world_name[$1]}\"... "
as_user "${server_user_name[${world_server_id[$1]}]}" "rm -f \"${world_flag_inram[$1]}\""
echo "Done."
echo -n "Removing world \"${world_name[$1]}\" from RAM... "
as_user "${server_user_name[${world_server_id[$1]}]}" "rm -r \"${world_ramdisk_path[$1]}\""
echo "Done."
else
echo -n "Adding RAM flag to world \"${world_name[$1]}\"... "
as_user "${server_user_name[${world_server_id[$1]}]}" "touch \"${world_flag_inram[$1]}\""
echo "Done."
echo -n "Copying world to RAM... "
world_to_ram "$1"
echo "Done."
fi
echo "Changes will only take effect after server is restarted."
}
2012-05-24 05:55:04 +00:00
# Backs up a world
# $1: The ID of the world
world_backup() {
echo -n "Backing up world \"${world_name[$1]}\"... "
file_name="$(date "+%F-%H-%M-%S").zip"
local server_id="${world_server_id[$1]}"
local containing_dir="$(dirname "${world_path[$1]}")"
local dir_name="$(basename "${world_path[$1]}")"
as_user "${server_user_name[$server_id]}" "mkdir -p \"${world_backup_path[$1]}\" && cd \"$containing_dir\" && zip -rq \"${world_backup_path[$1]}/${file_name}\" \"${dir_name}\""
2012-05-24 05:55:04 +00:00
echo "Done."
}
# Activates a world
# $1: The ID of the world
world_activate() {
if [ -d "${world_inactive_path[$1]}" ]; then
echo -n "Moving world \"${world_name[$1]}\" to the active worldstorage directory... "
local new_path="${world_active_path[$1]}"
as_user "${server_user_name[${world_server_id[$1]}]}" "mkdir -p \"$new_path\" && mv \"${world_inactive_path[$1]}\" \"$new_path\""
echo "Done."
else
if [ -d "${world_active_path[$1]}" ]; then
echo "World \"${world_name[$1]}\" is already activate."
else
echoerr "Error: Directory \"${world_inactive_path[$1]}\" could not be found."
return 1
fi
fi
}
# Deactivates a world
# $1: The ID of the world
world_deactivate() {
if server_is_running "${world_server_id[$1]}"; then
echoerr "Error: Worlds cannot be deactivated whilst the server is running."
return 1
else
if [ -d "${world_active_path[$1]}" ]; then
echo -n "Moving world \"${world_name[$1]}\" to the inactive worldstorage directory... "
local new_path="${world_inactive_path[$1]}"
as_user "${server_user_name[${world_server_id[$1]}]}" "mkdir -p \"$new_path\" && mv \"${world_path[$1]}\" \"$new_path\""
echo "Done."
else
if [ -d "${world_inactive_path[$1]}" ]; then
echo "World \"${world_name[$1]}\" is already deactivate."
else
echoerr "Error: Directory \"${world_active_path[$1]}\" could not be found."
return 1
fi
fi
fi
}
### Server Utility Functions
# Returns the ID for a server.
# An ID is given to a server when loaded into memory, and can be used to lookup
# config information for that server
# $1: The name of the server
server_get_id() {
local i=0
while [[ "$i" -lt "$num_servers" ]]; do
if [[ "${server_name[$i]}" == "$1" ]]; then
echo "$i"
return 0
fi
i="$(( $i + 1 ))"
done
return 1
}
# Returns the ID of a server's world.
# $1: The ID of the server
# $2: The name of the world
server_world_get_id() {
if [ -d "${server_world_storage[$1]}/$2" ] || [ -d "${server_world_storage_inactive[$1]}/$2" ]; then
# If the directory exists
local start="${server_world_offset[$1]}"
local max="$(( $i + ${server_num_worlds[$1]} ))"
# For each of the servers worlds:
for ((i=$start; i<$max; i++)); do
if [[ "${world_name[$i]}" == "$2" ]]; then
echo "$i"
return 0
fi
done
fi
return 1
}
# Returns 0 if the server $1 is running and 1 if not
# $1: The ID of the server
server_is_running() {
2012-05-24 01:29:20 +00:00
if ps ax | grep -v grep | grep "${server_screen_name[$1]} ${server_invocation[$1]}" > /dev/null
then
return 0
else
return 1
2012-05-21 15:13:24 +00:00
fi
}
# Creates symbolic links in the server directory (SERVER_STORAGE_PATH) for each
# of the Minecraft worlds located in the world storage directory.
# $1: The id of the server for which links should be ensured
server_ensure_links() {
echo -n "Maintaining world symbolic links... "
local start="${server_world_offset[$1]}"
local max="$(( $start + ${server_num_worlds[$1]} ))"
local output="false"
for ((i=$start; i<$max; i++)); do
if [[ "${world_status[$i]}" != "active" ]]; then
# Remove the symbolic link if it exists
as_user "${server_user_name[$1]}" "rm -f \"${world_link[$i]}\""
continue
fi
# -L checks for the path being a link rather than a file
# ! -a, since it is within double square brackets means: the negation of
# the existence of the file. In other words: true if does not exist
if [[ -L "${world_link[$i]}" || ! -a "${world_link[$i]}" ]]; then
# If there is a symbolic link in the server direcotry to this world,
# or there is not a directory in the server directory containing this world.
# Get the original file path the symbolic link is pointing to
# If there is no link, link_target will contain nothing
link_target="$(readlink "${world_link[$i]}")"
if "${world_inram[$i]}"; then
# If this world is marked as loaded into RAM
if [ "${link_target}" != "${world_ramdisk_path[$i]}" ]; then
# If the symbolic link does not point to the RAM version of the world
# Remove the symbolic link if it exists
as_user "${server_user_name[$1]}" "rm -f \"${world_link[$i]}\""
# Create a new symbolic link pointing to the RAM version of the world
as_user "${server_user_name[$1]}" "ln -s \"${world_ramdisk_path[$i]}\" \"${world_link[$i]}\""
fi
2012-05-21 15:13:24 +00:00
else
# Otherwise the world is not loaded into RAM, and is just on disk
if [ "${link_target}" != "${world_path[$i]}" ]; then
# If the symbolic link does not point to the disk version of the world
# Remove the symbolic link if it exists
as_user "${server_user_name[$1]}" "rm -f \"${world_link[$i]}\""
# Create a new symbolic link pointing to the disk version of the world
as_user "${server_user_name[$1]}" "ln -s \"${world_path[$i]}\" \"${world_link[$i]}\""
fi
2012-05-21 15:13:24 +00:00
fi
else
echoerr -en "\n Error: Could not create link for world \"${world_name[$i]}\". The file \"${world_link[$i]}\" already exists, and should not be overwritten automatically. Either remove this file, or rename \"${world_name[$i]}\"."
output="true"
fi
done
if [[ "$output" == "true" ]]; then
echo -e "\nDone."
else
echo "Done."
fi
}
# Moves a servers worlds into RAM
# $1: The ID of the server
server_worlds_to_ram() {
# Only proceed if there is a ramdisk path set in config
if [ ! -z "$RAMDISK_STORAGE_PATH" ]; then
echo -n "Synchronising flagged worlds on disk to RAM... "
local i="${server_world_offset[$1]}"
local max="$(( $i + ${server_num_worlds[$1]} ))"
# For each of the servers worlds:
while [[ "$i" -lt "$max" ]]; do
if "${world_inram[$i]}" && [ -L "${world_link[$i]}" ]; then
world_to_ram "$i"
fi
i="$(( $i + 1 ))"
done
echo "Done."
fi
}
2012-05-24 01:29:20 +00:00
# Moves a servers "in RAM" worlds back to disk
# $1: The ID of the server
server_worlds_to_disk() {
if [ ! -z "$RAMDISK_STORAGE_PATH" ]; then
echo -n "Synchronising worlds in RAM to disk... "
local i="${server_world_offset[$1]}"
local max="$(( $i + ${server_num_worlds[$1]} ))"
2012-05-24 01:29:20 +00:00
# For each of the servers worlds:
while [[ "$i" -lt "$max" ]]; do
if [ -d "${world_ramdisk_path[$i]}" ]; then
world_to_disk "$i"
2012-05-24 01:29:20 +00:00
fi
i="$(( $i + 1 ))"
2012-05-24 01:29:20 +00:00
done
echo "Done."
fi
}
# Watches a server's log for a specific line
# $1: The ID for the server
# $2: A UNIX timestamp (seconds since 1970) which the $3 line must be after
# $3->: The line or lines in the log to wait for
# returns: When the line is found
server_log_get_line() {
# Make sure there is a server log to check
as_user "${server_user_name[$1]}" "touch ${server_log[$1]}"
while read line; do
line_time="$(log_line_get_time "$line")"
# If the entry is old enough
if [[ "$line_time" -ge "$2" ]]; then
for search_line in "${@:3}"; do
local regex="${LOG_REGEX} ${search_line}"
# and matches the regular expression
if [[ "$line" =~ $regex ]]; then
echo "$line"
return 0
fi
done
fi
done < <(as_user "${server_user_name[$1]}" "tail --follow --lines=100 --sleep-interval=0.1 \"${server_log[$1]}\"")
}
# The same as server_log_get_line, but does not print the line to stdout
# when found.
server_log_wait_for_line() {
server_log_get_line "$@" > /dev/null
}
2012-05-24 01:29:20 +00:00
# Sends as string to a server for execution
# $1: The ID of the server
# $2: The line of text to enter into the server console
server_eval() {
as_user "${server_user_name[$1]}" "screen -p 0 -S ${server_screen_name[$1]} -X eval 'stuff \"$2\"\015'"
2012-05-24 01:29:20 +00:00
}
# The same as server_eval, but also waits for a log entry before returning
# $1: The ID of the server
# $2: A line of text to enter into the server console
# $3->: The line or lines of text in the log to wait for
2012-05-24 01:29:20 +00:00
# stdout: The full entry found in the logs
server_eval_and_get_line() {
time_now="$(now)"
server_eval "$1" "$2"
server_log_get_line "$1" "$time_now" "${@:3}"
2012-05-24 01:29:20 +00:00
}
# The same as server_eval_and_get_line, but does not print anything to stdout
server_eval_and_wait() {
server_eval_and_get_line "$@" > /dev/null
2012-05-24 01:29:20 +00:00
}
# Gets the process ID for a server if running, otherwise it outputs nothing
# $1: The ID of the server
server_pid() {
ps ax | grep -v grep | grep "${server_screen_name[$1]} ${server_invocation[$1]}" | awk '{print $1}'
}
# Waits for a server to stop by polling 10 times a second
# This approach is fairyl intensive, so only use when you are expecting the
# server to stop soon
# $1: The ID of the server to wait for
server_wait_for_stop() {
local pid="$(server_pid "$1")"
# if the process is still running, wait for it to stop
if [ ! -z "$pid" ]; then
while ps -p "$pid" > /dev/null; do
sleep 0.1
done
fi
2012-05-24 01:29:20 +00:00
}
# Sets a server's active/inactive state
# $1: The ID of the server
# $2: A string containing "active" or "inactive"
server_set_active() {
case "$2" in
active)
as_user "${server_user_name[$1]}" "touch \"${server_flag_active[$1]}\""
server_active[$1]="true"
;;
inactive)
as_user "${server_user_name[$1]}" "rm -f \"${server_flag_active[$1]}\""
server_active[$1]="false"
;;
*)
return 1
;;
esac
}
2012-05-21 17:34:12 +00:00
### Jar Group Functions
# Lists the jar files grouped by jar groups.
jargroup_list() {
for group in $(ls -1 "$JAR_STORAGE_PATH"); do
echo "${group}:"
for jar in $(ls -1r "$JAR_STORAGE_PATH/$group"); do
if [[ "$jar" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}- ]]; then
echo " $jar"
fi
done
done
}
# Creates a new jargroup
# $1: The name for the jargroup
jargroup_create() {
if is_valid_name "$1"; then
if [[ ! -e "$JAR_STORAGE_PATH/$1" ]]; then
printf "Creating jar group... "
local error="$(as_user_stderr "$USERNAME" "mkdir -p \"$JAR_STORAGE_PATH/$1\"")"
if [[ "$error" != "" ]]; then
echo "Failed."
echo "Reason: $error"
return 1
fi
error="$(as_user "$USERNAME" "echo \"$2\" > \"$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET\"")"
if [[ "$error" != "" ]]; then
echo "Failed."
echo "Reason: $error"
return 1
fi
echo "Done."
# Download the latest version now
jargroup_getlatest "$1"
else
echo "A jar group with that name already exists."
return 1
fi
else
return 1
fi
}
# Downloads the latest version for a jargroup, using the target URL for that
# group. Saves the download with the date and time encoded in the start of the
# file name, in the jar group directory in question. Removes the file if there
# is no difference between it and the current version.
# $1: The jargroup name to download the latest version for
jargroup_getlatest() {
if is_valid_name "$1"; then
if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
if [[ -e "$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET" ]]; then
printf "Downloading latest version... "
2012-05-21 13:39:59 +00:00
# Try and make
local error="$(as_user_stderr "$USERNAME" "mkdir -p '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'")"
if [[ "$error" != "" ]]; then
echo "Failed."
echo "Reason: $error"
return 1
fi
2012-05-21 13:39:59 +00:00
as_user "$USERNAME" "wget --quiet --trust-server-names --no-check-certificate --input-file='$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET' --directory-prefix='$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'"
echo "Done."
2012-05-21 13:39:59 +00:00
local num_files="$(as_user "$USERNAME" "ls -1 '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR' | wc -l")"
2012-05-21 13:39:59 +00:00
if [[ "$num_files" == 1 ]]; then
# There was 1 file downloaded
local file_name="$(ls -1 "$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR")"
local new_name="$(date +%F-%H-%M-%S)-$file_name"
local most_recent_jar="$(get_latest_file "$JAR_STORAGE_PATH/$1")"
2012-05-30 19:44:42 +00:00
if [[ ! -e "$most_recent_jar" ]] || ! diff "$most_recent_jar" "$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR/$file_name" > /dev/null; then
# There is not a previous version to do a comparison against, or
# The previous version is different:
# Add it to the group
2012-05-21 13:39:59 +00:00
[[ -e "$most_recent_jar" ]]
local was_previous="$?"
as_user "$USERNAME" "mv '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR/$file_name' '$JAR_STORAGE_PATH/$1/$new_name'"
if [[ ! -z "$most_recent_jar" ]]; then
echo "Downloaded version was different to previous latest. Saved as \"$JAR_STORAGE_PATH/$1/$new_name\"."
else
echo "Saved as \"$JAR_STORAGE_PATH/$1/$new_name\"."
fi
else
echo "Existing version \"$JAR_STORAGE_PATH/$1/$new_name\" was already up to date."
fi
elif [[ "$num_files" == 0 ]]; then
# No file was downloaded
echo "Failed. No files were downloaded."
else
# Multiple files were
echo "Error. URL downloads multiple files."
fi
# Clean up the temp download folder
as_user "$USERNAME" "rm -fr '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'"
else
echo "Target URL not found, use $0 jargroup seturl <download-url>"
return 1
fi
2012-05-21 13:39:59 +00:00
else
echo "There is no jar group with the name \"$1\"."
return 1
fi
else
return 1
2012-05-21 13:39:59 +00:00
fi
}
# Deletes an existing jargroup
# $1: The name of the existing jargroup
jargroup_delete() {
if is_valid_name "$1"; then
if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
printf "Are you sure you want to delete this jar group [y/N]: "
2012-05-21 13:39:59 +00:00
read answer
if [[ "$answer" =~ ^y|Y|yes$ ]]; then
as_user "$USERNAME" "rm -rf \"$JAR_STORAGE_PATH/$1\""
echo "Jar group deleted."
else
echo "Jar group was NOT deleted."
fi
else
echo "There is no jar group with the name \"$1\"."
return 1
fi
else
return 1
fi
2012-05-20 12:40:46 +00:00
}
# Renames an existing jargroup
# $1: The name of the existing jargroup
# $2: The new name
jargroup_rename() {
if is_valid_name "$1"; then
if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
# If the jar group name is valid,
# and there is no other jar group with the name $1
if is_valid_name "$2"; then
if [[ -e "$JAR_STORAGE_PATH/$2" ]]; then
echo "Could not be renamed, there is already a jar group with the name \"$2\"."
return 1
else
# TODO: Update any symbolic links which point to a jar in this directory
as_user "$USERNAME" "mv '$JAR_STORAGE_PATH/$1' '$JAR_STORAGE_PATH/$2'"
echo "Renamed jar group \"$1\" to \"$2\"."
fi
else
return 1
fi
else
echo "There is no jar group with the name \"$1\"."
return 1
fi
else
return 1
fi
}
2012-05-20 12:40:46 +00:00
### Server Functions
# Echos a list of servers in the SERVER_STORAGE_PATH
server_list() {
ls -1 "$SERVER_STORAGE_PATH"
}
# Creates a new server
# $1: The server name to create
server_create() {
if is_valid_name "$1"; then
if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
echo "A server with that name already exists."
return 1
else
printf "Creating server directory... "
as_user "$USERNAME" "mkdir -p '$SERVER_STORAGE_PATH/$1'"
2012-05-31 00:53:16 +00:00
as_user "$USERNAME" "touch '$SERVER_STORAGE_PATH/$1/$DEFAULT_WHITELIST'"
as_user "$USERNAME" "touch '$SERVER_STORAGE_PATH/$1/$DEFAULT_BANNED_IPS'"
as_user "$USERNAME" "touch '$SERVER_STORAGE_PATH/$1/$DEFAULT_BANNED_PLAYERS'"
as_user "$USERNAME" "touch '$SERVER_STORAGE_PATH/$1/$DEFAULT_OPS'"
as_user "$USERNAME" "touch '$SERVER_STORAGE_PATH/$1/$DEFAULT_PROPERTIES'"
echo "Done."
# Now that the new server has been created, we must call init again
# to ensure it is recognised.
init
server_set_jar "$(server_get_id "$1")" "minecraft"
fi
else
return 1
fi
}
# Deletes an existing server
# $2: The server name to delete
server_delete() {
if is_valid_name "$1"; then
if [[ -d "$SERVER_STORAGE_PATH/$1" ]]; then
printf "Are you sure you want to delete this server and its worlds (note: backups are preserved) [y/N]: "
read answer
if [[ "$answer" =~ ^y|Y|yes$ ]]; then
# TODO: stop the server if running first
as_user "$USERNAME" "rm -rf '$SERVER_STORAGE_PATH/$1'"
echo "Server deleted."
else
echo "Server was NOT deleted."
fi
else
echo "There is no server with the name \"$1\"."
return 1
fi
else
return 1
fi
}
# Renames an existing server
# $1: The server name to change
# $2: The new name for the server
server_rename() {
if is_valid_name "$1"; then
if server_is_running "$(server_get_id "$1")"; then
echoerr "Error: Can only rename a stopped server."
return 1
else
if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
# If the server name is valid and exists
# TODO: Check that the server is not running first, return if it is
if is_valid_name "$2"; then
# If the server name is valid
if [[ -e "$SERVER_STORAGE_PATH/$2" ]]; then
# and there is not already a server with the name $2
echoerr "Error: Could not be renamed, there is already a server with the name \"$2\"."
return 1
else
as_user "$USERNAME" "mv '$SERVER_STORAGE_PATH/$1' '$SERVER_STORAGE_PATH/$2'"
echo "Renamed server \"$1\" to \"$2\"."
fi
else
return 1
fi
else
echoerr "Error: There is no server with the name \"$1\"."
return 1
fi
fi
else
return 1
fi
}
# Starts a single server
# $1: The ID of the server
server_start() {
if server_is_running "$1"; then
echo "Server \"${server_name[$1]}\" is already running!"
else
server_ensure_links "$1"
server_worlds_to_ram "$1"
local time_now="$(now)"
printf "Starting server... "
as_user "${server_user_name[$1]}" "cd \"${server_path[$1]}\" && screen -dmS \"${server_screen_name[$1]}\" ${server_invocation[$1]}"
server_log_wait_for_line "$1" "$time_now" "${server_confirm_start[$1]}"
echo "Done."
fi
}
2012-05-24 01:29:20 +00:00
# Sends the "save-all" command to a server
# $1: The ID of the server
server_save_all() {
if server_is_running "$1"; then
echo -n "Forcing save... "
2012-05-24 01:29:20 +00:00
# Send the "save-all" command and wait for it to finish
server_eval_and_wait "$1" "save-all" "${server_confirm_save_all[$1]}"
2012-05-24 01:29:20 +00:00
echo "Done."
else
echo "Server \"${server_name[$1]}\" is not running."
2012-05-24 01:29:20 +00:00
fi
}
# Sends the "save-off" command to a server
# $1: The ID of the server
server_save_off() {
if server_is_running "$1"; then
echo -n "Disabling level saving... "
# Send the "save-off" command and wait for it to finish
server_eval_and_wait "$1" "save-off" "${server_confirm_save_off[$1]}"
echo "Done."
# Writes any in-memory data manged by the kernel to disk
sync
else
echo "Server \"${server_name[$1]}\" is not running."
fi
}
# Sends the "save-on" command to a server
# $1: The ID of the server
server_save_on() {
if server_is_running "$1"; then
echo -n "Enabling level saving... "
# Send the "save-on" command and wait for it to finish
server_eval_and_wait "$1" "save-on" "${server_confirm_save_on[$1]}"
echo "Done."
else
echo "Server \"${server_name[$1]}\" is not running."
fi
}
# Stops a single server after a delay
# $1: The ID of the server
2012-05-24 01:29:20 +00:00
server_stop() {
if server_is_running "$1"; then
# Change the state of the script
STOP_COUNTDOWN[$id]="true"
server_eval "$id" "say ${server_stop_message[$id]}"
echo "Issued the warning \"${server_stop_message[$id]}\" to players."
echo -n "Shutting down... "
for ((i="${server_stop_delay[$id]}"; i>0; i--)); do
tput sc # Save cursor position
echo -n "in $i seconds."
sleep 1
tput rc # Restore cursor to position of last `sc'
tput el # Clear to end of line
done
echo -e "Now."
server_stop_now "$1"
else
echo "Server \"${server_name[$1]}\" is not running."
fi
}
# Stops a single server right now
# $1: The ID of the server
server_stop_now() {
if server_is_running "$1"; then
server_save_all "$1"
2012-05-24 01:29:20 +00:00
echo -n "Stopping the server... "
2012-05-24 01:29:20 +00:00
server_eval "$1" "stop"
STOP_COUNTDOWN[$1]="false"
RESTART_COUNTDOWN[$1]="false"
server_wait_for_stop "$1"
2012-05-24 01:29:20 +00:00
echo "Done."
# Synchronise all worlds in RAM to disk
server_worlds_to_disk "$1"
2012-05-24 01:29:20 +00:00
else
echo "Server \"${server_name[$1]}\" is not running."
2012-05-24 01:29:20 +00:00
fi
}
# Restarts a single server after a delay
# $1: The ID of the server
server_restart() {
# Restarts the server if it is already running
if server_is_running "$1"; then
# Change the state of the script
RESTART_COUNTDOWN[$id]="true"
server_eval "$id" "say ${server_restart_message[$id]}"
echo "Issued the warning \"${server_restart_message[$id]}\" to players."
echo -n "Restarting... "
for ((i="${server_stop_delay[$id]}"; i>0; i--)); do
tput sc # Save cursor position
echo -n "in $i seconds."
sleep 1
tput rc # Restore cursor to position of last `sc'
tput el # Clear to end of line
done
echo -e "Now."
server_stop_now "$1"
fi
server_start "$1"
}
# Restarts a single server right away
# $1: The ID of the server
server_restart_now() {
# Restarts the server if it is already running
if server_is_running "$1"; then
server_stop_now "$1"
fi
server_start "$1"
}
# List the worlds available for a server
# $1: The ID of the server
server_worlds_list() {
local i="${server_world_offset[$1]}"
local max="$(( $i + ${server_num_worlds[$1]} ))"
2012-05-24 05:55:04 +00:00
# For each of the servers worlds:
while [[ "$i" -lt "$max" ]]; do
if "${world_inram[$i]}"; then
echo "[RAM] ${world_name[$i]}"
else
echo "[DSK] ${world_name[$i]}"
fi
2012-05-24 05:55:04 +00:00
i="$(( $i + 1 ))"
2012-05-24 05:55:04 +00:00
done
}
# Backs up the worlds for a server
# $1: The ID of the server
server_worlds_backup() {
local i="${server_world_offset[$1]}"
local max="$(( $i + ${server_num_worlds[$1]} ))"
2012-05-24 05:55:04 +00:00
# For each of the servers worlds:
while [[ "$i" -lt "$max" ]]; do
world_backup "$i"
i="$(( $i + 1 ))"
done
}
2012-05-24 06:22:52 +00:00
# Moves a servers log into another file, leaving the original log file empty
# $1: The ID of the server
server_log_roll() {
# Moves and Gzips the logfile, a big log file slows down the
# server A LOT (what was notch thinking?)
printf "Rolling server logs... "
if [ -e "${server_log[$1]}" ]; then
file_name="${server_name[$1]}-$(date +%F-%H-%M-%S).log"
as_user "${server_user_name[$1]}" "mkdir -p \"${server_log_archive_path[$1]}\" && cp \"${server_log[$1]}\" \"${server_log_archive_path[$1]}/${file_name}\" && gzip \"${server_log_archive_path[$1]}/${file_name}\""
2012-05-24 06:22:52 +00:00
if [ -e "${server_log_archive_path[$1]}/${file_name}.gz" ]; then
as_user "${server_user_name[$1]}" "cp \"/dev/null\" \"${server_log[$1]}\""
as_user "${server_user_name[$1]}" "echo \"Previous logs can be found at \\\"${server_log_archive_path[$1]}\\\"\" > \"${server_log[$1]}\""
2012-05-24 06:22:52 +00:00
else
echoerr "Failed."
return 1
fi
fi
echo "Done."
}
# Backups a server's directory
# $1: The ID of the server
server_backup() {
echo -n "Backing up the entire server directory... "
zip_flags="-rq"
# Add the "y" flag if symbolic links should not be followed
if [ "${server_complete_backup_follow_symlinks[$1]}" != "true" ]; then
zip_flags="${zip_flags}y"
fi
# Zip up the server directory
file_name="${server_backup_path[$1]}/$(date "+%F-%H-%M-%S").zip"
as_user "${server_user_name[$1]}" "mkdir -p \"${server_backup_path[$1]}\" && cd \"$SERVER_STORAGE_PATH\" && zip ${zip_flags} \"${file_name}\" \"${server_name[$1]}\""
echo "Done."
}
# Sets a server's jar file
# $1: The ID of the server
# $2: The name of the jar group
# $3: Optionally, a specific jar to use.
server_set_jar() {
if [ -e "$JAR_STORAGE_PATH/$2" ]; then
if [ -z "$3" ]; then
# If a specific jar file is not mentioned
# Download the latest version
jargroup_getlatest "$2"
local jar="$(get_latest_file "$JAR_STORAGE_PATH/$2")"
else
# If a specific jar IS mentioned use that
local jar="$JAR_STORAGE_PATH/$2/$3"
if [[ ! -e "$jar" ]]; then
echo "There is no jar named \"$3\" in jargroup \"$2\"."
return 1
fi
fi
if [[ ! -z "$jar" ]]; then
as_user "${server_user[$1]}" "ln -sf \"$jar\" \"${server_jar[$1]}\""
echo "Server \"${server_name[$1]}\" is now using \"$jar\"."
fi
else
echo "There is no jargorup named \"$2\"."
fi
}
# Lists the players currently connected to a server
# $1: The ID of the server
server_connected() {
if server_is_running "$1"; then
local line="$(server_eval_and_get_line "$1" "list" "Connected players:")"
# Cuts the start off the line, and the last three (invisible)
# characters from the end.
local players="${line:46}"
if [ -z "$players" ]; then
echo "No players are connected."
else
echo "$players"
fi
else
echo "Server \"${server_name[$id]}\" is not running. No users are connected."
fi
}
2012-05-29 03:51:03 +00:00
### Manager Functions
# Stops all running servers after a servers specified delay
# $1: String containing "stop" or "restart". Represents whether the stop is
# with a mind to stop the server, or just to restart it. And effects
# the message issued to players on a server.
manager_stop_all_servers() {
# An array of true/false for each server
local was_running
# False if no servers were running at all
local any_running="false"
# For all running servers issue the stop warning
local max_countdown=0
for ((server=0; server<${num_servers}; server++)); do
if server_is_running "$server"; then
2012-05-29 03:51:03 +00:00
any_running="true"
was_running[$server]="true"
STOP_COUNTDOWN[$server]="true"
if [[ "${server_stop_delay[$i]}" -gt "$max_countdown" ]]; then
max_countdown="${server_stop_delay[$server]}"
2012-05-29 03:51:03 +00:00
fi
# Send a warning message to the server
case "$1" in
stop) server_eval "$server" "say ${server_stop_message[$server]}";;
restart) server_eval "$server" "say ${server_restart_message[$server]}";;
2012-05-29 03:51:03 +00:00
esac
# Send message to stdout
echo "Server \"${server_name[$server]}\" was running, now stopping:"
case "$1" in
stop) echo " Issued the warning \"${server_stop_message[$server]}\" to players.";;
restart) echo " Issued the warning \"${server_restart_message[$server]}\" to players.";;
esac
case "${server_stop_delay[$server]}" in
2012-05-29 03:51:03 +00:00
0) echo " Stopping without delay.";;
1) echo " Stopping after 1 second.";;
*) echo " Stopping after ${server_stop_delay[$server]} seconds.";;
esac
else
echo "Server \"${server_name[$server]}\" was NOT running."
was_running[$server]="false"
fi
done
if "$any_running"; then
# Wait for the maximum possible delay, stopping servers
# at the correct times
echo -n "All servers will have been issued the stop command... "
for ((tick="${max_countdown}"; tick>=0; tick--)); do
2012-05-29 03:51:03 +00:00
tput sc # Save cursor position
if [[ "$tick" -le 1 ]]; then
2012-05-29 03:51:03 +00:00
echo -n "in $tick second."
else
echo -n "in $tick seconds."
fi
# Each second check all server, to see if its their time to
# stop. If so issue the stop command, and don't hang.
for ((server=0; server<${num_servers}; server++)); do
if server_is_running "$server"; then
stop_tick="$(( ${max_countdown} - ${server_stop_delay[$server]} ))"
if [[ "$stop_tick" == "$tick" ]]; then
server_eval "$server" "stop"
2012-05-29 03:51:03 +00:00
STOP_COUNTDOWN[$server]="false"
fi
fi
done
if [[ "$tick" > 0 ]]; then
2012-05-29 03:51:03 +00:00
sleep 1
fi
tput rc # Restore cursor to position of last `sc'
tput el # Clear to end of line
done
# Start a new line
echo "Now."
# Finally check all servers have stopped
for ((server=0; server<${num_servers}; server++)); do
if "${was_running[$server]}"; then
echo -n "Ensuring server \"${server_name[$server]}\" has stopped... "
server_wait_for_stop "$server"
2012-05-29 03:51:03 +00:00
echo "Done."
fi
done
else
echo "No servers were running."
fi
}
# Stops all running servers without delay
manager_stop_all_servers_now() {
# An array of true/false for each server
local was_running
# False if no servers were running at all
local any_running="false"
# Stop all servers at the same time
for ((server=0; server<${num_servers}; server++)); do
if server_is_running "$server"; then
2012-05-29 03:51:03 +00:00
was_running[$server]="true"
any_running="true"
echo "Server \"${server_name[$server]}\" was running, now stopping."
server_eval "$server" "stop"
2012-05-29 03:51:03 +00:00
else
echo "Server \"${server_name[$server]}\" was NOT running."
was_running[$server]="false"
fi
done
if "$any_running"; then
2012-05-29 03:51:03 +00:00
# Ensure all the servers have stopped
for ((server=0; server<${num_servers}; server++)); do
if "${was_running[$server]}"; then
2012-05-29 03:51:03 +00:00
echo -n "Ensuring server \"${server_name[$server]}\" has stopped... "
server_wait_for_stop "$server"
2012-05-29 03:51:03 +00:00
echo "Done."
fi
done
else
echo "No servers were running."
fi
}
### Main Functions
# Initialises a server's world
# $1: The server id
# $2: The world id to use
# $3: The name of the world
server_world_init() {
world_server_id[$2]="$1"
world_name[$2]="$3"
world_active_path[$2]="${server_world_storage[$1]}/${world_name[$2]}"
world_inactive_path[$2]="${server_world_storage_inactive[$1]}/${world_name[$2]}"
# Set the status of this world (active/inactive)
if [ -d "${world_active_path[$2]}" ]; then
world_status[$2]="active"
world_path[$2]="${world_active_path[$2]}"
else
if [ -d "${world_inactive_path[$2]}" ]; then
world_status[$2]="inactive"
world_path[$2]="${world_inactive_path[$2]}"
else
world_status[$2]="unknown"
echoerr "Error: World cannot be found in either \"${world_active_path[$2]}\" or \"${world_inactive_path[$2]}\"."
return 1
fi
fi
world_link[$2]="${server_path[$1]}/${world_name[$2]}"
world_flag_inram[$2]="${world_path[$2]}/$WORLD_FLAG_INRAM"
world_backup_path[$2]="$WORLD_ARCHIVE_PATH/${server_name[$1]}/${world_name[$2]}"
# If the ramdisk path is set, get the path for this world
if [ ! -z "$RAMDISK_STORAGE_PATH" ]; then
world_ramdisk_path[$2]="${RAMDISK_STORAGE_PATH}/${server_name[$1]}/${world_name[$2]}"
fi
# Detect whether this world should be in ram
if [[ -e "${world_path[$2]}/$WORLD_FLAG_INRAM" ]]; then
world_inram[$2]="true"
else
world_inram[$2]="false"
fi
}
# Load a config file for a server
# $1: The id of the server to laod
server_load_config() {
local name value
if [[ -f "${server_conf[$1]}" ]]; then
while read line; do
# ignore comment lines
echo "$line" | grep "^#" >/dev/null 2>&1 && continue
# if not empty, set the property using declare
if [ ! -z "$line" ]; then
name="$(echo $line | awk -F '=' '{print $1}')"
value="$(echo $line | awk -F '\"' '{print $2}')"
fi
case "$name" in
SERVER_USER) server_user_name[$1]="$value";;
SCREEN_NAME) server_screen_name[$1]="$value";;
WORLD_STORAGE_PATH) server_world_storage[$1]="${server_path[$1]}/$value";;
WORLD_STORAGE_INACTIVE_PATH) server_world_storage_inactive[$1]="${server_path[$1]}/$value";;
LOG) server_log[$1]="${server_path[$1]}/$value";;
PROPERTIES) server_properties[$1]="$value";;
WHITELIST) server_whitelist[$1]="$value";;
BANNED_PLAYERS) server_banned_players[$1]="$value";;
BANNED_IPS) server_banned_ips[$1]="$value";;
OPS) server_ops[$1]="$value";;
JAR) server_jar[$1]="${server_path[$1]}/$value";;
RAM) server_ram[$1]="$value";;
INVOCATION) server_invocation[$1]="$value";;
STOP_DELAY) server_stop_delay[$1]="$value";;
RESTART_DELAY) server_restart_delay[$1]="$value";;
STOP_MESSAGE) server_stop_message[$1]="$value";;
STOP_ABORT) server_stop_abort[$1]="$value";;
RESTART_MESSAGE) server_restart_message[$1]="$value";;
RESTART_ABORT) server_restart_abort[$1]="$value";;
WORLD_BACKUP_STARTED) server_world_backup_started[$1]="$value";;
WORLD_BACKUP_FINISHED) server_world_backup_finished[$1]="$value";;
COMPLETE_BACKUP_STARTED) server_complete_backup_started[$1]="$value";;
COMPLETE_BACKUP_FINISHED) server_complete_backup_finished[$1]="$value";;
CONFIRM_SAVE_ON) server_confirm_save_on[$1]="$value";;
CONFIRM_SAVE_OFF) server_confirm_save_off[$1]="$value";;
CONFIRM_SAVE_ALL) server_confrim_save_all[$1]="$value";;
CONFIRM_START) server_confrim_start[$1]="$value";;
CONFIRM_WHITELIST_LIST) server_confirm_whitelist_list[$1]="$value";;
CONFIRM_KICK) server_confirm_kick[$1]="$value";;
CONFIRM_KICK_FAIL) server_confirm_kick_fail[$1]="$value";;
CONFIRM_TIME_SET) server_confirm_time_set[$1]="$value";;
CONFIRM_TIME_SET_FAIL) server_confirm_time_set_fail[$1]="$value";;
CONFIRM_TIME_ADD) server_confirm_time_add[$1]="$value";;
CONFIRM_TIME_ADD_FAIL) server_confirm_time_add_fail[$1]="$value";;
CONFIRM_TOGGLEDOWNFALL) server_confirm_toggledownfall[$1]="$value";;
CONFIRM_TOGGLEDOWNFALL_FAIL) server_confirm_toggledownfall_fail[$1]="$value";;
CONFIRM_GAMEMODE) server_confirm_gamemode[$1]="$value";;
CONFIRM_GAMEMODE_FAIL_NO_USER) server_confirm_gamemode_fail_no_user[$1]="$value";;
CONFIRM_GAMEMODE_FAIL_NO_CHANGE) server_confirm_gamemode_fail_no_change[$1]="$value";;
COMPLETE_BACKUP_FOLLOW_SYMLINKS) server_complete_backup_follow_symlinks[$1]="$value";;
esac
done < "${server_conf[$1]}"
fi
}
# Initialise a server's variables
# $1: The id to use for this server
# $2: The name of the server
server_init() {
server_name[$1]="$2"
server_path[$1]="$SERVER_STORAGE_PATH/$2"
server_conf[$1]="${server_path[$1]}/$DEFAULT_SERVER_CONF"
server_flag_active[$1]="${server_path[$1]}/$SERVER_FLAG_ACTIVE"
server_backup_path[$1]="$BACKUP_ARCHIVE_PATH/${server_name[$1]}"
server_log_archive_path[$1]="$LOG_ARCHIVE_PATH/${server_name[$1]}"
if [[ -e "${server_flag_active[$1]}" ]]; then
server_active[$1]="true"
else
server_active[$1]="false"
fi
# Setup defaults
# Note: screen_name will at this stage have the {SERVER_NAME} tag in it
# which needs to be replaced.
# Invocation may also the {RAM} and {JAR} tags.
server_user_name[$1]="$DEFAULT_SERVER_USER"
server_screen_name[$1]="${DEFAULT_SCREEN_NAME//\{SERVER_NAME\}/${server_name[$1]}}" # Replace tags now, they cannot change
server_world_storage[$1]="${server_path[$1]}/$DEFAULT_WORLD_STORAGE_PATH"
server_world_storage_inactive[$1]="${server_path[$1]}/$DEFAULT_WORLD_STORAGE_INACTIVE_PATH"
server_log[$1]="${server_path[$1]}/$DEFAULT_LOG"
server_properties[$1]="${server_path[$1]}/$DEFAULT_PROPERTIES"
server_whitelist[$1]="${server_path[$1]}/$DEFAULT_WHITELIST"
server_banned_players[$1]="${server_path[$1]}/$DEFAULT_BANNED_PLAYERS"
server_banned_ips[$1]="${server_path[$1]}/$DEFAULT_BANNED_IPS"
server_ops[$1]="${server_path[$1]}/$DEFAULT_OPS"
server_jar[$1]="${server_path[$1]}/$DEFAULT_JAR"
server_ram[$1]="$DEFAULT_RAM"
server_invocation[$1]="$DEFAULT_INVOCATION" # Don't replace tags yet, they may change
server_stop_delay[$1]="$DEFAULT_STOP_DELAY"
server_restart_delay[$1]="$DEFAULT_RESTART_DELAY"
server_stop_message[$1]="$DEFAULT_STOP_MESSAGE"
server_stop_abort[$1]="$DEFAULT_STOP_ABORT"
server_restart_message[$1]="$DEFAULT_RESTART_MESSAGE"
server_restart_abort[$1]="$DEFAULT_RESTART_ABORT"
server_world_backup_started[$1]="$DEFAULT_WORLD_BACKUP_STARTED"
server_world_backup_finished[$1]="$DEFAULT_WORLD_BACKUP_FINISHED"
server_complete_backup_started[$1]="$DEFAULT_COMPLETE_BACKUP_STARTED"
server_complete_backup_finished[$1]="$DEFAULT_COMPLETE_BACKUP_FINISHED"
server_confirm_save_on[$1]="$DEFAULT_CONFIRM_SAVE_ON"
server_confirm_save_off[$1]="$DEFAULT_CONFIRM_SAVE_OFF"
server_confirm_save_all[$1]="$DEFAULT_CONFIRM_SAVE_ALL"
server_confirm_start[$1]="$DEFAULT_CONFIRM_START"
server_confirm_kick[$1]="$DEFAULT_CONFIRM_KICK"
server_confirm_kick_fail[$1]="$DEFAULT_CONFIRM_KICK_FAIL"
server_confirm_time_set[$1]="$DEFAULT_CONFIRM_TIME_SET"
server_confirm_time_set_fail[$1]="$DEFAULT_CONFIRM_TIME_SET_FAIL"
server_confirm_time_add[$1]="$DEFAULT_CONFIRM_TIME_ADD"
server_confirm_time_add_fail[$1]="$DEFAULT_CONFIRM_TIME_ADD_FAIL"
server_confirm_toggledownfall[$1]="$DEFAULT_CONFIRM_TOGGLEDOWNFALL"
server_confirm_toggledownfall_fail[$1]="$DEFAULT_CONFIRM_TOGGLEDOWNFALL_FAIL"
server_confirm_gamemode[$1]="$DEFAULT_CONFIRM_GAMEMODE"
server_confirm_gamemode_fail_no_user[$1]="$DEFAULT_CONFIRM_GAMEMODE_FAIL_NO_USER"
server_confirm_gamemode_fail_no_change[$1]="$DEFAULT_CONFIRM_GAMEMODE_FAIL_NO_CHANGE"
server_complete_backup_follow_symlinks[$1]="$DEFAULT_COMPLETE_BACKUP_FOLLOW_SYMLINKS"
# Load config overrides from server config file if present
server_load_config "$1"
# Replace tags in delay messages
server_stop_message[$1]="${server_stop_message[$1]//\{DELAY\}/${server_stop_delay[$1]}}"
server_restart_message[$1]="${server_restart_message[$1]//\{DELAY\}/${server_restart_delay[$1]}}"
# Replace tags in server invocation
server_invocation[$1]="${server_invocation[$1]//\{RAM\}/${server_ram[$1]}}"
server_invocation[$1]="${server_invocation[$1]//\{JAR\}/${server_jar[$1]}}"
# Load worlds if there is a world storage directory present
server_world_offset[$1]=0
server_num_worlds[$1]=0
# Start world id's for this server's worlds at the end of the array
local id="$num_worlds"
# Record the index at which worlds for this server start
server_world_offset[$1]="$id"
if [[ -d "${server_world_storage[$1]}" ]]; then
# Load active worlds
while IFS= read -r -d $'\0' path; do
local name="$(basename "$path")"
server_world_init "$1" "$id" "$name"
# Build the server_worlds comma separated list
if [[ "$id" == "${server_world_offset[$1]}" ]]; then
server_worlds[$1]="$name"
else
server_worlds[$1]="${server_worlds[$1]}, $name"
fi
id="$(($id+1))"
num_worlds="$id"
done < <(find "${server_world_storage[$1]}" -mindepth 1 -maxdepth 1 -type d -print0)
fi
if [[ -d "${server_world_storage_inactive[$1]}" ]]; then
# Load inactive worlds
while IFS= read -r -d $'\0' path; do
local name="$(basename "$path")"
server_world_init "$1" "$id" "$name"
# Build the server_worlds_inactive comma separated list
if [[ "$id" == "${server_world_offset[$1]}" ]]; then
server_worlds[$1]="$name"
else
server_worlds[$1]="${server_worlds[$1]}, $name"
fi
id="$(($id+1))"
num_worlds="$id"
done < <(find "${server_world_storage_inactive[$1]}" -mindepth 1 -maxdepth 1 -type d -print0)
fi
# Record the number of worlds this server has
server_num_worlds[$1]="$(( $id - ${server_world_offset[$1]} ))"
}
# Asserts that a variable has been set in the config file
# $1: The name of the variable to test
# $2: The message to print to stderr if the variable is unset
assert_is_set_in_config() {
[[ ! ${!1} && ${!1-_} ]] && {
echoerr "Error: $1 must be set in $CONF"
exit 1
}
}
# Ensures all non-optional settings have been set
init_ensure_settings() {
assert_is_set_in_config USERNAME
assert_is_set_in_config SERVER_STORAGE_PATH
assert_is_set_in_config JAR_STORAGE_PATH
assert_is_set_in_config RAMDISK_STORAGE_PATH
assert_is_set_in_config WORLD_ARCHIVE_PATH
assert_is_set_in_config LOG_ARCHIVE_PATH
assert_is_set_in_config BACKUP_ARCHIVE_PATH
assert_is_set_in_config DEFAULT_SERVER_CONF
assert_is_set_in_config DEFAULT_SERVER_USER
assert_is_set_in_config DEFAULT_SCREEN_NAME
assert_is_set_in_config DEFAULT_WORLD_STORAGE_PATH
assert_is_set_in_config DEFAULT_WORLD_STORAGE_INACTIVE_PATH
assert_is_set_in_config DEFAULT_COMPLETE_BACKUP_FOLLOW_SYMLINKS
assert_is_set_in_config DEFAULT_LOG
assert_is_set_in_config DEFAULT_PROPERTIES
assert_is_set_in_config DEFAULT_WHITELIST
assert_is_set_in_config DEFAULT_BANNED_PLAYERS
assert_is_set_in_config DEFAULT_BANNED_IPS
assert_is_set_in_config DEFAULT_OPS
assert_is_set_in_config DEFAULT_JAR
assert_is_set_in_config DEFAULT_RAM
assert_is_set_in_config DEFAULT_INVOCATION
assert_is_set_in_config DEFAULT_STOP_DELAY
assert_is_set_in_config DEFAULT_RESTART_DELAY
assert_is_set_in_config DEFAULT_STOP_MESSAGE
assert_is_set_in_config DEFAULT_STOP_ABORT
assert_is_set_in_config DEFAULT_RESTART_MESSAGE
assert_is_set_in_config DEFAULT_RESTART_ABORT
assert_is_set_in_config DEFAULT_WORLD_BACKUP_STARTED
assert_is_set_in_config DEFAULT_WORLD_BACKUP_FINISHED
assert_is_set_in_config DEFAULT_COMPLETE_BACKUP_STARTED
assert_is_set_in_config DEFAULT_COMPLETE_BACKUP_FINISHED
assert_is_set_in_config DEFAULT_CONFIRM_SAVE_ON
assert_is_set_in_config DEFAULT_CONFIRM_SAVE_OFF
assert_is_set_in_config DEFAULT_CONFIRM_SAVE_ALL
assert_is_set_in_config DEFAULT_CONFIRM_START
assert_is_set_in_config DEFAULT_CONFIRM_KICK
assert_is_set_in_config DEFAULT_CONFIRM_KICK_FAIL
assert_is_set_in_config DEFAULT_CONFIRM_TIME_SET
assert_is_set_in_config DEFAULT_CONFIRM_TIME_SET_FAIL
assert_is_set_in_config DEFAULT_CONFIRM_TIME_ADD
assert_is_set_in_config DEFAULT_CONFIRM_TIME_ADD_FAIL
assert_is_set_in_config DEFAULT_CONFIRM_TOGGLEDOWNFALL
assert_is_set_in_config DEFAULT_CONFIRM_TOGGLEDOWNFALL_FAIL
assert_is_set_in_config DEFAULT_CONFIRM_GAMEMODE
assert_is_set_in_config DEFAULT_CONFIRM_GAMEMODE_FAIL_NO_USER
assert_is_set_in_config DEFAULT_CONFIRM_GAMEMODE_FAIL_NO_CHANGE
}
init() {
# Sourcing $CONF will override the previous defaults, with new values
source "$CONF"
init_ensure_settings
num_worlds=0
num_servers=0
if [ -d "$SERVER_STORAGE_PATH" ]; then
local id=0
while IFS= read -r -d $'\0' path; do
local name="$(basename "$path")"
server_init "$id" "$name"
id="$(($id+1))"
num_servers="$id"
done < <(find "$SERVER_STORAGE_PATH" -mindepth 1 -maxdepth 1 -type d -print0)
fi
}
# Called if the script is interrupted before exiting naturally
interrupt() {
local exit_message="false"
for ((i=0; $i<$num_servers; i++)); do
if [[ "${STOP_COUNTDOWN[$i]}" == "true" ]] && server_is_running "$i"; then
if [[ "$exit_message" == "false" ]]; then
echo -e "\nInterrupted..."
exit_message="true"
fi
server_eval "$i" "say ${server_stop_abort[$i]}"
echo "Server \"${server_name[$i]}\" shutdown was aborted."
fi
if [[ "${RESTART_COUNTDOWN[$i]}" == "true" ]] && server_is_running "$i"; then
if [[ "$exit_message" == "false" ]]; then
echo -e "\nInterrupted..."
exit_message="true"
fi
server_eval "$i" "say ${server_restart_abort[$i]}"
echo "Server \"${server_name[$i]}\" restart was aborted."
fi
done
exit
}
# The main function which starts the script
main() {
# Initialise variables that represent system state
init
# Trap interrupts to the script by calling the interrupt function
trap interrupt EXIT
case "$1" in
start)
# Required start option, for debian init.d scripts
2012-05-29 03:51:03 +00:00
for ((server=0; server<${num_servers}; server++)); do
# Only starts active servers
if "${server_active[$server]}"; then
if server_is_running "$server"; then
2012-05-29 03:51:03 +00:00
echo "[ACTIVE] Server \"${server_name[$server]}\" already started."
else
2012-05-29 03:51:03 +00:00
echo "[ACTIVE] Server \"${server_name[$server]}\" starting:"
server_start "$server"
fi
else
if server_is_running "$server"; then
2012-05-29 03:51:03 +00:00
echo "[INACTIVE] Server \"${server_name[$server]}\" already started. It should not be running! Use \"$0 ${server_name[$server]} stop\" to stop this server."
else
2012-05-29 03:51:03 +00:00
echo "[INACTIVE] Server \"${server_name[$server]}\" leaving stopped, as this server is inactive."
fi
fi
done
;;
stop)
if [ "$2" != "now" ]; then
# If the the now flag is not specified
2012-05-29 03:51:03 +00:00
manager_stop_all_servers "stop"
else
# If the now flag is specified
2012-05-29 03:51:03 +00:00
manager_stop_all_servers_now
fi
;;
restart)
# Required restart option, for debian init.d scripts
2012-05-29 03:51:03 +00:00
# Stop any running servers, now if specified
echo "Stopping servers:"
2012-05-29 03:51:03 +00:00
main "stop" "$2"
2012-05-29 03:51:03 +00:00
echo "Starting servers:"
main "start"
;;
2012-06-02 20:02:58 +00:00
version)
echo "Minecraft Server Manager $VERSION"
;;
server)
case "$2" in
list)
# Lists the existing servers
server_list
;;
create)
if [ -z "$3" ]; then
# If a server name is not provided
echo "Invalid command."
else
# Create a new server
server_create "$3"
fi
;;
delete)
if [ -z "$3" ]; then
# If a server name is not provided
echo "Invalid command."
else
# Delete an existing server, with confirmation
server_delete "$3"
fi
;;
rename)
if [ -z "$3" ] || [ -z "$4" ]; then
# If a server name is not provided
echo "Invalid command."
else
# Rename an existing server
server_rename "$3" "$4"
fi
;;
*)
# "server" is not a valid command
echo "Invalid command."
;;
esac
;;
jargroup)
case "$2" in
list)
# Lists the jars grouped by jargroup
jargroup_list
;;
create)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "Invlaid command."
else
jargroup_create "$3" "$4"
fi
;;
delete)
if [ -z "$3" ]; then
echo "Invalid command."
else
jargroup_delete "$3"
fi
;;
rename)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "Invalid command."
else
jargroup_rename "$3" "$4"
fi
;;
changetarget)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "Invalid command."
else
jargroup_settarget "$3" "$4"
fi
;;
getlatest)
if [ -z "$3" ]; then
echo "Invalid command."
else
jargroup_getlatest "$3"
fi
;;
*)
# "jargroup" is not a valid command
echo "Invalid command."
;;
esac
;;
help)
# Outputs a list of all commands
echo -e "Usage: $0 command:"
echo -e
echo -e "--Setup Commands------------------------------------------------"
echo -e " server list List servers"
echo -e " server create <name> Creates a new Minecraft server"
echo -e " server delete <name> Deletes an existing Minecraft server"
echo -e " server rename <name> <new-name> Renames an existing Minecraft server"
echo -e
echo -e "--Server Mangement Commands-------------------------------------"
echo -e " <server> start Starts a server"
echo -e " <server> stop [now] Stops a server after warning players, or right now"
echo -e " <server> restart [now] Restarts a server after warning players, or right now"
echo -e " <server> status Show the running/stopped status of a server"
echo -e " <server> connected List a servers connected players"
echo -e " <server> worlds list Lists the worlds a server has"
echo -e " <server> worlds load Creates links to worlds in storage for a server"
echo -e " <server> worlds ram <world> Toggles a world's \"in RAM\" status"
echo -e " <server> worlds todisk Synchronises any \"in RAM\" worlds to disk a server has"
echo -e " <server> worlds backup Makes a backup of all worlds a server has"
echo -e " <server> worlds on|off <world> Activate or deactivate a world, inactive worlds are not backed up"
echo -e " <server> logroll Move a server log to a gziped archive, to reduce lag"
echo -e " <server> backup Makes a backup of an entire server directory"
echo -e " <server> jar <jargroup> [<file>] Sets a server's jar file"
echo -e
echo -e "--Server Pass Through Commands----------------------------------"
echo -e " <server> wl on|off Enables/disables server whitelist checking"
echo -e " <server> wl add|remove <player> Add/remove a player to/from a server's whitelist"
echo -e " <server> wl list List the players whitelisted for a server"
echo -e " <server> bl player add|remove <player> Ban/pardon a player from/for a server"
echo -e " <server> bl ip add|remove <ip address> Ban/pardon an IP address from/for a server"
echo -e " <server> bl list Lists the banned players and IP address for a server"
echo -e " <server> op add|remove <player> Add/remove operator status for a player on a server"
echo -e " <server> op list Lists the operator players for a server"
echo -e " <server> gm survival|creative <player> Change the game mode for a player on a server"
echo -e " <server> kick <player> Forcibly disconnect a player from a server"
echo -e " <server> say <message> Broadcast a (pink) message to all players on a server"
echo -e " <server> time set|add <number> Set/increment time on a server (0-24000)"
echo -e " <server> toggledownfall Toggles rain and snow on a server"
echo -e " <server> save on|off Enable/disable writing world changes to file"
echo -e " <server> save all Force the writing of all non-saved world changes to file"
echo -e " <server> cmd <command> Send a command string to the server and return"
echo -e " <server> cmdlog <command> Same as 'cmd' but shows log output afterwards (Ctrl+C to exit)"
echo -e
echo -e "--Jar Commands--------------------------------------------------"
echo -e " jargroup list List the stored jar files."
echo -e " jargroup create <name> <download-url> Create a new jar group, with a URL for new downloads"
echo -e " jargroup delete <name> Delete a jar group"
echo -e " jargroup rename <name> <new-name> Rename a jar group"
echo -e " jargroup changeurl <name> <download-url> Change the download URL for a jar group"
echo -e " jargroup getlatest <name> Download the latest jar file for a jar group"
echo -e
echo -e "--Global Commands-----------------------------------------------"
echo -e " start Starts all active servers"
echo -e " stop [now] Stops all running servers"
echo -e " restart [now] Restarts all active servers"
2012-06-02 20:02:58 +00:00
echo -e " version Prints the Minecraft Server Manager version installed8"
;;
"")
echo "No such command see: $0 help"
;;
*)
if [[ "$1" == "all" ]] || [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
2012-05-24 01:29:20 +00:00
local id="$(server_get_id "$1")"
2012-05-24 01:29:20 +00:00
case "$2" in
start)
server_set_active "$id" "active"
server_start "$id"
;;
stop)
server_set_active "$id" "inactive"
if [[ "$3" != "now" ]]; then
server_stop "$id"
2012-05-24 01:29:20 +00:00
else
server_stop_now "$id"
2012-05-24 01:29:20 +00:00
fi
;;
restart)
server_set_active "$1" "active"
if [[ "$3" != "now" ]]; then
server_restart "$id"
else
server_restart_now "$id"
fi
;;
status)
if server_is_running "$id"; then
2012-05-24 03:33:57 +00:00
echo "Server \"${server_name[$id]}\" is running."
else
echo "Server \"${server_name[$id]}\" is stopped."
fi
;;
connected)
server_connected "$id"
;;
worlds)
case "$3" in
list)
server_worlds_list "$id"
;;
load)
server_ensure_links "$id"
;;
ram)
if [ -z "$4" ]; then
echo "Invalid command."
else
world_id="$(server_world_get_id "$id" "$4")"
if [ ! -z "$world_id" ]; then
world_toggle_ramdisk_state "$world_id"
else
echo "Server \"${server_name[$id]}\" has no world with that name."
fi
fi
;;
todisk)
server_save_off "$id"
server_save_all "$id"
server_worlds_to_disk "$id"
server_save_on "$id"
;;
backup)
if server_is_running "$id"; then
server_eval "$id" "say ${server_world_backup_started[$id]}"
server_save_off "$id"
server_save_all "$id"
fi
server_worlds_to_disk "$id"
server_worlds_backup "$id"
if server_is_running "$id"; then
server_save_on "$id"
server_eval "$id" "say ${server_world_backup_finished[$id]}"
fi
echo "Backup took $SECONDS seconds".
;;
on)
if [ -z "$4" ]; then
echo "Invalid command."
else
world_activate "$(server_world_get_id "$id" "$4")"
fi
;;
off)
if [ -z "$4" ]; then
echo "Invalid command."
else
world_deactivate "$(server_world_get_id "$id" "$4")"
fi
;;
*)
echo "Invalid command."
;;
esac
;;
logroll)
server_log_roll "$id"
;;
backup)
if server_is_running "$id"; then
server_eval "$id" "say ${server_complete_backup_started[$id]}"
server_save_off "$id"
server_save_all "$id"
fi
server_worlds_to_disk "$id"
server_backup "$id"
if server_is_running "$id"; then
server_save_on "$id"
server_eval "$id" "say ${server_complete_backup_finished[$id]}"
fi
echo "Backup took $SECONDS seconds".
;;
jar)
if [ -z "$3" ]; then
echo "Invalid command."
else
server_set_jar "$id" "$3" "$4"
fi
;;
whitelist|wl)
case "$3" in
on)
if server_is_running "$id"; then
server_eval "$id" "whitelist on"
echo "Whitelist enabled"
else
2012-05-29 04:23:24 +00:00
echo "Server \"${server_name[$id]}\" is not running."
fi
;;
off)
if server_is_running "$id"; then
server_eval "$id" "whitelist off"
echo "Whitelist disabled"
else
2012-05-29 04:23:24 +00:00
echo "Server \"${server_name[$id]}\" is not running."
fi
;;
add)
if [ -z "$4" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
server_eval "$id" "whitelist add $4"
echo "Added \"$4\" to the whitelist."
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
remove)
2012-05-29 04:41:15 +00:00
if [ -z "$4" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
server_eval "$id" "whitelist remove $4"
2012-05-29 04:41:15 +00:00
echo "Removed \"$4\" from the whitelist."
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
list)
local players="$(cat "${server_whitelist[$id]}")"
2012-05-29 04:23:24 +00:00
if [ -z "$players" ]; then
echo "No players are whitelisted."
2012-05-29 04:23:24 +00:00
else
echo "$players"
2012-05-29 04:23:24 +00:00
fi
;;
*)
echo "Invalid command."
;;
esac
;;
blacklist|bl)
case "$3" in
player)
if [ -z "$5" ]; then
echo "Invalid command."
else
case "$4" in
add)
for player in ${*:5}; do
server_eval "$id" "ban $player"
done
if [[ $# -gt 5 ]]; then
echo -n "Blacklisted the following players: "
echo -n "$5"
for player in ${*:6}; do
echo -n ", $player"
done
echo "."
else
echo "Blacklisted \"$5\"."
fi
;;
remove)
for player in ${*:5}; do
server_eval "$id" "pardon $player"
done
if [[ $# -gt 5 ]]; then
echo -n "Removed the following players from the blacklist: "
echo -n "$5"
for player in ${*:6}; do
echo -n ", $player"
done
echo "."
else
echo "Removed \"$5\" from the blacklist."
fi
;;
*)
echo "Invalid command."
;;
esac
fi
;;
ip)
case "$4" in
add)
for address in ${*:5}; do
server_eval "$id" "ban-ip $address"
done
if [[ $# > 5 ]]; then
echo -n "Blacklisted the following ip addresses: "
echo -n "$5"
for player in ${*:6}; do
echo -n ", $address"
done
echo "."
else
echo "Blacklisted \"$5\"."
fi
;;
remove)
for address in ${*:5}; do
server_eval "$id" "pardon-ip $address"
done
if [[ $# > 5 ]]; then
echo -n "Removed the following ip addresses from the blacklist: "
echo -n "$5"
for player in ${*:6}; do
echo -n ", $address"
done
echo "."
else
echo "Removed \"$5\" from the ip blacklist."
fi
;;
*)
echo "Invalid command."
;;
esac
;;
list)
local players="$(cat "${server_banned_players[$id]}")"
local ips="$(cat "${server_banned_ips[$id]}")"
if [[ -z "$players" && -z "$ips" ]]; then
echo "The blacklist is empty."
else
if [[ ! -z "$players" ]]; then
echo "Players:"
for name in $players; do
echo " $name"
done
fi
if [[ ! -z "$ips" ]]; then
echo "IP Addresses:"
for address in $ips; do
echo " $address"
done
fi
fi
;;
*)
echo "Invalid command."
;;
esac
;;
operator|op)
case "$3" in
add)
if server_is_running "$id"; then
server_eval "$id" "op $4"
echo "The player \"$4\" is now an operator for the server."
else
echo "Server \"${server_name[$id]}\" is not running."
fi
;;
remove)
if server_is_running "$id"; then
server_eval "$id" "deop $4"
echo "The player \"$4\" is no longer an operator for the server."
else
echo "Server \"${server_name[$id]}\" is not running."
fi
;;
2012-05-31 04:46:16 +00:00
list)
local players="$(cat "${server_ops[$id]}")"
2012-05-31 04:46:16 +00:00
if [ -z "$players" ]; then
echo "No players are operators."
else
echo "$players"
fi
;;
*)
echo "Invalid command"
;;
esac
;;
gamemode|gm)
case "$3" in
survival|creative|0|1)
if [ -z "$4" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
case "$3" in
creative|1) local mode=1;;
survival|0) local mode=0;;
*) echoerr "Invalid mode"; exit 1;;
esac
local line="$(server_eval_and_get_line "$id" "gamemode $4 $mode" "${server_confirm_gamemode[$id]}" "${server_confirm_gamemode_fail_no_user[$id]}" "${server_confirm_gamemode_fail_no_change[$id]}")"
local regex="${LOG_REGEX} ${server_confirm_gamemode[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "Changed game mode of \"$4\" to \"$3\"."
fi
local regex="${LOG_REGEX} ${server_confirm_gamemode_fail_no_user[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "The player \"$4\" was not found to be logged on."
fi
local regex="${LOG_REGEX} ${server_confirm_gamemode_fail_no_change[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "The player \"$4\" was already in mode \"$3\"."
fi
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
*)
echo "Invalid command."
;;
esac
;;
kick)
if [ -z "$3" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
local line="$(server_eval_and_get_line "$id" "kick $3" "${server_confirm_kick[$id]}" "${server_confirm_kick_fail[$id]}")"
local regex="${LOG_REGEX} ${server_confirm_kick[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "Kicked \"$3\" from game."
fi
local regex="${LOG_REGEX} ${server_confirm_kick_fail[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "The player \"$3\" is not connected."
fi
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
say)
if [ -z "$3" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
server_eval "$id" "say ${*:3}"
echo "Message sent to players."
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
time)
case "$3" in
set)
if [ -z "$4" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
local line="$(server_eval_and_get_line "$id" "time set $4" "${server_confirm_time_set[$id]}" "${server_confirm_time_set_fail[$id]}")"
local regex="${LOG_REGEX} ${server_confirm_time_set[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "Set time to \"$4\"."
fi
local regex="${LOG_REGEX} ${server_confirm_time_set_fail[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "Unable to convert \"$4\" to a time."
fi
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
add)
if [ -z "$4" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
local line="$(server_eval_and_get_line "$id" "time add $4" "${server_confirm_time_add[$id]}" "${server_confirm_time_add_fail[$id]}")"
local regex="${LOG_REGEX} ${server_confirm_time_add[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "Added \"$4\" to time."
fi
local regex="${LOG_REGEX} ${server_confirm_time_add_fail[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "Unable to convert \"$4\" to a time."
fi
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
*)
echo "Invalid command."
;;
esac
;;
toggledownfall|tdf)
if server_is_running "$id"; then
local line="$(server_eval_and_get_line "$id" "toggledownfall $3" "${server_confirm_toggledownfall[$id]}" "${server_confirm_toggledownfall_fail[$id]}")"
local regex="${LOG_REGEX} ${server_confirm_toggledownfall[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "${line:36:(-3)}"
fi
local regex="${LOG_REGEX} ${server_confirm_toggledownfall_fail[$id]}"
if [[ "$line" =~ $regex ]]; then
echo "${line:34:(-3)}"
fi
else
echo "Server \"${server_name[$id]}\" is not running."
fi
;;
save)
case "$3" in
on)
server_save_on "$id"
;;
off)
server_save_off "$id"
;;
all)
server_save_all "$id"
;;
*)
echo "Invalid command."
;;
esac
;;
cmd)
if [ -z "$3" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
server_eval "$id" "${*:3}"
echo "Command sent."
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
cmdlog)
if [ -z "$3" ]; then
echo "Invalid command."
else
if server_is_running "$id"; then
server_eval "$id" "${*:3}"
echo "Now watching logs (press Ctrl+C to exit):"
as_user "${server_user_name[$id]}" "tail --follow --lines=0 --sleep-interval=0.1 ${server_log[$id]}"
else
echo "Server \"${server_name[$id]}\" is not running."
fi
fi
;;
console)
if server_is_running "$id"; then
as_user "${server_user_name[$1]}" "screen -r ${server_screen_name[$1]}"
else
echo "Server \"${server_name[$id]}\" is not running."
fi
;;
*)
echo "Invalid command."
;;
esac
else
echo "No server with that name."
fi
;;
esac
}
### Start point
main "$@"
exit 0