From 0b921a09f5cf1992a8618fdd128ac509f5031a2d Mon Sep 17 00:00:00 2001 From: Marcus Whybrow Date: Thu, 24 May 2012 01:09:05 +0100 Subject: [PATCH] Implemented server start, global config, server config and init function Sorry for the monster commit, was all necessary to get server start to work. --- manager.config | 116 ------- msm | 915 ++++++++++++++++++++++++++++++++++--------------- msm.conf | 142 +++++++- 3 files changed, 771 insertions(+), 402 deletions(-) delete mode 100644 manager.config diff --git a/manager.config b/manager.config deleted file mode 100644 index 5737db3..0000000 --- a/manager.config +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash -# /opt/minecraft/server2/manager.config - - -### Important Names - -# A short name representation for this server, used for consistent directory names: -SERVER_NAME="server2" - -# Name to use for the screen instance (must be unique system wide): -SCREEN_NAME="minecraft2" - -# User that should start and interact with the server -SERVER_USER="minecraft" - - -### Server Directories - -# Path to minecraft directory: -SERVER_PATH="/opt/minecraft/${SERVER_NAME}" - -# Where the worlds are located on the disk. Can not be the same as SERVER_PATH: -WORLD_STORAGE_PATH="${SERVER_PATH}/worldstorage" - -# Path to plugin folder: -SERVER_PLUGIN_PATH="${SERVER_PATH}/plugins" - - -### Backups and Archives - -# When a snapshot (backup) is created of a world it will be stored in this directory -WORLD_SNAPSHOT_PATH="/mnt/Drobo/Public/Shares/Backups/Minecraft/WorldBackups/${SERVER_NAME}" - -# When the server log is compressed and archived, it is stored here: -LOG_ARCHIVE_PATH="/mnt/Drobo/Public/Shares/Backups/Minecraft/Logs/${SERVER_NAME}" - -# When a complete backup of the entire server directory is make, it is stored here: -COMPLETE_BACKUP_PATH="/mnt/Drobo/Public/Shares/Backups/Minecraft/WholeBackups/${SERVER_NAME}" - -# Symbolic links usually point to large or centralised files which should not be -# included in a complete backup of this singular server. -# options: true, false -COMPLETE_BACKUP_FOLLOW_SYMLINKS="false" - -# When a backup of Bukkit plugin config files is made, it is stored here: -CONFIG_BACKUP_PATH="/mnt/Drobo/Public/Shares/Backups/Minecraft/ConfigBackups/${SERVER_NAME}" - -# The pattern used to search for config files within the plugin directory -CONFIG_BACKUP_PATTERN="*.yml" - - -### Speedy Ramdisk - -# Path to a directory located in the mounted ramdisk. -# The default mount point in Ubuntu is: /dev/shm -RAMDISK_PATH="/dev/shm/Minecraft/$SERVER_NAME" - - -### Jar configuration - -# Location of the server jar to run (minecraft_server.jar, bukkit.jar etc.): -JAR="${SERVER_PATH}/server.jar" - -# The amount of memory the server will use when started: -INITAL_MEMORY="2048M" - -# The maximum amount of memory the server can use at any time: -MAXIMUM_MEMORY="2048M" - -# Number of CPUs/cores to use at runtime: -CPU_COUNT=2 - -# The command which launches the Minecraft server using the variables -# provided thus far: -INVOCATION="java -Xmx${MAXIMUM_MEMORY} -Xms${INITAL_MEMORY} -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalPacing -XX:ParallelGCThreads=${CPU_COUNT} -XX:+AggressiveOpts -jar ${JAR} nogui" - - -### Configuration - -# The amount of time (in seconds) between broadcasting to players -# the message MESSAGE_SERVER_STOP_WARNING (see below) and the server actually -# shutting down: -CONFIG_SERVER_STOP_DELAY=10 - -CONFIG_SERVER_RESTART_DELAY=10 - -CONFIG_SERVER_LOG_ROLL_RESTART_DELAY=10 - - -### Messages - -# The message displayed to players when gracefully shutting down the server. -# The server shuts down (after this message is broadcast) following a delay -# specified in CONFIG_SERVER_STOP_DELAY -MESSAGE_SERVER_STOP_WARNING="SERVER SHUTTING DOWN IN 10 SECONDS!" - -MESSAGE_SERVER_RESTART_WARNING="SERVER REBOOT IN 10 SECONDS!" - -MESSAGE_SERVER_LOG_ROLL_RESTART_WARNING="ROUTINE REBOOT IN 10 SECONDS!" - -MESSAGE_SERVER_WORLDS_BACKUP_STARTED="Backing up world." -MESSAGE_SERVER_WORLDS_BACKUP_FINISHED="Backup complete." - -MESSAGE_SERVER_COMPLETE_BACKUP_STARTED="Backing up entire server." -MESSAGE_SERVER_COMPLETE_BACKUP_FINISHED="Backup complete." - - -### Stuff that probably wont need changing - -# The location of the standard Minecraft log file -SERVER_LOG="${SERVER_PATH}/server.log" - -CONFIRMATIONS_SAVE_ON="CONSOLE: Enabling level saving.." -CONFIRMATIONS_SAVE_OFF="CONSOLE: Disabling level saving.." -CONFIRMATIONS_SAVE_ALL="CONSOLE: Save complete." -CONFIRMATIONS_START="Done" \ No newline at end of file diff --git a/msm b/msm index 6b86fe3..50184f7 100755 --- a/msm +++ b/msm @@ -35,15 +35,22 @@ 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" + ### Utility Functions -# Executes the command "$1" as SERVER_USER +# Executes the command "$2" as user "$1" +# $1: The user to execute the command as +# $2: The command to execute as_user() { - if [ $(whoami) == $SERVER_USER ] ; then - bash -c "$1" + if [ $(whoami) == $1 ]; then + bash -c "$2" else - su - $SERVER_USER -s /bin/bash -c "$1" + su - $1 -s /bin/bash -c "$2" fi } @@ -103,91 +110,170 @@ get_latest_file() { echo $best_file } +# Returns the current time as a UNIX timestamp (in seconds since 1970) +now() { + date +%s +} -### Server Functions -# Echos a list of servers in the SERVER_STORAGE_PATH -server_list() { - echo "Servers:" - for server in "$SERVER_STORAGE_PATH/*"; do - echo " $(basename $server)" +### 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 +} + + +### World Utility Functions + +# Moves a world to RAM +# $1: the ID of the world to move +world_to_ram() { + if $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 +} + + +### 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 +server_get_id() { + local i=0 + while [[ $i < $num_servers ]]; do + if [[ "${server_name[$i]}" == "$1" ]]; then + echo "$i" + return 0 + fi + + i=$(( $i + 1 )) + done + + return 1 +} + +# Returns 0 if the server $1 is running and 1 if not +# $1: The name of server +server_is_running() { + local id=$(server_get_id "$1") + + if ps ax | grep -v grep | grep "${server_screen_name[$i]} ${server_invocation[$i]}" > /dev/null + then + return 0 + else + return 1 + 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() { + local i=${server_world_offset[$1]} + local max=$(( $i + ${server_num_worlds[$1]} )) + + while [[ $i < $max ]]; do + # -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 "rm -f \"${world_link[$i]}\"" + + # Create a new symbolic link pointing to the RAM version of the world + as_user "ln -s \"${world_ramdisk_path[$i]}\" \"${world_link[$i]}\"" + fi + 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 "rm -f \"${world_link[$i]}\"" + + # Create a new symbolic link pointing to the disk version of the world + as_user "ln -s \"${world_path[$i]}\" \"${world_link[$i]}\"" + fi + fi + else + # There was no symbolic link, and there was a directory, meaning a world + # directory has been placed in SERVER_PATH, this is not allowed: + echoerr "Could not process ${world_name[$i]}. Please move all worlds to ${server_world_storage[$1]}." + return 1 + fi + + i=$(( $i + 1 )) done } -# 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 "mkdir -p '$SERVER_STORAGE_PATH/$1'" - as_user "touch '$SERVER_STORAGE_PATH/$1/$WHITELIST'" - as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_IPS'" - as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_PLAYERS'" - echo "Done." - fi - else - return 1 +# 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 $RAMDISK_STORAGE_PATH; then + local i=${server_world_offset[$1]} + local max=$(( $i + ${server_num_worlds[$1]} )) + + # For each of the servers worlds: + while [[ $i < $max ]]; do + if ${world_inram[$i]} && [ -L ${world_link[$i]} ]; then + world_to_ram $i + fi + + i=$(( $i + 1 )) + done fi } -# Deletes an existing server -# $2: The server name to delete -server_delete() { - if is_valid_name "$1"; then - if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then - printf "Are you sure you want to delete this server and its worlds (note: backups are preserved) [y/N]: " +# Watches the a server's log +# $1: The ID for the server +# $2: The line in the log to wait for +# $3: A UNIX timestamp (seconds since 1970) which the $2 line must be after +# 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]}" - read answer - if [[ "$answer" =~ ^y|Y|yes$ ]]; then - # TODO: stop the server if running first - as_user "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 + regex="^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[.*\] ${2}" + + while read line + do + line_time=$(log_line_get_time "$line") + + # If the entry is old enough and matches the regular expression + if [[ $line_time -ge $3 && $line =~ $regex ]] + then + echo $line + break fi - else - return 1 - fi + done < <(as_user ${server_user_name[$1]} "tail --follow --lines=100 --sleep-interval=0.1 ${server_log[$1]}") } -# 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 [[ -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 - echo "Could not be renamed, there is already a server with the name \"$2\"." - return 1 - else - as_user "mv '$SERVER_STORAGE_PATH/$1' '$SERVER_STORAGE_PATH/$2'" - echo "Renamed server \"$1\" to \"$2\"." - fi - else - return 1 - fi - else - echo "There is no server with the name \"$1\"." - return 1 - fi - else - return 1 - fi +# 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 $1 $2 $3 > /dev/null } @@ -366,210 +452,497 @@ jargroup_rename() { } -### Script Options +### Server Functions -case "$1" in - start) - # Required start option, for debian init.d scripts - ;; - stop) - # Required stop option, for debian init.d scripts - ;; - restart) - # Required restart option, for debian init.d scripts - ;; - 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." +# Echos a list of servers in the SERVER_STORAGE_PATH +server_list() { + echo "Servers:" + for server in "$SERVER_STORAGE_PATH/*"; do + echo " $(basename $server)" + done +} + +# 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 "mkdir -p '$SERVER_STORAGE_PATH/$1'" + as_user "touch '$SERVER_STORAGE_PATH/$1/$WHITELIST'" + as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_IPS'" + as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_PLAYERS'" + echo "Done." + fi + else + return 1 + fi +} + +# Deletes an existing server +# $2: The server name to delete +server_delete() { + if is_valid_name "$1"; then + if [[ -e "$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 "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 [[ -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 + echo "Could not be renamed, there is already a server with the name \"$2\"." + return 1 else - # Create a new server - server_create "$3" + as_user "mv '$SERVER_STORAGE_PATH/$1' '$SERVER_STORAGE_PATH/$2'" + echo "Renamed server \"$1\" to \"$2\"." fi - ;; - delete) - if [ -z "$3" ]; then - # If a server name is not provided - echo "Invalid command." + else + return 1 + fi + else + echo "There is no server with the name \"$1\"." + return 1 + fi + else + return 1 + fi +} + +# Starts a single server +# $1: The ID of the server to start +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 "${server_confirm_start[$1]}" "$time_now" + + echo "Done." + + echo "${server_user_name[$1]} ${server_path[$1]} ${server_screen_name[$1]} ${server_invocation[$1]}" + fi +} + + +### Main Functions + +init() { + local i=0 + local j=0 + + for server in $(ls -1 "$SERVER_STORAGE_PATH"); do + server_name[$i]="$server" + server_path[$i]="$SERVER_STORAGE_PATH/$server" + server_conf[$i]="${server_path[$i]}/$DEFAULT_SERVER_CONF" + + if [[ -e "$server/$SERVER_FLAG_ACTIVE" ]]; then + server_active[$i]="true" + else + server_active[$i]="false" + fi + + # Setup defaults + # Note: screen_name will at this stage have the {SERVER_NAME} tag in it + # which needs to be replaced. + # Invocation will may also the {RAM} and {JAR} tags. + + server_user_name[$i]="$DEFAULT_SERVER_USER" + server_screen_name[$i]="${DEFAULT_SCREEN_NAME//\{SERVER_NAME\}/${server_name[$i]}}" # Replace tags now, they cannot change + server_world_storage[$i]="${server_path[$i]}/$DEFAULT_WORLD_STORAGE_PATH" + server_log[$i]="${server_path[$i]}/$DEFAULT_LOG" + server_jar[$i]="${server_path[$i]}/$DEFAULT_JAR" + server_ram[$i]="$DEFAULT_RAM" + server_invocation[$i]="$DEFAULT_INVOCATION" # Don't replace tags yet, they may change + server_stop_delay[$i]="$DEFAULT_STOP_DELAY" + server_restart_delay[$i]="$DEFAULT_RESTART_DELAY" + server_stop_message[$i]="$DEFAULT_STOP_MESSAGE" + server_restart_message[$i]="$DEFAULT_RESTART_MESSAGE" + server_world_backup_started[$i]="$DEFAULT_WORLD_BACKUP_STARTED" + server_world_backup_finished[$i]="$DEFAULT_WORLD_BACKUP_FINISHED" + server_complete_backup_started[$i]="$DEFAULT_COMPLETE_BACKUP_STARTED" + server_complete_backup_finished[$i]="$DEFAULT_COMPLETE_BACKUP_FINISHED" + server_confirm_save_on[$i]="$DEFAULT_CONFIRM_SAVE_ON" + server_confirm_save_off[$i]="$DEFAULT_CONFIRM_SAVE_OFF" + server_confirm_save_all[$i]="$DEFAULT_CONFIRM_SAVE_ALL" + server_confirm_start[$i]="$DEFAULT_CONFIRM_START" + + + # Load config overrides from server config file if present + + if [[ -e "${server_conf[$i]}" ]]; then + local name + local value + + 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[$i]="$value";; + SCREEN_NAME) server_screen_name[$i]="$value";; + WORLD_STORAGE_DIR) server_world_storage[$i]="${server_path[$i]}/$value";; + LOG) server_log[$i]="${server_path[$i]}/$value";; + JAR) server_jar[$i]="${server_path[$i]}/$value";; + RAM) server_ram[$i]="$value";; + INVOCATION) server_invocation[$i]="$value";; + STOP_DELAY) server_stop_delay[$i]="$value";; + RESTART_DELAY) server_restart_delay[$i]="$value";; + STOP_MESSAGE) server_stop_message[$i]="$value";; + RESTART_MESSAGE) server_restart_message[$i]="$value";; + WORLD_BACKUP_STARTED) server_world_backup_started[$i]="$value";; + WORLD_BACKUP_FINISHED) server_world_backup_finished[$i]="$value";; + COMPLETE_BACKUP_STARTED) server_complete_backup_started[$i]="$value";; + COMPLETE_BACKUP_FINISHED) server_complete_backup_finished[$i]="$value";; + CONFIRM_SAVE_ON) server_confirm_save_on[$i]="$value";; + CONFIRM_SAVE_OFF) server_confirm_save_off[$i]="$value";; + CONFIRM_SAVE_ALL) server_confrim_save_all[$i]="$value";; + CONFIRM_START) server_confrim_start[$i]="$value";; + esac + done < "${server_conf[$i]}" + fi + + # Replace tags in delay messages + server_stop_message[$i]="${server_stop_message[$i]//\{DELAY\}/${server_stop_delay[$i]}}" + server_restart_message[$i]="${server_restart_message[$i]//\{DELAY\}/${server_restart_delay[$i]}}" + + # Replace tags in server invocation + server_invocation[$i]="${server_invocation[$i]//\{RAM\}/${server_ram[$i]}}" + server_invocation[$i]="${server_invocation[$i]//\{JAR\}/${server_jar[$i]}}" + + + # Load worlds if there is a world storage directory present + server_world_offset[$i]=0 + server_num_worlds[$i]=0 + + if [[ -e "${server_world_storage[$i]}" ]]; then + server_worlds[$i]=$(ls -1 "${server_world_storage[$i]}") + + # Record the index at which worlds for this server start + server_world_offset[$i]=$j + + for world in ${server_worlds[$i]}; do + world_server_id[$j]="$i" + world_name[$j]="$world" + world_path[$j]="${server_world_storage[$i]}/$world_name[$j]" + world_link[$j]="${server_path[$i]}/$world_name[$j]" + + if $RAMDISK_STORAGE_PATH; then + world_ramdisk_path[$j]="${RAMDISK_STORAGE_PATH}/${server_name[$i]}/${world_name[$j]}" + fi + + if [[ -e "${world_path[$j]}/$WORLD_FLAG_INRAM" ]]; then + world_inram[$j]="true" else - # Delete an existing server, with confirmation - server_delete "$3" + world_inram[$j]="false" 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 \t\t\t\t\tList servers" - echo -e " server create \t\t\t\tCreates a new Minecraft server" - echo -e " server delete \t\t\t\tDeletes an existing Minecraft server" - echo -e " server rename \t\tRenames an existing Minecraft server" - echo -e - echo -e "--Server Mangement Commands-------------------------------------" - echo -e " start \t\t\t\tStarts a server" - echo -e " stop [now] \t\t\t\tStops a server after warning players, or right now" - echo -e " restart [now] \t\t\tRestarts a server after warning players, or right now" - echo -e " status \t\t\t\tShow the running/stopped status of a server" - echo -e " connected \t\t\t\tList a servers connected players" - echo -e " worlds list \t\t\t\tLists the worlds a server has" - echo -e " worlds load \t\t\t\tCreates links to wolrds in storage for a server" - echo -e " worlds ram \t\t\tToggles a world's \"in RAM\" status" - echo -e " worlds toram \t\t\tSynchronises any RAM enabled worlds to RAM a server has" - echo -e " worlds todisk \t\t\tSynchronises any \"in RAM\" worlds to disk a server has" - echo -e " worlds backup \t\t\tMakes a backup of all worlds a server has" - echo -e " logroll \t\t\t\tMove a server log to a gziped archive, to reduce lag" - echo -e " backup \t\t\t\tMakes a backup of an entire server directory" - echo -e - echo -e "--Server Pass Through Commands----------------------------------" - echo -e " wl on|off \t\t\tEnabled/disable server whitelist check" - echo -e " wl add|remove \t\tAdd/remove a player to/from a server's whitelist" - echo -e " wl list \t\t\t\tList the players whitelisted for a server" - echo -e " bl player add|remove \tBan/pardon a player from/for a server" - echo -e " bl ip add|remove \tBan/pardon an IP address from/for a server" - echo -e " bl list \t\t\t\tLists the banned players and IP address for a server" - echo -e " op add|remove \t\tAdd/remove operator status for a player on a server" - echo -e " gm survival|creative \tChange the game mode for a player on a server" - echo -e " kick \t\t\tForcibly disconnect a player from a server" - echo -e " say \t\t\tBroadcast a (pink) message to all players on a server" - echo -e " time set|add \t\tSet/increment time on a server (0-24000)" - echo -e " toggledownfall \t\t\tToggles rain and snow on a server" - echo -e " save on|off \t\t\t\tEnable/disable writing world changes to file" - echo -e " save all \t\t\t\tForce the writing of all non-saved world changes to file" - echo -e " cmd \t\t\tSend a command string to the server and return" - echo -e " cmdlog \t\t\tSame as 'cmd' but shows log output afterwards (Ctrl+C to exit)" - echo -e - echo -e "--Jar Commands--------------------------------------------------" - echo -e " jargroup list \t\t\t\tList the stored jar files." - echo -e " jargroup create \tCreate a new jar group, with a URL for new downloads" - echo -e " jargroup delete \t\t\tDelete a jar group" - echo -e " jargroup rename \t\tRename a jar group" - echo -e " jargroup changeurl \tChange the download URL for a jar group" - echo -e " jargroup getlatest \t\t\tDownload the latest jar file for a jar group" - echo -e - echo -e "--Global Commands-----------------------------------------------" - echo -e " start \t\t\t\t\tStarts all active servers" - echo -e " stop [now]\t\t\t\t\tStops all running servers" - echo -e " restart [now]\t\t\t\t\tRestarts all active servers" - ;; - "") - echo "No such command see: $0 help" - ;; - *) - if [[ "$1" == "all" ]] || [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then + + # echo + # echo "World server: ${server_name[${world_server_id[$j]}]}" + # echo "World name: ${world_name[$j]}" + # echo "World inram: ${world_inram[$j]}" + + j=$(($j+1)) + done + + # Record the number of worlds this server has + server_num_worlds[$i]=$(( $j - ${server_world_offset[$i]} )) + + echo ${server_num_worlds[$i]} + fi + + ### Printout + # echo "Name: ${server_name[$i]}" + # echo "Path: ${server_path[$i]}" + # echo "Conf: ${server_conf[$i]}" + # echo "Active: ${server_active[$i]}" + # echo "User name: ${server_user_name[$i]}" + # echo "Screen name: ${server_screen_name[$i]}" + # echo "World storage: ${server_world_storage[$i]}" + # echo "Log: ${server_log[$i]}" + # echo "Jar: ${server_jar[$i]}" + # echo "Ram: ${server_ram[$i]}" + # echo "Invocation: ${server_invocation[$i]}" + # echo "Stop delay: ${server_stop_delay[$i]}" + # echo "Restart delay: ${server_restart_delay[$i]}" + # echo "Stop message: ${server_stop_message[$i]}" + # echo "Restart message: ${server_restart_message[$i]}" + # echo "World backup started: ${server_world_backup_started[$i]}" + # echo "World backup finished: ${server_world_backup_finished[$i]}" + # echo "World backup started: ${server_complete_backup_started[$i]}" + # echo "World backup finished: ${server_complete_backup_finished[$i]}" + # echo "Confirm save-on: ${server_confirm_save_on[$i]}" + # echo "Confirm save-off: ${server_confirm_save_off[$i]}" + # echo "Confirm save-all: ${server_confirm_save_all[$i]}" + # echo "Confirm start: ${server_confirm_start[$i]}" + # echo -n "Worlds: " + # for world in ${server_worlds[$i]}; do + # echo -n "$world, " + # done + # echo + # echo + + i=$(($i+1)) + done + + num_servers=$i + num_worlds=$j +} + +main() { + # Initialise variables that represent system state + init + + case "$1" in + start) + # Required start option, for debian init.d scripts + ;; + stop) + # Required stop option, for debian init.d scripts + ;; + restart) + # Required restart option, for debian init.d scripts + ;; + server) case "$2" in - start) + list) + # Lists the existing servers + server_list ;; - stop) + create) + if [ -z "$3" ]; then + # If a server name is not provided + echo "Invalid command." + else + # Create a new server + server_create "$3" + fi ;; - restart) + 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 ;; - worlds) + 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 ;; - backup) - ;; - logroll) - ;; - connected) - ;; - status) - ;; - whitelist|wl) - ;; - blacklist|bl) - ;; - operator|op) - ;; - gamemode|gm) - ;; - kick) - ;; - say) - ;; - "time") - ;; - toggledownfall) - ;; - save) - ;; - cmd) - ;; - cmdlog) + *) + # "server" is not a valid command + echo "Invalid command." ;; esac - else - echo "No server with that name." - fi - ;; -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 -exit 0 \ No newline at end of file + ;; + 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 \t\t\t\t\tList servers" + echo -e " server create \t\t\t\tCreates a new Minecraft server" + echo -e " server delete \t\t\t\tDeletes an existing Minecraft server" + echo -e " server rename \t\tRenames an existing Minecraft server" + echo -e + echo -e "--Server Mangement Commands-------------------------------------" + echo -e " start \t\t\t\tStarts a server" + echo -e " stop [now] \t\t\t\tStops a server after warning players, or right now" + echo -e " restart [now] \t\t\tRestarts a server after warning players, or right now" + echo -e " status \t\t\t\tShow the running/stopped status of a server" + echo -e " connected \t\t\t\tList a servers connected players" + echo -e " worlds list \t\t\t\tLists the worlds a server has" + echo -e " worlds load \t\t\t\tCreates links to wolrds in storage for a server" + echo -e " worlds ram \t\t\tToggles a world's \"in RAM\" status" + echo -e " worlds toram \t\t\tSynchronises any RAM enabled worlds to RAM a server has" + echo -e " worlds todisk \t\t\tSynchronises any \"in RAM\" worlds to disk a server has" + echo -e " worlds backup \t\t\tMakes a backup of all worlds a server has" + echo -e " logroll \t\t\t\tMove a server log to a gziped archive, to reduce lag" + echo -e " backup \t\t\t\tMakes a backup of an entire server directory" + echo -e + echo -e "--Server Pass Through Commands----------------------------------" + echo -e " wl on|off \t\t\tEnabled/disable server whitelist check" + echo -e " wl add|remove \t\tAdd/remove a player to/from a server's whitelist" + echo -e " wl list \t\t\t\tList the players whitelisted for a server" + echo -e " bl player add|remove \tBan/pardon a player from/for a server" + echo -e " bl ip add|remove \tBan/pardon an IP address from/for a server" + echo -e " bl list \t\t\t\tLists the banned players and IP address for a server" + echo -e " op add|remove \t\tAdd/remove operator status for a player on a server" + echo -e " gm survival|creative \tChange the game mode for a player on a server" + echo -e " kick \t\t\tForcibly disconnect a player from a server" + echo -e " say \t\t\tBroadcast a (pink) message to all players on a server" + echo -e " time set|add \t\tSet/increment time on a server (0-24000)" + echo -e " toggledownfall \t\t\tToggles rain and snow on a server" + echo -e " save on|off \t\t\t\tEnable/disable writing world changes to file" + echo -e " save all \t\t\t\tForce the writing of all non-saved world changes to file" + echo -e " cmd \t\t\tSend a command string to the server and return" + echo -e " cmdlog \t\t\tSame as 'cmd' but shows log output afterwards (Ctrl+C to exit)" + echo -e + echo -e "--Jar Commands--------------------------------------------------" + echo -e " jargroup list \t\t\t\tList the stored jar files." + echo -e " jargroup create \tCreate a new jar group, with a URL for new downloads" + echo -e " jargroup delete \t\t\tDelete a jar group" + echo -e " jargroup rename \t\tRename a jar group" + echo -e " jargroup changeurl \tChange the download URL for a jar group" + echo -e " jargroup getlatest \t\t\tDownload the latest jar file for a jar group" + echo -e + echo -e "--Global Commands-----------------------------------------------" + echo -e " start \t\t\t\t\tStarts all active servers" + echo -e " stop [now]\t\t\t\t\tStops all running servers" + echo -e " restart [now]\t\t\t\t\tRestarts all active servers" + ;; + "") + echo "No such command see: $0 help" + ;; + *) + if [[ "$1" == "all" ]] || [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then + case "$2" in + start) + server_start $(server_get_id "$1") + ;; + stop) + ;; + restart) + ;; + worlds) + ;; + backup) + ;; + logroll) + ;; + connected) + ;; + status) + ;; + whitelist|wl) + ;; + blacklist|bl) + ;; + operator|op) + ;; + gamemode|gm) + ;; + kick) + ;; + say) + ;; + "time") + ;; + toggledownfall) + ;; + save) + ;; + cmd) + ;; + cmdlog) + ;; + esac + else + echo "No server with that name." + fi + ;; + esac +} + + +### Start point + +main "$@" +exit 0 diff --git a/msm.conf b/msm.conf index b8c289e..0137106 100644 --- a/msm.conf +++ b/msm.conf @@ -1,25 +1,137 @@ #!/bin/bash -# User which manages all servers -SERVER_USER="minecraft" +# Minecraft Server Manager Configuration File +# =========================================== +# +# -# A base path to use in the rest of this config file (if you choose) -STORAGE_PATH="/opt/msm" + +# Directory Locations +# ------------------- + +# These values are very important. They define where MSM stores various files # Where new servers are stored -SERVER_STORAGE_PATH="${STORAGE_PATH}/servers" - -# Where RAM enambled worlds are stored -RAMDISK_STORAGE_PATH="" +SERVER_STORAGE_PATH="/opt/msm/servers" # Where runnable jar files for use by servers are stored -JAR_STORAGE_PATH="${STORAGE_PATH}/jars" +JAR_STORAGE_PATH="/opt/msm/jars" -# Where "WorldEdit snapshot" compatible world backups are stored -WORLD_ARCHIVE_PATH="${STORAGE_PATH}/archives/worlds" -# Where archived logs are stored -LOG_ARCHIVE_PATH="${STORAGE_PATH}/archives/logs" +# Where RAM enambled worlds are stored +# This needs to be a path located inside the mounted ramdisk for your system +# under Ubuntu this would be "/dev/shm", so "/dev/shm/msm" would be a good +# location. If left blank RAM disk will not be used. +RAMDISK_STORAGE_PATH="" -# Where compelte server backups are stored -BACKUP_ARCHIVE_PATH="${STORAGE_PATH}/archives/backups" \ No newline at end of file + +# Quick IMPORTANT note: +# The following three paths are used to store backups of your servers which +# MSM creates periodically. Backups are usefull if a world becomes unlayable +# and needs to be restored to an earlier point in time. Or maybe a new plugin +# for a server corrupts some files. In these cases you can restore to a +# backup from these directories. +# +# If you want protection for disk failure also, be sure to spcify paths to a +# separate disk. This can be achieved by mounting an external hard drive, or +# a NAS and put the following paths in there. + + +# Where "WorldEdit snapshot" compatible world backups are stored. +WORLD_ARCHIVE_PATH="/opt/msm/archives/worlds" + +# Where archived logs are stored, when a servers logs are "rolled": +LOG_ARCHIVE_PATH="/opt/msm/archives/logs" + +# Where complete server backups are stored: +BACKUP_ARCHIVE_PATH="/opt/msm/archives/backups" + + + +# Server Defaults +# --------------- + +# These values are the default values used when a server does not override +# them in its individual config file. +# They are also the default values used when a new server is created, without +# specifying any arguments. + + +# Server config file location, relative to each server directory +DEFAULT_SERVER_CONF="server.conf" + +# User which stars the server, and interacts with it +DEFAULT_SERVER_USER="minecraft" + +# The name under which to run a servers screen session, each is made unique +# by featuring the {SERVER_NAME} tag. +DEFAULT_SCREEN_NAME="msm-{SERVER_NAME}" + +# A directory name relative to the server directory where worlds are stored +DEFAULT_WORLD_STORAGE_PATH="worldstorage" + +# The location of the standard Minecraft log file, relative to the +# server directory +DEFAULT_LOG="server.log" + +# The location of the jar file to execute, relative to the server directory +DEFAULT_JAR="server.jar" + +# The amount of memory to dedicate to a server (in MB) +DEFAULT_RAM="1024" + +# The command used to start the server. You may use the following tags which +# will be replaced: +# +# {RAM} - Gets replaced with the amount of RAM specified for the server +# {JAR} - Gets replaced with the location of the jar file for the server +# +# Hard coding values here (not using {MEMORY} and {JAR} tags) will result in +# all servers using the values. +DEFAULT_INVOCATION="java -Xms{RAM}M -Xmx{RAM}M -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalPacing -XX:+AggressiveOpts -jar {JAR} nogui" + +# The default time to delay between warning players the server is stopping and +# actually stopping the server: +DEFAULT_STOP_DELAY=10 + +# The default time to delay between warning players the server is restarting +# and actually restarting the server: +DEFAULT_RESTART_DELAY=10 + +# The default message to send to players on a server which is about to be +# shut down. You may use the tag "{DELAY}" to specify the time delay +# before shutdown: +DEFAULT_STOP_MESSAGE="SERVER SHUTTING DOWN IN {DELAY} SECONDS!" + +# The default message sent to players on a server which is about to be +# restarted. You may use the tag "{DELAY}" to specify the time delay before +# the server restarts: +DEFAULT_RESTART_MESSAGE="SERVER REBOOT IN {DELAY} SECONDS!" + +# The default message to send to players when a server begins backing up +# its worlds: +DEFAULT_WORLD_BACKUP_STARTED="Backing up world." + +# The default message to send to players when a server has finished backing +# up its worlds: +DEFAULT_WORLD_BACKUP_FINISHED="Backup complete." + +# The default message to send to players when a server begins backing up the +# entire server directory: +DEFAULT_COMPLETE_BACKUP_STARTED="Backing up entire server." + +# The default message to send to players when a serer finishes backing up the +# entire server directory: +DEFAULT_COMPLETE_BACKUP_FINISHED="Backup complete." + +# The message once logged confirms the "save-on" command has finished +DEFAULT_CONFIRM_SAVE_ON="CONSOLE: Enabling level saving.." + +# The message once logged confirms the "save-off" command has finished +DEFAULT_CONFIRM_SAVE_OFF="CONSOLE: Disabling level saving.." + +# The message once logged confirms the "save-all" command has finished +DEFAULT_CONFIRM_SAVE_ALL="CONSOLE: Save complete." + +# The message once logged confirms the server has started up +DEFAULT_CONFIRM_START="Done" \ No newline at end of file