diff --git a/CentOS/Squad/Squad-Connect-Server.bash b/CentOS/Squad/Squad-Connect-Server.bash new file mode 100755 index 0000000..29004d4 --- /dev/null +++ b/CentOS/Squad/Squad-Connect-Server.bash @@ -0,0 +1,17 @@ +#!/bin/bash --posix + +set -e + +usage() { + echo \ + "Help Menu + Usage: $(basename ${0}) " + exit "${1}" +} + +[[ -z "${1}" ]] && usage 1 +[[ "${1}" == "--help" || "${1}" == "-h" ]] && usage 0 + +tmux attach-session -t "Squad-Server-${1}" + +exit ${?} diff --git a/CentOS/Squad/Squad-Install-Server.bash b/CentOS/Squad/Squad-Install-Server.bash new file mode 100755 index 0000000..41d2f75 --- /dev/null +++ b/CentOS/Squad/Squad-Install-Server.bash @@ -0,0 +1,182 @@ +#!/bin/bash + + +echo_rgb() { + # Echo a colored string to the terminal based on rgb values + # + # Positional Arguments: + # + # message + # - The message to be printed to stdout + # red + # - The red value from 0 to 255 + # green + # - The green value from 0 to 255 + # blue + # - The blue value from 0 to 255 + # + # Usage: + # echo_rgb "Yep" 10 8 30 + # + # POSIX Compliant: + # N/A + # + + local red + local green + local blue + local input + + input="${1}" + red="${2}" + green="${3}" + blue="${4}" + + printf "\e[0;38;2;%s;%s;%sm%s\e[m\n" "${red}" "${green}" "${blue}" "${input}" +} + +log() { + # Print a message and send it to stdout or stderr depending upon log level, also configurable with debug etc. + # + # Arguments: + # level + # - The log level, defined within a case check in this function + # message + # - The info message + # line_number + # - The line number of the calling function (${LINNO}) + # + # Usage: + # log "info" "Could not find that directory" + # + # POSIX Compliant: + # Yes + # + + # Set debug status depending if a global debug variable has been set to either 1 or 0 + local debug + if [ ${DEBUG} ]; then + debug=${DEBUG} + else + debug=0 + fi + + local FORMAT + FORMAT="[$(echo_rgb "$(date +%Y-%m-%dT%H:%M:%S)" 180 140 255)]" + + # Convert the level to uppercase + local level + level=$(echo "${1}" | tr '[:lower:]' '[:upper:]') + + local message + message="${2}" + + case "${level}" in + INFO) + # Output all info log levels to stdout + printf "${FORMAT}[$(echo_rgb "INFO" 0 140 255)] %s\n" "${message}" >&1 + return 0 + ;; + WARN | WARNING) + # Output all info log levels to stdout + printf "${FORMAT}[$(echo_rgb "WARNING" 255 255 0)] %s\n" "${message}" >&1 + return 0 + ;; + DEBUG) + [[ ${debug} == 0 ]] && return + printf "${FORMAT}[$(echo_rgb "DEBUG" 0 160 110)] %s\n" "${message}" >&1 + return 0 + ;; + ERROR) + # Output all error log levels to stderr + printf "${FORMAT}[$(echo_rgb "ERROR" 255 0 0)] %s\n" "${message}" >&2 + return 0 + ;; + # Further log levels can be added by extending this switch statement with more comparisons + + *) # Default case, no matches + # Returns non-zero code as an improper log option was passed, this helps with using `set -e` + printf "${FORMAT}[ERROR] %s\n" "Invalid log level passed, received level \"${level}\" with message \"${message}\"" >&2 + return 1 + ;; + esac +} + +usage() { + # Print out usage instructions for the local script + # + # Arguments: + # None + # + # Usage: + # usage + # + # POSIX Compliant: + # Yes + # + printf "Usage: %s\n" \ + "$(basename ${0}) -s + -s | --server + The new squad server to install/update, see the ~/Squad directory -- each number corresponds to an ID + + Example: + --server 0" +} + +server_id="" + +parse_args() { + # Parse input arguments + # + # Arguments: + # Consult the `usage` function + # + # Usage: + # parse_args "$@" + # - All arguments should be ingested by parse_args first for variable setting + # + # POSIX Compliant: + # Yes + # + + while :; do + case ${1} in + -h | -\? | --help) + usage # Display a usage synopsis. + exit + ;; + --) # End of all options. + break + ;; + -s | --server) + shift + server_id="${1}" + [[ -z "${server_id}" ]] \ + && log "error" "Server requires an argument, e.g. --server 0" \ + && exit 1 + [[ ! "${server_id}" =~ [0-9] ]] \ + && log "error" "Server ID must be a number, received: ${server_id}" \ + && exit 1 + ;; + -?*) + printf 'Unknown option: %s\n' "$1" >&2 + usage + ;; + *) # Default case: No more options, so break out of the loop. + break ;; + esac + shift + done +} + +important() { + echo_rgb "${1}" 145 233 255 +} + +parse_args "$@" + +mkdir -p ~/Squad/Server-"${server_id}" \ + && log "info" "Created a new Squad server directory at $(important ~/Squad/Server-"${server_id}")" +steamcmd +login anonymous +force_install_dir ~/Squad/Server-"${server_id}" +app_update 403240 validate +quit + +log "info" "Successfully installed a Squad server to $(important ~/Squad/Server-"${server_id}")" \ No newline at end of file diff --git a/CentOS/Squad/Squad-Open-Ports.bash b/CentOS/Squad/Squad-Open-Ports.bash new file mode 100755 index 0000000..e2d79bc --- /dev/null +++ b/CentOS/Squad/Squad-Open-Ports.bash @@ -0,0 +1,4 @@ +#!/bin/bash + +firewall-cmd --zone=public --add-port=13000-13299/tcp --permanent +firewall-cmd --reload \ No newline at end of file diff --git a/CentOS/Squad/Squad-Start-Server.bash b/CentOS/Squad/Squad-Start-Server.bash new file mode 100755 index 0000000..52ac444 --- /dev/null +++ b/CentOS/Squad/Squad-Start-Server.bash @@ -0,0 +1,319 @@ +#!/bin/bash + +### PORTS ### +# GAME PORT RANGE: 13000-13099 +# QUERY PORT RANGE: 13100-13199 +# RCON PORT RANGE: 13200-13299 + +echo_rgb() { + # Echo a colored string to the terminal based on rgb values + # + # Positional Arguments: + # + # message + # - The message to be printed to stdout + # red + # - The red value from 0 to 255 + # green + # - The green value from 0 to 255 + # blue + # - The blue value from 0 to 255 + # + # Usage: + # echo_rgb "Yep" 10 8 30 + # + # POSIX Compliant: + # N/A + # + + local red + local green + local blue + local input + + input="${1}" + red="${2}" + green="${3}" + blue="${4}" + + printf "\e[0;38;2;%s;%s;%sm%s\e[m\n" "${red}" "${green}" "${blue}" "${input}" +} + +log() { + # Print a message and send it to stdout or stderr depending upon log level, also configurable with debug etc. + # + # Arguments: + # level + # - The log level, defined within a case check in this function + # message + # - The info message + # line_number + # - The line number of the calling function (${LINNO}) + # + # Usage: + # log "info" "Could not find that directory" + # + # POSIX Compliant: + # Yes + # + + # Set debug status depending if a global debug variable has been set to either 1 or 0 + local debug + if [ ${DEBUG} ]; then + debug=${DEBUG} + else + debug=0 + fi + + local FORMAT + FORMAT="[$(echo_rgb "$(date +%Y-%m-%dT%H:%M:%S)" 180 140 255)]" + + # Convert the level to uppercase + local level + level=$(echo "${1}" | tr '[:lower:]' '[:upper:]') + + local message + message="${2}" + + case "${level}" in + INFO) + # Output all info log levels to stdout + printf "${FORMAT}[$(echo_rgb "INFO" 0 140 255)] %s\n" "${message}" >&1 + return 0 + ;; + WARN | WARNING) + # Output all info log levels to stdout + printf "${FORMAT}[$(echo_rgb "WARNING" 255 255 0)] %s\n" "${message}" >&1 + return 0 + ;; + DEBUG) + [[ ${debug} == 0 ]] && return + printf "${FORMAT}[$(echo_rgb "DEBUG" 0 160 110)] %s\n" "${message}" >&1 + return 0 + ;; + ERROR) + # Output all error log levels to stderr + printf "${FORMAT}[$(echo_rgb "ERROR" 255 0 0)] %s\n" "${message}" >&2 + return 0 + ;; + # Further log levels can be added by extending this switch statement with more comparisons + + *) # Default case, no matches + # Returns non-zero code as an improper log option was passed, this helps with using `set -e` + printf "${FORMAT}[ERROR] %s\n" "Invalid log level passed, received level \"${level}\" with message \"${message}\"" >&2 + return 1 + ;; + esac +} + +confirmation() { + # Receive confirmation from user as y, Y, n, or N + # returns 0 when answer is yes and 1 when answer is no + # + # Arguments: + # message + # - The confirmation prompt sent to the user, for example: + # Would you like to overwrite foobar.txt (y/N)? + # + # Usage: + # confirmation "Some prompt" + # - Sends "Some prompt" to the user and gets their input + # + # POSIX Compliant: + # Yes + # + + local message + message="${1}" + + local choice + + while true; do + read -p "${message} " -n 1 -r choice + case "$choice" in + y | Y) + echo "" + return 0 + ;; + n | N) + echo "" + return 1 + ;; + *) echo -e "\nInput must be either y, Y, n, or N" ;; + esac + done +} + +usage() { + # Print out usage instructions for the local script + # + # Arguments: + # None + # + # Usage: + # usage + # + # POSIX Compliant: + # Yes + # + printf "Usage: %s\n" \ + "$(basename "${0}") -s + -s | --server + Which Squad server to start, see the ~/Squad directory -- each number corresponds to an ID + + Example: + --server 0" +} + +server_id="" + +parse_args() { + # Parse input arguments + # + # Arguments: + # Consult the `usage` function + # + # Usage: + # parse_args "$@" + # - All arguments should be ingested by parse_args first for variable setting + # + # POSIX Compliant: + # Yes + # + + while :; do + case ${1} in + -h | -\? | --help) + usage # Display a usage synopsis. + exit + ;; + --) # End of all options. + break + ;; + -s | --server) + shift + server_id="${1}" + [[ -z "${server_id}" ]] \ + && log "error" "Server requires an argument, e.g. --server 0" \ + && exit 1 + [[ ! "${server_id}" =~ [0-9] ]] \ + && log "error" "Server ID must be a number, received: ${server_id}" \ + && exit 1 + ;; + -?*) + printf 'Unknown option: %s\n' "$1" >&2 + usage + ;; + *) # Default case: No more options, so break out of the loop. + break ;; + esac + shift + done +} + +important() { + echo_rgb "${1}" 145 233 255 +} + +parse_args "$@" + +### CONSTANTS ### +TICK_RATE="60" +### CONSTANTS ### + + +squad_directory=~/Squad/Server-"${server_id}" + +[[ ! -d "${squad_directory}" ]] \ + && log "error" "The Squad directory does not exist at ${squad_directory}, unable to start $(important "${squad_directory}")" \ + && exit 1 + +squad_start_script="${squad_directory}"/SquadGameServer.sh + +[[ ! -f "${squad_start_script}" ]] \ + && log "error" "The given Squad server lacks a start script, could not find $(importamt "${squad_start_script}")" \ + && exit 1 + +squad_rcon_config="${squad_directory}"/SquadGame/ServerConfig/Rcon.cfg + +[[ ! -f "${squad_rcon_config}" ]] \ + && log "error" "The given Squad server lacks a Rcon.cfg, could not find $(important "${squad_rcon_config}")" \ + && exit 1 + + +# If the tmux session exists then we prompt the user if they want to kill it off and start the new session +tmux has-session -t "Squad-Server-${server_id}" > /dev/null 2>&1 + +if [ "${?}" == 0 ]; then + log "warning" "Squad server $(important "Squad-Server-${server_id}") is currently running" + confirmation "Would you like to kill it and then run the new one (y/N)?" + if [ "${?}" == 0 ]; then + log "info" "Ok, killing server $(important "Squad-Server-${server_id}")" + tmux kill-session -t "Squad-Server-${server_id}" \ + && log "info" "Successfully killed $(important "Squad-Server-${server_id}")" + else + log "info" "Not ending current server $(important "Squad-Server-${server_id}"), exiting..." + exit 0 + fi +fi + + +# Create our relevant ports +game_port=$(("13000" + "${server_id}")) +query_port=$(("13100" + "${server_id}")) +rcon_port=$(("13200" + "${server_id}")) + + +# Create backups before modifying files +backup_extension="$(date +%Y-%m-%dT%H:%M:%S%z)" +mkdir -p "${squad_directory}"/Backups/ +log "info" "Creating a backup of $(important "Rcon.cfg")" +cp "${squad_directory}"/SquadGame/ServerConfig/Rcon.cfg "${squad_directory}"/Backups/Rcon.cfg."${backup_extension}" \ + && log "info" "Successfully created a backup of $(important "Rcon.cfg")" + + +## RCON configuration + +# If we haven't created a rcon.password file go ahead and make it and generate a password for it +if [ ! -f ~/Squad/rcon.password ]; then + log "info" "Creating a global rcon password in $(important ~/Squad/rcon.password) as it did not exist" + openssl rand -base64 48 > ~/Squad/rcon.password +fi + +# Get the rcon password +rcon_password="$(cat ~/Squad/rcon.password)" + +log "info" "Updating $(important "Squad-Server-${server_id}")'s RCON config" + +# Update the password, since sed is PIA in regards to base64 & raw strings we fix this one up line by line +while read -r line +do + # Overwrites the password + echo "${line//Password=*/Password="${rcon_password}"}" + + # Overwrites the rcon port + echo "${line//Port=*/Port="${rcon_port}"}" + +done < "${squad_rcon_config}" > "rcon.temp" && mv "rcon.temp" "${squad_rcon_config}" + +log "info" "Successfully updated RCON config" + +log "info" \ +"Starting $(important "Squad-Server-${server_id}") with the following parameters: + $(important \ + "-Port=${game_port} + -QueryPort=${query_port} + -FIXEDMAXTICKRATE=${TICK_RATE} + -USEALLAVAILABLECORES + -LOG")" + +# Kick off a new tmux session containing the Squad Server +tmux new-session -d -s "Squad-Server-${server_id}" \ + "${squad_start_script}" -Port="${game_port}" -QueryPort="${query_port}" -FIXEDMAXTICKRATE="${TICK_RATE}" \ + -USEALLAVILABLECORES -LOG && +log "info" \ + "Started Squad-Server-${server_id} with the following ports: + $(important \ + "Game Port: ${game_port} + Query Port: ${query_port} + RCON Port ${rcon_port}")" diff --git a/Templates/Functions/Arg-Parsing.bash b/Templates/Functions/Arg-Parsing.bash index d6cc109..d4ac222 100755 --- a/Templates/Functions/Arg-Parsing.bash +++ b/Templates/Functions/Arg-Parsing.bash @@ -13,9 +13,8 @@ usage() { # POSIX Compliant: # Yes # - printf "Usage: %s %s\n" \ - "$(basename ${0})" \ - "-i \"this is some input\" -t \"this is some more example input\" + printf "Usage: %s\n" \ + "$(basename ${0}) -i \"this is some input\" -t \"this is some more example input\" --input | -i Example: --input \"this is an example input\" @@ -60,7 +59,7 @@ parse_args() { *) # Default case: No more options, so break out of the loop. break ;; esac - shift 2 + shift done }