mirror of
https://github.com/eliasstepanik/ark-ac-server-tools.git
synced 2026-01-12 10:58:28 +00:00
This should make it possible to determine the arkmanager version when it hasn't been installed using the netinstall script.
1933 lines
55 KiB
Bash
Executable File
1933 lines
55 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ARK: survival evolved manager
|
|
#
|
|
# Original author: LeXaT
|
|
# Maintainer: FezVrasta
|
|
# Contributors: Sispheor, Atriusftw, klightspeed, lexat, puseidr
|
|
|
|
# Script version
|
|
arkstVersion="1.6"
|
|
arkstCommit=''
|
|
|
|
doUpgradeTools() {
|
|
local sudo=sudo
|
|
if [ "$UID" == 0 -o "$steamcmd_user" == "--me" ]; then
|
|
sudo=
|
|
fi
|
|
echo "arkmanager v${arkstVersion}: Checking for updates..."
|
|
arkstLatestVersion=`curl -s https://raw.githubusercontent.com/FezVrasta/ark-server-tools/${arkstChannel}/.version`
|
|
arkstLatestCommit=`curl -s https://api.github.com/repos/FezVrasta/ark-server-tools/git/refs/heads/${arkstChannel} | sed -n 's/^ *"sha": "\(.*\)",.*/\1/p'`
|
|
|
|
if [ "$arkstLatestVersion" == "Not Found" ]; then
|
|
echo "Channel ${arkstChannel} does not exist"
|
|
echo
|
|
echo "Available channels:"
|
|
curl -s https://api.github.com/repos/FezVrasta/ark-server-tools/git/refs/heads | sed -n 's|^ *"ref": "refs/heads/\(.*\)",|\1|p'
|
|
echo
|
|
return
|
|
fi
|
|
|
|
reinstall_args=()
|
|
if [ -n "$install_bindir" ]; then
|
|
reinstall_args=( "${reinstall_args[@]}" "--bindir" "$install_bindir" )
|
|
fi
|
|
if [ -n "$install_libexecdir" ]; then
|
|
reinstall_args=( "${reinstall_args[@]}" "--libexecdir" "$install_libexecdir" )
|
|
fi
|
|
if [ -n "$install_datadir" ]; then
|
|
reinstall_args=( "${reinstall_args[@]}" "--datadir" "$install_datadir" )
|
|
fi
|
|
if [[ $arkstLatestVersion > $arkstVersion ]]; then
|
|
read -p "A new version was found! Do you want to upgrade ARK Server Tools to v${arkstLatestVersion}?" -n 1 -r
|
|
echo -en "\n"
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
curl -s https://raw.githubusercontent.com/FezVrasta/ark-server-tools/${arkstChannel}/netinstall.sh | $sudo bash -s -- ${steamcmd_user} ${arkstChannel} "${reinstall_args[@]}"
|
|
exit 0
|
|
fi
|
|
elif [[ $arkstLatestVersion == $arkstVersion && "$arkstLatestCommit" != "$arkstCommit" ]]; then
|
|
read -p "A hotfix is available for v${arkstLatestVersion}. Do you wish to install it?" -n 1 -r
|
|
echo -en "\n"
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
curl -s https://raw.githubusercontent.com/FezVrasta/ark-server-tools/${arkstChannel}/netinstall.sh | $sudo bash -s -- ${steamcmd_user} ${arkstChannel} "${reinstall_args[@]}"
|
|
exit 0
|
|
fi
|
|
else
|
|
echo "Your ARK server tools are already up to date"
|
|
fi
|
|
}
|
|
|
|
doUninstallTools() {
|
|
local sudo=sudo
|
|
if [ "$UID" == 0 -o "$steamcmd_user" == "--me" ]; then
|
|
sudo=
|
|
fi
|
|
|
|
read -p "Are you sure you want to uninstall the ARK Server Tools? [y/N]" -n 1 -r
|
|
|
|
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
|
|
if [ -n "${install_datadir}" -a -x "${install_datadir}/arkmanager-uninstall.sh" ]; then
|
|
$sudo "${install_datadir}/arkmanager-uninstall.sh"
|
|
exit 0
|
|
elif [ -n "${install_libexecdir}" -a -x "${install_libexecdir}/arkmanager-uninstall.sh" ]; then
|
|
$sudo "${install_libexecdir}/arkmanager-uninstall.sh"
|
|
exit 0
|
|
fi
|
|
fi
|
|
}
|
|
|
|
runAsRoot(){
|
|
getConfigVar(){
|
|
val="$(echo -ne "$(sed -n "/^$1=/{s|^[^=]*=||;s|[[:space:]]*\\(#.*\\)*\$||;s|^\"\\(.*\\)\"\$|\\1|;s|^'\\(.*\\)'\$|\\1|;p}" <"/etc/arkmanager/arkmanager.cfg" | tail -n1)")"
|
|
if [ -n "$val" ]; then
|
|
echo "$val"
|
|
else
|
|
echo "$2"
|
|
fi
|
|
}
|
|
|
|
arkstChannel="$(getConfigVar arkstChannel "master")"
|
|
install_bindir="$(getConfigVar install_bindir "${0%/*}")"
|
|
install_libexecdir="$(getConfigVar install_libexecdir "${install_bindir%/*}/libexec/arkmanager")"
|
|
install_datadir="$(getConfigVar install_datadir "${install_bindir%/*}/share/arkmanager")"
|
|
steamcmd_user="$(getConfigVar steamcmd_user "steam")"
|
|
|
|
if ! getent passwd "$steamcmd_user" >/dev/null 2>&1; then
|
|
echo "Invalid steamcmd_user in config file"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$1" == "upgrade-tools" ]; then
|
|
doUpgradeTools
|
|
elif [ "$1" == "uninstall-tools" ]; then
|
|
doUninstallTools
|
|
else
|
|
su "$steamcmd_user" -c "$(printf "%q" "$0")$(printf " %q" "$@")"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Check the user is not currently running this script as root
|
|
if [ "$(id -u)" == "0" ]; then
|
|
runAsRoot "$@"
|
|
exit 0
|
|
fi
|
|
|
|
#---------------------
|
|
# Variables
|
|
#---------------------
|
|
|
|
# Global variables
|
|
if [ -f "/etc/arkmanager/arkmanager.cfg" ]; then
|
|
source /etc/arkmanager/arkmanager.cfg
|
|
fi
|
|
|
|
if [ -f "${HOME}/.arkmanager.cfg" ]; then
|
|
source "${HOME}/.arkmanager.cfg"
|
|
fi
|
|
|
|
lsof=lsof
|
|
if [ -x /usr/sbin/lsof ]; then
|
|
lsof=/usr/sbin/lsof
|
|
fi
|
|
|
|
# Local variables
|
|
instver=""
|
|
bnumber=""
|
|
GREEN="\\033[1;32m"
|
|
RED="\\033[1;31m"
|
|
YELLOW="\\e[0;33m"
|
|
NORMAL="\\033[0;39m"
|
|
maxOpenFiles=100000
|
|
|
|
# Set TERM to "dumb" if TERM is not set
|
|
export TERM=${TERM:-dumb}
|
|
|
|
arkmanagerLog="arkmanager.log" # here are logged the actions performed by arkmanager
|
|
arkserverLog="arkserver.log" # here is logged the output of ShooterGameServer
|
|
|
|
appid="${appid:-376030}"
|
|
mod_appid="${mod_appid:-346110}"
|
|
arkautorestartfile="${arkautorestartfile:-ShooterGame/Saved/.autorestart}"
|
|
install_bindir="${install_bindir:-${0%/*}}"
|
|
install_libexecdir="${install_libexecdir:-${install_bindir%/*}/libexec/arkmanager}"
|
|
|
|
if [ "$steamcmd_user" == "--me" ]; then
|
|
install_datadir="${install_datadir:-${HOME}/.share/local/arkmanager}"
|
|
else
|
|
install_datadir="${install_datadir:-${install_bindir%/*}/share/arkmanager}"
|
|
fi
|
|
|
|
|
|
#---------------------
|
|
# functions
|
|
#---------------------
|
|
|
|
#
|
|
# timestamp
|
|
#
|
|
timestamp() {
|
|
date +%T
|
|
}
|
|
|
|
#
|
|
# check configuration and report errors
|
|
#
|
|
checkConfig() {
|
|
# SteamCMD configuration
|
|
# steamcmdroot
|
|
if [ ! -d "$steamcmdroot" ] ; then
|
|
echo -e "[" "$RED" "ERROR" "$NORMAL" "]" "\tYour SteamCMD root seems not valid."
|
|
fi
|
|
# steamcmdexec
|
|
if [ ! -f "$steamcmdroot/$steamcmdexec" ] ; then
|
|
echo -e "[" "$RED" "ERROR" "$NORMAL" "]" "\tYour SteamCMD exec could not be found."
|
|
fi
|
|
# steamcmd_user
|
|
if [ "$steamcmd_user" != "--me" ]; then
|
|
if ! getent passwd $steamcmd_user > /dev/null 2>&1 ; then
|
|
echo -e "[" "$RED" "ERROR" "$NORMAL" "]" "\tYour SteamCMD user is not valid."
|
|
fi
|
|
fi
|
|
|
|
# Environment configuration
|
|
# arkserverexec
|
|
if [ -n "$arkserverroot" ] && [ ! -f "$arkserverroot/$arkserverexec" ] ; then
|
|
echo -e "[" "$YELLOW" "WARN" "$NORMAL" "]" "\tYour ARK server exec could not be found."
|
|
fi
|
|
|
|
# SavedArks directory
|
|
if [ -n "$arkserverroot" ]; then
|
|
local savedarksdir="${arkserverroot}/ShooterGame/Saved/${ark_AltSaveDirectoryName:-SavedArks}"
|
|
if [ ! -w "${savedarksdir}" ]; then
|
|
echo -e "[" "$RED" "ERROR" "$NORMAL" "]" "\tThe ARK SavedArks directory is not writable, and saveworld will fail"
|
|
fi
|
|
fi
|
|
|
|
# Warn if any mods are requested but not installed
|
|
if [ -n "$arkserverroot" -a -d "${arkserverroot}/ShooterGame/Content/Mods" ]; then
|
|
for modid in $(getModIds); do
|
|
if [ ! -f "${arkserverroot}/ShooterGame/Content/Mods/${modid}/mod.info" ]; then
|
|
echo -e "[" "$RED" "ERROR" "$NORMAL" "]" "\tMod ${modid} is requested but not installed. Run 'arkmanager installmod ${modid}' to install this mod."
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Warn if mod_branch=Linux
|
|
if [ "$mod_branch" == "Linux" -a -z "$nowarnmodbranch" ]; then
|
|
echo -e "[" "$YELLOW" "WARN" "$NORMAL" "]" "\tmod_branch is set to Linux. Linux mods are known to cause the server to crash. It is suggested you set mod_branch to Windows."
|
|
fi
|
|
|
|
# Service configuration
|
|
# logdir
|
|
if [ ! -w "$logdir" ] ; then
|
|
echo -e "[" "$RED" "ERROR" "$NORMAL" "]" "\tYou have not rights to write in the log directory."
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Get setting from config or from ini file
|
|
# $1 is the setting name
|
|
# $2 is the default
|
|
#
|
|
getArkServerSetting() {
|
|
local varname="ark_$1"
|
|
if [ -n "${!varname}" ]; then
|
|
echo "${!varname}"
|
|
else
|
|
local val="$(tr -d '\0\376\377' <"${arkserverroot}/ShooterGame/Saved/Config/LinuxServer/GameUserSettings.ini" | sed -n '/^\[ServerSettings\]/,/^\[.*\]/{s/^'"$1"'[[:space:]]*=[[:space:]]*//p;}' )"
|
|
if [ -n "$val" ]; then
|
|
echo "$val"
|
|
else
|
|
echo "$2"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Get server admin password
|
|
#
|
|
getAdminPassword() {
|
|
getArkServerSetting "ServerAdminPassword" ""
|
|
}
|
|
|
|
#
|
|
# Get server RCON Port
|
|
#
|
|
getRconPort() {
|
|
getArkServerSetting "RCONPort" "32330"
|
|
}
|
|
|
|
#
|
|
# Get server Game Port
|
|
#
|
|
getGamePort() {
|
|
echo "${ark_Port:-7778}"
|
|
}
|
|
|
|
#
|
|
# Get server Query Port
|
|
#
|
|
getQueryPort(){
|
|
echo "${ark_QueryPort:-27015}"
|
|
}
|
|
|
|
#
|
|
# Execute RCON command
|
|
#
|
|
rconcmd() {
|
|
local adminpass="$(getAdminPassword)"
|
|
if [ -z "$adminpass" ]; then
|
|
echo "ServerAdminPassword is empty - unable to execute RCON command"
|
|
return 1
|
|
elif [[ "$adminpass" =~ [?\177-\377] ]]; then
|
|
echo "ServerAdminPassword contains invalid characters"
|
|
return 1
|
|
fi
|
|
perl -MSocket -e '
|
|
sub sendpkt {
|
|
my ($sock, $reqid, $reqtype, $body) = @_;
|
|
my $packet = pack("VVV", length($body) + 10, $reqid, $reqtype) . $body . "\0\0";
|
|
send($sock, $packet, 0) or die "Error sending command to server: $!";
|
|
}
|
|
|
|
sub recvpkt {
|
|
my ($sock) = @_;
|
|
my $data = "";
|
|
recv($sock, $data, 12, 0);
|
|
die "Empty response" if length($data) == 0;
|
|
my ($pktlen, $resid, $restype) = unpack("VVV", $data);
|
|
recv($sock, $data, $pktlen - 8, 0);
|
|
return ($resid, $restype, substr($data, 0, $pktlen - 10));
|
|
}
|
|
|
|
sub auth {
|
|
my ($sock, $password) = @_;
|
|
my $reqid = 1;
|
|
sendpkt($sock, $reqid, 3, $password);
|
|
my ($resid, $restype, $rcvbody) = recvpkt($sock);
|
|
die "Authentication failed" if $resid == -1;
|
|
}
|
|
|
|
my $port = $ARGV[0];
|
|
my $ipaddr = $ARGV[1];
|
|
my $password = $ARGV[2];
|
|
my $command = $ARGV[3];
|
|
socket(my $socket, PF_INET, SOCK_STREAM, 0);
|
|
setsockopt($socket, SOL_SOCKET, SO_RCVTIMEO, pack("i4", 30, 0, 0, 0));
|
|
my $sockaddr = pack_sockaddr_in($port, inet_aton($ipaddr));
|
|
connect($socket, $sockaddr) or die "Error connecting to server: $!";
|
|
auth($socket, $password);
|
|
sendpkt($socket, 2, 2, $command);
|
|
my ($resid, $restype, $rcvbody) = recvpkt($socket);
|
|
if ($rcvbody eq "Server received, But no response!!") {
|
|
print "Command processed\n";
|
|
} else {
|
|
print $rcvbody, "\n";
|
|
}
|
|
' "$(getRconPort)" "${ark_MultiHome:-127.0.0.1}" "$adminpass" "$1"
|
|
}
|
|
|
|
#
|
|
# Save world
|
|
#
|
|
doSaveWorld() {
|
|
rconcmd saveworld
|
|
}
|
|
|
|
#
|
|
# Exit cleanly
|
|
#
|
|
doExitServer() {
|
|
rconcmd doexit
|
|
}
|
|
|
|
#
|
|
# Broadcast message
|
|
#
|
|
doBroadcast(){
|
|
rconcmd "broadcast $1"
|
|
}
|
|
|
|
#
|
|
# Broadcast message with echo
|
|
#
|
|
doBroadcastWithEcho(){
|
|
echo "$1"
|
|
doBroadcast "$1"
|
|
}
|
|
|
|
#
|
|
# SteamCMD helper function
|
|
#
|
|
function runSteamCMD(){
|
|
"$steamcmdroot/$steamcmdexec" +@NoPromptForPassword 1 +login ${steamlogin:-anonymous} "$@" +quit
|
|
}
|
|
|
|
function runSteamCMDspinner(){
|
|
if [ -n "$verbose" ]; then
|
|
echo
|
|
runSteamCMD "$@"
|
|
return $?
|
|
else
|
|
if [ -z "$progressDisplayType" ]; then
|
|
if stty <&1 >/dev/null 2>&1; then
|
|
progressDisplayType=spinner
|
|
else
|
|
progressDisplayType=dots
|
|
fi
|
|
fi
|
|
runSteamCMD "$@" >/dev/null 2>&1 &
|
|
local scpid=$!
|
|
local pos=0
|
|
local spinner=( '\b-' '\b/' '\b|' '\b\\' )
|
|
if [ "$progressDisplayType" == "dots" ]; then
|
|
spinner=( '.' )
|
|
fi
|
|
echo -n ' ... '
|
|
while kill -0 $scpid 2>/dev/null; do
|
|
echo -ne "${spinner[$pos]}"
|
|
(( pos = (pos + 1) % ${#spinner[*]} ))
|
|
sleep 0.5
|
|
done
|
|
echo -ne '\b \b'
|
|
wait $scpid
|
|
return $?
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Check if a new version is available but not apply it
|
|
#
|
|
function checkForUpdate(){
|
|
tput sc
|
|
echo "Querying Steam database for latest version..."
|
|
|
|
if isUpdateNeeded; then
|
|
tput rc; tput ed;
|
|
echo -e "Current version:" "$RED" $instver "$NORMAL"
|
|
echo -e "Available version:" "$GREEN" $bnumber "$NORMAL"
|
|
echo -e "Your server needs to be restarted in order to receive the latest update."
|
|
echo -e "Run \"arkmanager update\" to do so"
|
|
return 1
|
|
else
|
|
tput rc; tput ed;
|
|
echo -e "Current version:" "$GREEN" $instver "$NORMAL"
|
|
echo -e "Available version:" "$GREEN" $bnumber "$NORMAL"
|
|
echo "Your server is up to date!"
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Check if the server need to be updated
|
|
# Return 0 if update is needed, else return 1
|
|
#
|
|
function isUpdateNeeded(){
|
|
getCurrentVersion
|
|
getAvailableVersion
|
|
if [[ "$bnumber" == "Unknown" || "$bnumber" -eq "$instver" ]]; then
|
|
return 1 # no update needed
|
|
else
|
|
return 0 # update needed
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Parse an ACF structure
|
|
# $1 is the desired path
|
|
# $2 is the desired property
|
|
# $3 is the current path
|
|
#
|
|
function parseSteamACF(){
|
|
local sname
|
|
while read name val; do
|
|
name="${name#\"}"
|
|
name="${name%\"}"
|
|
val="${val#\"}"
|
|
val="${val%\"}"
|
|
if [ "$name" = "}" ]; then
|
|
break
|
|
elif [ "$name" == "{" ]; then
|
|
parseSteamACF "$1" "$2" "${3}.${sname}"
|
|
else
|
|
if [ "$3" == "$1" -a "$name" == "$2" ]; then
|
|
echo "$val"
|
|
break
|
|
fi
|
|
sname="${name}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# Return the current version number
|
|
#
|
|
function getCurrentVersion(){
|
|
if [ -f "${arkserverroot}/steamapps/appmanifest_${appid}.acf" ]; then
|
|
instver=`while read name val; do if [ "${name}" == "{" ]; then parseSteamACF "" "buildid"; break; fi; done <"${arkserverroot}/steamapps/appmanifest_${appid}.acf"`
|
|
echo $instver > "$arkserverroot/arkversion"
|
|
else
|
|
instver=""
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Get the current available server version on steamdb
|
|
#
|
|
function getAvailableVersion(){
|
|
rm -f "$steamcmd_appinfocache"
|
|
bnumber=`runSteamCMD +app_info_update 1 +app_info_print "$appid" +quit | while read name val; do if [ "${name}" == "{" ]; then parseSteamACF ".depots.branches.public" "buildid"; break; fi; done`
|
|
if [ -z "$bnumber" ]; then
|
|
bnumber="Unknown"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Get the PID of the server process
|
|
#
|
|
function getServerPID(){
|
|
ps -ef | grep "$arkserverroot/$arkserverexec" | grep -v grep | awk '{print $2}'
|
|
}
|
|
|
|
#
|
|
# Check id the server process is alive
|
|
#
|
|
function isTheServerRunning(){
|
|
if [ -n "`getServerPID`" ]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Check if the server is up
|
|
#
|
|
#
|
|
function isTheServerUp(){
|
|
$lsof -i "${ark_MultiHome:+udp@}${ark_MultiHome}:$(getGamePort)" > /dev/null
|
|
result=$?
|
|
if [ $result -ne 0 ]; then
|
|
perl -MSocket -MFcntl -e '
|
|
my $port = int($ARGV[0]);
|
|
socket(my $socket, PF_INET, SOCK_DGRAM, 0);
|
|
setsockopt($socket, SOL_SOCKET, SO_RCVTIMEO, pack("i4", 1, 0, 0, 0));
|
|
my $sockaddr = pack_sockaddr_in($port, inet_aton($ARGV[1]));
|
|
send($socket, "\xff\xff\xff\xffTSource Engine Query\x00", 0, $sockaddr);
|
|
my $flags = fcntl($socket, F_GETFL, 0) or exit(1);
|
|
fcntl($socket, F_SETFL, $flags | O_NONBLOCK) or exit(1);
|
|
my $data = "";
|
|
my $rin = "";
|
|
vec($rin, fileno($socket), 1) = 1;
|
|
if (select($rin, undef, undef, 0.25) >= 0) {
|
|
recv($socket, $data, 1400, 0) or exit(1);
|
|
my ($servername, $mapname, $game, $fullname, $rest) = split(/\x00/, substr($data, 6), 5);
|
|
my $maxplayers = ord(substr($rest, 3, 1));
|
|
if ($maxplayers == 0) { exit(1); }
|
|
exit(0);
|
|
} else {
|
|
exit(1);
|
|
}
|
|
' "$(getQueryPort)" "${ark_MultiHome:-127.0.0.1}"
|
|
result=$?
|
|
fi
|
|
# In this case, the result is:
|
|
# 1 if the command fail. The port is not listenning
|
|
# 0 if the command succeed. The port is listenning
|
|
if [ $result -eq 0 ];then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Check if the server is visible in the steam server list
|
|
#
|
|
function isTheServerOnline(){
|
|
if [ -n "$ark_MultiHome" ]; then
|
|
publicip="$(curl --interface "${ark_MultiHome}" -s https://api.ipify.org/)"
|
|
else
|
|
publicip="$(curl -s https://api.ipify.org/)"
|
|
fi
|
|
local serverresp
|
|
|
|
if [[ "$publicip" =~ [1-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]* ]]; then
|
|
serverresp="$(curl -s "http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=${publicip}:$(getQueryPort)")"
|
|
fi
|
|
|
|
# If the Steam server response contains "addr": "$ip:$port",
|
|
# then the server has registered with the Steam master server
|
|
if [[ "$serverresp" =~ "\"addr\": \""([^\"]*):([0-9]*)"\"" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Check if anybody is connected to the server
|
|
#
|
|
function numPlayersConnected(){
|
|
perl -MSocket -e '
|
|
my $port = int($ARGV[0]);
|
|
socket(my $socket, PF_INET, SOCK_DGRAM, 0);
|
|
setsockopt($socket, SOL_SOCKET, SO_RCVTIMEO, pack("i4", 1, 0, 0, 0));
|
|
my $sockaddr = pack_sockaddr_in($port, inet_aton($ARGV[1]));
|
|
send($socket, "\xff\xff\xff\xffTSource Engine Query\x00", 0, $sockaddr);
|
|
my $data = "";
|
|
recv($socket, $data, 1400, 0) or (print "0" and exit(1));
|
|
my ($servername, $mapname, $game, $fullname, $rest) = split(/\x00/, substr($data, 6), 5);
|
|
my $players = ord(substr($rest, 2, 1));
|
|
print "$players\n";
|
|
' "${ark_QueryPort}" "${ark_MultiHome:-127.0.0.1}"
|
|
}
|
|
|
|
#
|
|
# run function
|
|
#
|
|
doRun() {
|
|
cd "$arkserverroot"
|
|
|
|
arkserveropts="$serverMap"
|
|
|
|
if [ -n "$serverMapModId" ]; then
|
|
arkserveropts="-MapModID=$serverMapModId"
|
|
fi
|
|
|
|
if [ -z "$arkserveropts" ]; then
|
|
arkserveropts="TheIsland"
|
|
fi
|
|
|
|
arkextraopts=( )
|
|
|
|
# bring in ark_... options
|
|
for varname in "${!ark_@}"; do
|
|
name="${varname#ark_}"
|
|
val="${!varname}"
|
|
|
|
# Port is actually one higher than specified
|
|
# i.e. specifying port 7777 will have the server
|
|
# use port 7778
|
|
if [ "$name" == "Port" ]; then
|
|
(( val = val - 1 ))
|
|
fi
|
|
|
|
if [ -n "$val" ]; then
|
|
arkserveropts="${arkserveropts}?${name}=${val}"
|
|
else
|
|
arkserveropts="${arkserveropts}?${name}"
|
|
fi
|
|
done
|
|
|
|
# bring in arkflag_... flags
|
|
for varname in "${!arkflag_@}"; do
|
|
name="${varname#arkflag_}"
|
|
|
|
arkextraopts=( "${arkextraopts[@]}" "-${name}" )
|
|
done
|
|
|
|
# bring in arkopt_... options
|
|
for varname in "${!arkopt_@}"; do
|
|
name="${varname#arkopt_}"
|
|
val="${!varname}"
|
|
|
|
if [ -n "$val" ]; then
|
|
arkextraopts=( "${arkextraopts[@]}" "-${name}=${val}" )
|
|
fi
|
|
done
|
|
|
|
arkserveropts="${arkserveropts}?listen"
|
|
# run the server in background
|
|
echo "`timestamp`: start"
|
|
# set max open files limit before we start the server
|
|
ulimit -n $maxOpenFiles
|
|
|
|
serverpid=0
|
|
restartserver=1
|
|
|
|
# Shutdown the server when we are terminated
|
|
shutdown_server(){
|
|
restartserver=0
|
|
rm "$arkserverroot/$arkautorestartfile"
|
|
if [ "$serverpid" -ne 0 ]; then
|
|
kill -INT $serverpid
|
|
fi
|
|
}
|
|
|
|
trap shutdown_server INT TERM
|
|
|
|
# Auto-restart loop
|
|
while [ $restartserver -ne 0 ]; do
|
|
echo -n "`timestamp`: Running"
|
|
printf " %q" "$arkserverroot/$arkserverexec" "$arkserveropts" "${arkextraopts[@]}"
|
|
echo
|
|
# Put the server process into the background so we can monitor it
|
|
"$arkserverroot/$arkserverexec" "$arkserveropts" "${arkextraopts[@]}" &
|
|
# Grab the server PID
|
|
serverpid=$!
|
|
echo "`timestamp`: Server PID: $serverpid"
|
|
# Disable auto-restart so we don't get caught in a restart loop
|
|
rm -f "$arkserverroot/$arkautorestartfile"
|
|
restartserver=0
|
|
|
|
sleep 5
|
|
|
|
while true; do
|
|
# Grab the current server PID
|
|
local pid="`getServerPID`"
|
|
if [ "$pid" == "$serverpid" ]; then
|
|
if [ "$restartserver" -eq 0 ]; then
|
|
# Check if the server has fully started
|
|
if ! isTheServerUp; then
|
|
# Enable auto-restart if the server is up
|
|
echo "`timestamp`: server is up"
|
|
touch "$arkserverroot/$arkautorestartfile"
|
|
restartserver=1
|
|
fi
|
|
fi
|
|
else
|
|
echo "`timestamp`: Bad PID '$pid'; expected '$serverpid'"
|
|
if [ "$pid" != "" ]; then
|
|
# Another instance must be running - disable autorestart
|
|
restartserver=0
|
|
fi
|
|
break
|
|
fi
|
|
sleep 5
|
|
done
|
|
|
|
# Wait on the now-dead process to reap it and get its return status
|
|
wait $serverpid
|
|
echo "`timestamp`: exited with status $?"
|
|
|
|
# doStop will remove the autorestart file
|
|
if [ ! -f "$arkserverroot/$arkautorestartfile" ]; then
|
|
restartserver=0
|
|
fi
|
|
|
|
if [ "$restartserver" -ne 0 ]; then
|
|
echo "`timestamp`: restarting server"
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# start function
|
|
#
|
|
doStart() {
|
|
if isTheServerRunning; then
|
|
echo "The server is already running"
|
|
else
|
|
if [ "$arkAutoUpdateOnStart" == "true" ]; then
|
|
if ! [[ " $* " =~ " --noautoupdate " ]]; then
|
|
echo "Updating server"
|
|
doUpdate --update-mods
|
|
fi
|
|
fi
|
|
tput sc
|
|
echo "The server is starting..."
|
|
|
|
doRun </dev/null >>"$logdir/$arkserverLog" 2>&1 & # output of this command is logged
|
|
echo "`timestamp`: start" >> "$logdir/$arkmanagerLog"
|
|
tput rc; tput ed;
|
|
echo "The server is now running, and should be up within 10 minutes"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# starts all servers specified by configfile_xxxxx in config file
|
|
#
|
|
doStartAll(){
|
|
doStart
|
|
for cfg in "${!configfile_@}"; do
|
|
if [ -f "${!cfg}" ]; then
|
|
(
|
|
source "${!cfg}"
|
|
doStart
|
|
)
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# stop the ARK server
|
|
#
|
|
doStop() {
|
|
if isTheServerRunning; then
|
|
if [[ " $* " =~ " --warn " ]]; then
|
|
doWarn "$1"
|
|
fi
|
|
if [[ " $* " =~ " --saveworld " ]]; then
|
|
doSaveWorld
|
|
fi
|
|
tput sc
|
|
echo "Stopping server..."
|
|
echo "`timestamp`: stopping" >> "$logdir/$arkmanagerLog"
|
|
rm -f "$arkserverroot/$arkautorestartfile"
|
|
# kill the server with the PID
|
|
PID=`getServerPID`
|
|
kill -INT $PID
|
|
|
|
for (( i = 0; i < 20; i++ )); do
|
|
sleep 1
|
|
if ! isTheServerRunning; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
if isTheServerRunning; then
|
|
tput rc
|
|
echo "Killing server..."
|
|
kill -KILL $PID
|
|
fi
|
|
|
|
tput rc; tput ed;
|
|
echo "The server has been stopped"
|
|
echo "`timestamp`: stopped" >> "$logdir/$arkmanagerLog"
|
|
else
|
|
echo "The server is already stopped"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# stops all servers specified by configfile_xxxxx in config file
|
|
#
|
|
doStopAll(){
|
|
doStop
|
|
for cfg in "${!configfile_@}"; do
|
|
if [ -f "${!cfg}" ]; then
|
|
(
|
|
source "${!cfg}"
|
|
doStop
|
|
)
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# install / update / download update
|
|
#
|
|
runSteamCMDAppUpdate(){
|
|
runSteamCMDspinner +force_install_dir "$1" +app_update $appid $2
|
|
}
|
|
|
|
#
|
|
# install of ARK server
|
|
#
|
|
doInstall() {
|
|
# Check if arkserverroot already exists
|
|
if [ ! -d "$arkserverroot" ]; then
|
|
# If it does not exist, try create it
|
|
echo -e "Creating the ARK server root directory ($arkserverroot)"
|
|
mkdir -p "$arkserverroot"
|
|
if [ ! $? ] ; then
|
|
echo -e "[" "$RED" "ERROR" "$NORMAL" "]" "\tFailed to create the defined ARK server root directory ($arkserverroot)"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
cd "$steamcmdroot"
|
|
echo -n "Installing ARK server"
|
|
# install the server
|
|
runSteamCMDAppUpdate "$arkserverroot" validate
|
|
# the current version should be the last version. We set our version
|
|
getCurrentVersion
|
|
}
|
|
|
|
#
|
|
# Waits for a configurable number of minutes before updating the server
|
|
#
|
|
doWarn(){
|
|
cd "$arkserverroot"
|
|
|
|
local warnmsgmin
|
|
local warnmsgsec
|
|
|
|
if [ "$1" == "update" ]; then
|
|
if [ -n "$msgWarnUpdateMinutes" ]; then
|
|
warnmsgmin="$msgWarnUpdateMinutes"
|
|
else
|
|
warnmsgmin="This ARK server will shutdown for an update in %d minutes"
|
|
fi
|
|
if [ -n "$msgWarnUpdateSeconds" ]; then
|
|
warnmsgsec="$msgWarnUpdateSeconds"
|
|
else
|
|
warnmsgsec="This ARK server will shutdown for an update in %d seconds"
|
|
fi
|
|
elif [ "$1" == "restart" ]; then
|
|
if [ -n "$msgWarnRestartMinutes" ]; then
|
|
warnmsgmin="$msgWarnRestartMinutes"
|
|
else
|
|
warnmsgmin="This ARK server will shutdown for a restart in %d minutes"
|
|
fi
|
|
if [ -n "$msgWarnRestartSeconds" ]; then
|
|
warnmsgsec="$msgWarnRestartSeconds"
|
|
else
|
|
warnmsgsec="This ARK server will shutdown for a restart in %d seconds"
|
|
fi
|
|
else
|
|
if [ -n "$msgWarnShutdownMinutes" ]; then
|
|
warnmsgmin="$msgWarnShutdownMinutes"
|
|
else
|
|
warnmsgmin="This ARK server will shutdown in %d minutes"
|
|
fi
|
|
if [ -n "$msgWarnShutdownSeconds" ]; then
|
|
warnmsgsec="$msgWarnShutdownSeconds"
|
|
else
|
|
warnmsgsec="This ARK server will shutdown in %d seconds"
|
|
fi
|
|
fi
|
|
|
|
local pid=`getServerPID`
|
|
local sleeppid
|
|
if [ -n "$pid" ]; then
|
|
local warnmsg
|
|
local warnminutes=$(( arkwarnminutes ))
|
|
if (( warnminutes == 0 )); then
|
|
warnminutes=60
|
|
fi
|
|
|
|
local warnintervals=( 90 60 45 30 20 15 10 5 4 3 2 )
|
|
|
|
for warninterval in "${warnintervals[@]}"; do
|
|
if [ "`getServerPID`" != "$pid" ]; then
|
|
echo "Server has stopped. Aborting $1"
|
|
return 1
|
|
fi
|
|
if (( warnminutes > warninterval )); then
|
|
sleep 1m &
|
|
sleeppid=$!
|
|
warnmsg="$(printf "$warnmsgmin" "$warnminutes")"
|
|
doBroadcastWithEcho "$warnmsg"
|
|
for (( min = warnminutes - 1; min >= warninterval; min-- )); do
|
|
numplayers=$(numPlayersConnected)
|
|
if (( numplayers + 0 == 0 )); then
|
|
echo "Nobody is connected. Shutting down immediately"
|
|
return 0
|
|
fi
|
|
wait $sleeppid
|
|
if (( $min > $warninterval )); then
|
|
sleep 1m &
|
|
sleeppid=$!
|
|
fi
|
|
done
|
|
warnminutes=$warninterval
|
|
fi
|
|
done
|
|
|
|
local warnseconds=120
|
|
warnintervals=( 90 60 45 30 20 15 10 5 0 )
|
|
for warninterval in "${warnintervals[@]}"; do
|
|
sleep $(( warnseconds - warninterval ))s &
|
|
sleeppid=$!
|
|
if [ "`getServerPID`" != "$pid" ]; then
|
|
echo "Server has stopped. Aborting update"
|
|
return 1
|
|
fi
|
|
warnmsg="$(printf "$warnmsgsec" "$warnseconds")"
|
|
doBroadcastWithEcho "$warnmsg"
|
|
if (( warnseconds >= 20 )); then
|
|
numplayers=$(numPlayersConnected)
|
|
if (( numplayers + 0 == 0 )); then
|
|
echo "Nobody is connected. Shutting down immediately"
|
|
return 0
|
|
fi
|
|
fi
|
|
wait $sleeppid
|
|
warnseconds=$warninterval
|
|
done
|
|
fi
|
|
|
|
if [ "`getServerPID`" != "$pid" ]; then
|
|
echo "Server has stopped. Aborting $1"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# Stop the server, update it and then start it back.
|
|
#
|
|
doUpdate() {
|
|
local appupdate=
|
|
local updatetype=normal
|
|
local validate=
|
|
local modupdate=
|
|
local saveworld=
|
|
local downloadonly=
|
|
|
|
for arg in "$@"; do
|
|
if [ "$arg" == "--force" ]; then
|
|
appupdate=1
|
|
elif [ "$arg" == "--safe" ]; then
|
|
updatetype=safe
|
|
elif [ "$arg" == "--warn" ]; then
|
|
updatetype=warn
|
|
elif [ "$arg" == "--ifempty" ]; then
|
|
updatetype=ifempty
|
|
elif [ "$arg" == "--validate" ]; then
|
|
validate=validate
|
|
appupdate=1
|
|
elif [ "$arg" == "--saveworld" ]; then
|
|
saveworld=1
|
|
elif [ "$arg" == "--update-mods" ]; then
|
|
modupdate=1
|
|
elif [ "$arg" == "--backup" ]; then
|
|
arkBackupPreUpdate=true
|
|
elif [[ "$arg" =~ "^--stagingdir=" ]]; then
|
|
arkStagingDir="${ark#--stagingdir=}"
|
|
elif [ "$arg" == "--downloadonly" ]; then
|
|
downloadonly=1
|
|
else
|
|
echo "Unrecognized option $arg"
|
|
echo "Try 'arkmanager -h' or 'arkmanager --help' for more information."
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo "$$" >"${arkserverroot}/.ark-update.lock.$$" 2>/dev/null
|
|
while true; do
|
|
if ! ln "${arkserverroot}/.ark-update.lock.$$" "${arkserverroot}/.ark-update.lock" 2>/dev/null; then
|
|
local lockpid="$(<"${arkserverroot}/.ark-update.lock")"
|
|
if [ -n "$lockpid" ] && [ "$lockpid" != "$$" ] && kill -0 "$lockpid" 2>/dev/null; then
|
|
echo "Update already in progress (PID: $lockpid)"
|
|
rm -f "${arkserverroot}/.ark-update.lock.$$" 2>/dev/null
|
|
return 1
|
|
fi
|
|
rm -f "${arkserverroot}/.ark-update.lock"
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
rm -f "${arkserverroot}/.ark-update.lock.$$"
|
|
|
|
if [ -n "$modupdate" ]; then
|
|
if ! doDownloadAllMods; then
|
|
modupdate=
|
|
fi
|
|
if ! isAnyModUpdateNeeded; then
|
|
modupdate=
|
|
fi
|
|
fi
|
|
|
|
cd "$arkserverroot"
|
|
|
|
if isUpdateNeeded; then
|
|
appupdate=1
|
|
|
|
if [ -n "${arkStagingDir}" -a "${arkStagingDir}" != "${arkserverroot}" ]; then
|
|
if [ ! -d "$arkStagingDir/ShooterGame" ]; then
|
|
echo "Copying to staging directory"
|
|
mkdir -p "$arkStagingDir"
|
|
if [ "$(stat -c "%d" "$arkserverroot")" == "$(stat -c "%d" "$arkStagingDir")" ]; then
|
|
cp -al "$arkserverroot/ShooterGame/." "$arkStagingDir/ShooterGame"
|
|
cp -al "$arkserverroot/Engine/." "$arkStagingDir/Engine"
|
|
cp -al "$arkserverroot/linux64/." "$arkStagingDir/linux64"
|
|
cp -al "$arkserverroot/PackageInfo.bin" "$arkStagingDir/PackageInfo.bin"
|
|
cp -al "$arkserverroot/steamclient.so" "$arkStagingDir/steamclient.so"
|
|
cp -a "$arkserverroot/steamapps/." "$arkStagingDir/steamapps"
|
|
else
|
|
rsync -a "$arkserverroot/." "$arkStagingDir/."
|
|
fi
|
|
rm -rf "$arkStagingDir/ShooterGame/Content/Mods/"*
|
|
rm -rf "$arkStagingDir/ShooterGame/Saved/"*
|
|
fi
|
|
|
|
echo -n "Downloading ARK update"
|
|
cd "$steamcmdroot"
|
|
runSteamCMDAppUpdate "$arkStagingDir" $validate
|
|
if [ -d "${arkStagingDir}/steamapps/downloading/${appid}" ]; then
|
|
echo "Update download interrupted"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$downloadonly" ]; then
|
|
if [ -n "$appupdate" -a -n "$arkStagingDir" -a "$arkStagingDir" != "$arkserverroot" ]; then
|
|
echo "Server update downloaded"
|
|
fi
|
|
if [ -n "$modupdate" ]; then
|
|
echo "Mod update downloaded"
|
|
fi
|
|
echo "Not applying update - download-only enabled"
|
|
elif [ -n "$appupdate" -o -n "$modupdate" ]; then
|
|
if isTheServerRunning; then
|
|
if [ "$updatetype" == "safe" ]; then
|
|
while [ ! `find $arkserverroot/ShooterGame/Saved/SavedArks -mmin -1 -name ${serverMap##*/}.ark` ]; do
|
|
echo "`timestamp`: Save file older than 1 minute. Delaying update." >> "$logdir/update.log"
|
|
sleep 30s
|
|
done
|
|
echo "`timestamp`: Save file newer than 1 minute. Performing an update." >> "$logdir/update.log"
|
|
elif [ "$updatetype" == "warn" ]; then
|
|
if ! doWarn update; then
|
|
return 1
|
|
fi
|
|
elif [ "$updatetype" == "ifempty" ]; then
|
|
numplayers=$(( $(numPlayersConnected) + 0 ))
|
|
if (( numplayers == 0 )); then
|
|
echo "${numplayers} players are still connected"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# check if the server was alive before the update so we can launch it back after the update
|
|
serverWasAlive=0
|
|
if isTheServerRunning ;then
|
|
serverWasAlive=1
|
|
fi
|
|
|
|
if [ -n "$saveworld" ]; then
|
|
echo "Saving world"
|
|
doSaveWorld
|
|
fi
|
|
|
|
doStop
|
|
|
|
# If user wants to back-up, we do it here.
|
|
|
|
if [ "$arkBackupPreUpdate" == "true" ]; then
|
|
doBackup
|
|
fi
|
|
|
|
if [ -n "$appupdate" ]; then
|
|
if [ -d "${arkStagingDir}" -a "${arkStagingDir}" != "${arkserverroot}" ]; then
|
|
echo "Applying update from staging directory"
|
|
if [ "$(stat -c "%d" "$arkserverroot")" == "$(stat -c "%d" "$arkStagingDir")" ]; then
|
|
cp -alu --remove-destination "$arkStagingDir/ShooterGame/." "$arkserverroot/ShooterGame"
|
|
cp -alu --remove-destination "$arkStagingDir/Engine/." "$arkserverroot/Engine"
|
|
cp -alu --remove-destination "$arkStagingDir/linux64/." "$arkserverroot/linux64"
|
|
cp -alu --remove-destination "$arkStagingDir/PackageInfo.bin" "$arkserverroot/PackageInfo.bin"
|
|
cp -alu --remove-destination "$arkStagingDir/steamclient.so" "$arkserverroot/steamclient.so"
|
|
cp -au --remove-destination "$arkStagingDir/steamapps/." "$arkserverroot/steamapps"
|
|
else
|
|
rsync -a "$arkStagingDir/." "$arkserverroot"
|
|
fi
|
|
cd "$arkserverroot"
|
|
find Engine ShooterGame linux64 -depth -print |
|
|
grep -v '^ShooterGame/\(Saved\|Content/Mods\)' |
|
|
while read f; do
|
|
if [ ! -e "${arkStagingDir}/${f}" ]; then
|
|
if [ -f "$f" ]; then
|
|
rm "${f}"
|
|
else
|
|
rmdir "${f}"
|
|
fi
|
|
fi
|
|
done
|
|
else
|
|
echo -n "Performing ARK update"
|
|
cd "$steamcmdroot"
|
|
runSteamCMDAppUpdate "$arkserverroot" $validate
|
|
fi
|
|
# the current version should be the last version. We set our version
|
|
getCurrentVersion
|
|
echo "`timestamp`: update to $instver complete" >> "$logdir/update.log"
|
|
fi
|
|
|
|
if [ -n "$modupdate" ]; then
|
|
for modid in $(getModIds); do
|
|
if isModUpdateNeeded $modid; then
|
|
echo "Updating mod $modid"
|
|
doExtractMod $modid
|
|
echo "`timestamp`: Mod $modid updated" >> "$logdir/update.log"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# we restart the server only if it was started before the update
|
|
if [ $serverWasAlive -eq 1 ]; then
|
|
doStart --noautoupdate
|
|
fi
|
|
else
|
|
echo "Your server is already up to date! The most recent version is ${bnumber}."
|
|
echo "`timestamp`: No update needed." >> "$logdir/update.log"
|
|
fi;
|
|
|
|
rm -f "${arkserverroot}/.ark-update.lock"
|
|
}
|
|
|
|
#
|
|
# Get the Mod IDs of the installed mods and the requested mods
|
|
#
|
|
getModIds(){
|
|
(
|
|
echo "${serverMapModId}"
|
|
echo "${ark_TotalConversionMod}"
|
|
echo "${ark_GameModIds}" | tr ',' '\n'
|
|
find "${arkserverroot}/ShooterGame/Content/Mods" -maxdepth 1 -type d -printf "%P\n"
|
|
) | sort | uniq | grep '^[1-9][0-9]*$'
|
|
}
|
|
|
|
#
|
|
# Downloads a mod from the Steam workshop
|
|
#
|
|
doDownloadMod(){
|
|
local modid=$1
|
|
local modsrcdir="$steamcmdroot/steamapps/workshop/content/$mod_appid/$modid"
|
|
local moddldir="$steamcmdroot/steamapps/workshop/downloads/$mod_appid"
|
|
cd "$steamcmdroot"
|
|
retries=10
|
|
|
|
while true; do
|
|
echo -n "Downloading mod $modid"
|
|
runSteamCMDspinner +workshop_download_item $mod_appid $modid
|
|
result=$?
|
|
if [ $result -eq 0 ]; then
|
|
break
|
|
else
|
|
echo
|
|
if [ ! -d "$moddldir" ]; then
|
|
echo "Mod $modid download failed"
|
|
break
|
|
fi
|
|
(( retries = retries - 1 ))
|
|
if (( retries <= 0 )); then
|
|
echo "Retries exhausted"
|
|
fi
|
|
echo "Mod $modid not fully downloaded - retrying"
|
|
fi
|
|
done
|
|
|
|
if [ -f "$modsrcdir/mod.info" ]; then
|
|
echo "Mod $modid downloaded"
|
|
return 0
|
|
else
|
|
echo "Mod $modid was not successfully downloaded"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Downloads all installed and requested mods from the Steam workshop
|
|
#
|
|
doDownloadAllMods(){
|
|
for modid in $(getModIds); do
|
|
doDownloadMod $modid || return 1
|
|
done
|
|
}
|
|
|
|
#
|
|
# Checks if the files a mod owns need to be updated
|
|
#
|
|
isModUpdateNeeded(){
|
|
local modid=$1
|
|
local modsrcdir="$steamcmdroot/steamapps/workshop/content/$mod_appid/$modid"
|
|
local moddestdir="$arkserverroot/ShooterGame/Content/Mods/$modid"
|
|
local modbranch="${mod_branch:-Windows}"
|
|
|
|
for varname in "${!mod_branch_@}"; do
|
|
if [ "mod_branch_$modid" == "$varname" ]; then
|
|
modbranch="${!varname}"
|
|
fi
|
|
done
|
|
|
|
if [ \( ! -f "$moddestdir/.modbranch" \) ] || [ "$(<"$moddestdir/.modbranch")" != "$modbranch" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if [ -f "$modsrcdir/mod.info" ]; then
|
|
if [ -f "$modsrcdir/${modbranch}NoEditor/mod.info" ]; then
|
|
modsrcdir="$modsrcdir/${modbranch}NoEditor"
|
|
fi
|
|
|
|
while read f; do
|
|
if [ \( ! -f "$moddestdir/${f%.z}" \) -o "$modsrcdir/$f" -nt "$moddestdir/${f%.z}" ]; then
|
|
return 0
|
|
fi
|
|
done < <(find "$modsrcdir" -type f ! -name "*.z.uncompressed_size" -printf "%P\n")
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
#
|
|
# Checks if any installed or requested mods need to be updated
|
|
#
|
|
isAnyModUpdateNeeded(){
|
|
for modid in $(getModIds); do
|
|
if isModUpdateNeeded $modid; then
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
#
|
|
# Extracts a mod into the ARK Mods directory
|
|
#
|
|
doExtractMod(){
|
|
local modid=$1
|
|
local modsrcdir="$steamcmdroot/steamapps/workshop/content/$mod_appid/$modid"
|
|
local moddestdir="$arkserverroot/ShooterGame/Content/Mods/$modid"
|
|
local modbranch="${mod_branch:-Windows}"
|
|
|
|
for varname in "${!mod_branch_@}"; do
|
|
if [ "mod_branch_$modid" == "$varname" ]; then
|
|
modbranch="${!varname}"
|
|
fi
|
|
done
|
|
|
|
if [ \( ! -f "$moddestdir/.modbranch" \) ] || [ "$(<"$moddestdir/.modbranch")" != "$modbranch" ]; then
|
|
rm -rf "$moddestdir"
|
|
fi
|
|
|
|
if [ -f "$modsrcdir/mod.info" ]; then
|
|
echo "Copying files to $moddestdir"
|
|
|
|
if [ -f "$modsrcdir/${modbranch}NoEditor/mod.info" ]; then
|
|
modsrcdir="$modsrcdir/${modbranch}NoEditor"
|
|
fi
|
|
|
|
find "$modsrcdir" -type d -printf "$moddestdir/%P\0" | xargs -0 -r mkdir -p
|
|
|
|
find "$moddestdir" -type f ! -name '.*' -printf "%P\n" | while read f; do
|
|
if [ \( ! -f "$modsrcdir/$f" \) -a \( ! -f "$modsrcdir/${f}.z" \) ]; then
|
|
rm "$moddestdir/$f"
|
|
fi
|
|
done
|
|
|
|
find "$moddestdir" -depth -type d -printf "%P\n" | while read d; do
|
|
if [ ! -d "$modsrcdir/$d" ]; then
|
|
rmdir "$moddestdir/$d"
|
|
fi
|
|
done
|
|
|
|
find "$modsrcdir" -type f ! \( -name '*.z' -or -name '*.z.uncompressed_size' \) -printf "%P\n" | while read f; do
|
|
if [ \( ! -f "$moddestdir/$f" \) -o "$modsrcdir/$f" -nt "$moddestdir/$f" ]; then
|
|
printf "%10d %s " "`stat -c '%s' "$modsrcdir/$f"`" "$f"
|
|
cp "$modsrcdir/$f" "$moddestdir/$f"
|
|
echo -ne "\r\\033[K"
|
|
fi
|
|
done
|
|
|
|
find "$modsrcdir" -type f -name '*.z' -printf "%P\n" | while read f; do
|
|
if [ \( ! -f "$moddestdir/${f%.z}" \) -o "$modsrcdir/$f" -nt "$moddestdir/${f%.z}" ]; then
|
|
printf "%10d %s " "`stat -c '%s' "$modsrcdir/$f"`" "${f%.z}"
|
|
perl -M'Compress::Raw::Zlib' -e '
|
|
my $sig;
|
|
read(STDIN, $sig, 8) or die "Unable to read compressed file: $!";
|
|
if ($sig != "\xC1\x83\x2A\x9E\x00\x00\x00\x00"){
|
|
die "Bad file magic";
|
|
}
|
|
my $data;
|
|
read(STDIN, $data, 24) or die "Unable to read compressed file: $!";
|
|
my ($chunksizelo, $chunksizehi,
|
|
$comprtotlo, $comprtothi,
|
|
$uncomtotlo, $uncomtothi) = unpack("(LLLLLL)<", $data);
|
|
my @chunks = ();
|
|
my $comprused = 0;
|
|
while ($comprused < $comprtotlo) {
|
|
read(STDIN, $data, 16) or die "Unable to read compressed file: $!";
|
|
my ($comprsizelo, $comprsizehi,
|
|
$uncomsizelo, $uncomsizehi) = unpack("(LLLL)<", $data);
|
|
push @chunks, $comprsizelo;
|
|
$comprused += $comprsizelo;
|
|
}
|
|
foreach my $comprsize (@chunks) {
|
|
read(STDIN, $data, $comprsize) or die "File read failed: $!";
|
|
my ($inflate, $status) = new Compress::Raw::Zlib::Inflate();
|
|
my $output;
|
|
$status = $inflate->inflate($data, $output, 1);
|
|
if ($status != Z_STREAM_END) {
|
|
die "Bad compressed stream; status: " . ($status);
|
|
}
|
|
if (length($data) != 0) {
|
|
die "Unconsumed data in input"
|
|
}
|
|
print $output;
|
|
}
|
|
' <"$modsrcdir/$f" >"$moddestdir/${f%.z}"
|
|
touch -c -r "$modsrcdir/$f" "$moddestdir/${f%.z}"
|
|
echo -ne "\r\\033[K"
|
|
fi
|
|
done
|
|
|
|
modname="$(curl -s "http://steamcommunity.com/sharedfiles/filedetails/?id=${modid}" | sed -n 's|^.*<div class="workshopItemTitle">\([^<]*\)</div>.*|\1|p')"
|
|
|
|
if [ -f "${moddestdir}/.mod" ]; then
|
|
rm "${moddestdir}/.mod"
|
|
fi
|
|
|
|
perl -e '
|
|
my $data;
|
|
{ local $/; $data = <STDIN>; }
|
|
my $mapnamelen = unpack("@0 L<", $data);
|
|
my $mapname = substr($data, 4, $mapnamelen - 1);
|
|
my $mapfilelen = unpack("@" . ($mapnamelen + 8) . " L<", $data);
|
|
my $mapfile = substr($data, $mapnamelen + 12, $mapfilelen);
|
|
my $modname = ($ARGV[1] || $mapname) . "\x00";
|
|
my $modnamelen = length($modname);
|
|
my $modpath = "../../../ShooterGame/Content/Mods/" . $ARGV[0] . "\x00";
|
|
my $modpathlen = length($modpath);
|
|
print pack("L< L< L< Z$modnamelen L< Z$modpathlen L< L< Z$mapfilelen",
|
|
$ARGV[0], 0, $modnamelen, $modname, $modpathlen, $modpath,
|
|
1, $mapfilelen, $mapfile);
|
|
print "\x33\xFF\x22\xFF\x02\x00\x00\x00\x01";
|
|
' $modid "$modname" <"$moddestdir/mod.info" >"${moddestdir}.mod"
|
|
|
|
if [ -f "$moddestdir/modmeta.info" ]; then
|
|
cat "$moddestdir/modmeta.info" >>"${moddestdir}.mod"
|
|
else
|
|
echo -ne '\x01\x00\x00\x00\x08\x00\x00\x00ModType\x00\x02\x00\x00\x001\x00' >>"${moddestdir}.mod"
|
|
fi
|
|
|
|
echo "$modbranch" >"$moddestdir/.modbranch"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Downloads mod and installs it into mods directory
|
|
#
|
|
doInstallMod(){
|
|
local modid=$1
|
|
|
|
if [ -f "$steamcmdroot/steamapps/workshop/appworkshop_${mod_appid}.acf" ]; then
|
|
sed -i "/^\\t\\t\"${modid}\"/,/^\\t\\t}/d" "$steamcmdroot/steamapps/workshop/appworkshop_${mod_appid}.acf"
|
|
fi
|
|
|
|
if doDownloadMod $modid; then
|
|
doExtractMod $modid
|
|
echo "Mod $modid installed"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Removes mod from mods directory
|
|
#
|
|
doUninstallMod(){
|
|
local modid=$1
|
|
local moddir="$arkserverroot/ShooterGame/Content/Mods/$modid"
|
|
if [ -d "${moddir}" ]; then
|
|
rm -rf "${moddir}"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Copies server state to a backup directory
|
|
#
|
|
doBackup(){
|
|
local datestamp=`date +"%Y-%m-%d_%H.%M.%S"`
|
|
local daystamp=`date +"%Y-%m-%d"`
|
|
local backupdir="${arkbackupdir}/${datestamp}"
|
|
local backupdirdaily="${arkbackupdir}/${daystamp}"
|
|
local savedir="SavedArks"
|
|
mkdir -p "$backupdir"
|
|
mkdir -p "$backupdirdaily"
|
|
|
|
# extract the map name from the active map mod
|
|
if [ -n "$serverMapModId" ]; then
|
|
serverMap="$(perl -e '
|
|
my $data;
|
|
{ local $/; $data = <>; }
|
|
my $mapnamelen = unpack("@0 L<", $data);
|
|
my $mapname = substr($data, 4, $mapnamelen - 1);
|
|
$mapnamelen += 4;
|
|
my $mapfilelen = unpack("@" . ($mapnamelen + 4) . " L<", $data);
|
|
my $mapfile = substr($data, $mapnamelen + 8, $mapfilelen - 1);
|
|
print $mapfile;
|
|
' <"${arkserverroot}/ShooterGame/Content/Mods/${serverMapModId}/mod.info")"
|
|
fi
|
|
|
|
# Get save directory name
|
|
if [ -n "${ark_AltSaveDirectoryName}" ]; then
|
|
savedir="${ark_AltSaveDirectoryName}"
|
|
fi
|
|
|
|
# ARK server uses Write-Unlink-Rename
|
|
echo -ne "${NORMAL} Copying ARK world file "
|
|
cp -p "${arkserverroot}/ShooterGame/Saved/${savedir}/${serverMap##*/}.ark" "${backupdir}/${serverMap##*/}.ark"
|
|
if [ ! -f "${backupdir}/${serverMap##*/}.ark" ]; then
|
|
sleep 2
|
|
cp -p "${arkserverroot}/ShooterGame/Saved/${savedir}/${serverMap##*/}.ark" "${backupdir}/${serverMap##*/}.ark"
|
|
fi
|
|
# If both attempts fail, server may have
|
|
# crashed between unlink and rename
|
|
if [ ! -f "${backupdir}/${serverMap##*/}.ark" ]; then
|
|
cp -p "${arkserverroot}/ShooterGame/Saved/${savedir}/${serverMap##*/}.tmp" "${backupdir##*/}/${serverMap##*/}.ark"
|
|
fi
|
|
if [ -f "${backupdir}/${serverMap##*/}.ark" ]; then
|
|
echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}\e[68G[ ${RED}FAILED${NORMAL} ]"
|
|
fi
|
|
|
|
# ARK server uses Lock-Truncate-Write-Unlock
|
|
# Unfortunately we can't lock the file, as
|
|
# ARK server uses a non-blocking lock and will
|
|
# fail to update the file if the lock fails.
|
|
echo -e "${NORMAL} Copying ARK profile files"
|
|
for f in "${arkserverroot}/ShooterGame/Saved/${savedir}/"*.arkprofile; do
|
|
echo -ne "${NORMAL} ${f##*/} "
|
|
cp -p "${f}" "${backupdir}/${f##*/}"
|
|
if [ ! -s "${backupdir}/${f##*/}" ]; then
|
|
sleep 2
|
|
cp -p "${f}" "${backupdir}/${f##*/}"
|
|
fi
|
|
# If both attempts fail, server may have
|
|
# crashed between truncate and write
|
|
if [ ! -s "${backupdir}/${f##*/}" ]; then
|
|
cp -p "${f%.arkprofile}.tmpprofile" "${backupdir}/${f##*/}"
|
|
fi
|
|
if [ -s "${backupdir}/${f##*/}" ]; then
|
|
echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}\e[68G[ ${RED}FAILED${NORMAL} ]"
|
|
fi
|
|
done
|
|
|
|
# ARK server uses Lock-Truncate-Write-Unlock
|
|
echo -e "${NORMAL} Copying ARK tribe files "
|
|
for f in "${arkserverroot}/ShooterGame/Saved/${savedir}/"*.arktribe; do
|
|
echo -ne "${NORMAL} ${f##*/} "
|
|
cp -p "${f}" "${backupdir}/${f##*/}"
|
|
if [ ! -s "${backupdir}/${f##*/}" ]; then
|
|
sleep 2
|
|
cp -p "${f}" "${backupdir}/${f##*/}"
|
|
fi
|
|
# If both attempts fail, server may have
|
|
# crashed between truncate and write
|
|
if [ ! -s "${backupdir}/${f##*/}" ]; then
|
|
cp -p "${f%.arktribe}.tmptribe" "${backupdir}/${f##*/}"
|
|
fi
|
|
if [ -s "${backupdir}/${f##*/}" ]; then
|
|
echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}\e[68G[ ${RED}FAILED${NORMAL} ]"
|
|
fi
|
|
done
|
|
|
|
# ARK server uses Lock-Truncate-Write-Unlock
|
|
echo -ne "${NORMAL} Copying GameUserSettings.ini "
|
|
cp -p "${arkserverroot}/ShooterGame/Saved/Config/LinuxServer/GameUserSettings.ini" "${backupdir}/GameUserSettings.ini"
|
|
if [ ! -s "${backupdir}/GameUserSettings.ini" ]; then
|
|
sleep 2
|
|
cp -p "${f}" "${backupdir}/${f##*/}"
|
|
fi
|
|
if [ -f "${backupdir}/GameUserSettings.ini" ]; then
|
|
echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}\e[68G[ ${RED}FAILED${NORMAL} ]"
|
|
fi
|
|
|
|
|
|
echo -ne "${NORMAL} Copying Game.ini "
|
|
|
|
cp -p "${arkserverroot}/ShooterGame/Saved/Config/LinuxServer/Game.ini" "${backupdir}/Game.ini"
|
|
if [ ! -s "${backupdir}/Game.ini" ]; then
|
|
sleep 2
|
|
cp -p "${f}" "${backupdir}/${f##*/}"
|
|
fi
|
|
if [ -f "${backupdir}/Game.ini" ]; then
|
|
echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}\e[68G[ ${RED}FAILED${NORMAL} ]"
|
|
fi
|
|
|
|
|
|
#Tar the files and remove the original Backup Directory. Saves about 50MB of disk space per backup
|
|
|
|
echo -ne "${NORMAL} Compressing Backup "
|
|
|
|
tar -jcf "${arkbackupdir}/${daystamp}/${datestamp}.tar.bz2" -C "${arkbackupdir}" "${datestamp}"
|
|
rm -rf ${backupdir}
|
|
|
|
if [ -f "${arkbackupdir}/${daystamp}/${datestamp}.tar.bz2" ]; then
|
|
echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}\e[68G[ ${RED}FAILED${NORMAL} ]"
|
|
fi
|
|
echo -e "${NORMAL} Created Backup: ${GREEN} ${datestamp}.tar.bz2${NORMAL}"
|
|
|
|
if [ -n "$arkMaxBackupSizeGB" ] && (( arkMaxBackupSizeGB >= 1 )); then
|
|
(( arkMaxBackupSizeMB = arkMaxBackupSizeGB * 1024 ))
|
|
fi
|
|
|
|
if [ -n "$arkMaxBackupSizeMB" ] && (( arkMaxBackupSizeMB > 64 )); then
|
|
find "${arkbackupdir}" -type f -printf "%T@\t%s\t%p\n" |
|
|
sort -n -r |
|
|
cut -f2-3 |
|
|
(sz=0; while read fsz f; do
|
|
if (( sz / 1048576 > arkMaxBackupSizeMB )); then
|
|
rm "$f"
|
|
fi
|
|
(( sz += fsz ))
|
|
done)
|
|
fi
|
|
}
|
|
|
|
|
|
#
|
|
# Print the status of the server (running? online? version?)
|
|
#
|
|
printStatus(){
|
|
if isTheServerRunning ;then
|
|
echo -e "$NORMAL" "Server running: " "$GREEN" "Yes" "$NORMAL"
|
|
else
|
|
echo -e "$NORMAL" "Server running: " "$RED" "No" "$NORMAL"
|
|
fi
|
|
|
|
if isTheServerUp ;then
|
|
echo -e "$NORMAL" "Server listening: " "$RED" "No" "$NORMAL"
|
|
else
|
|
echo -e "$NORMAL" "Server listening: " "$GREEN" "Yes" "$NORMAL"
|
|
perl -MSocket -e '
|
|
my $port = int($ARGV[0]);
|
|
socket(my $socket, PF_INET, SOCK_DGRAM, 0);
|
|
setsockopt($socket, SOL_SOCKET, SO_RCVTIMEO, pack("i4", 1, 0, 0, 0));
|
|
my $sockaddr = pack_sockaddr_in($port, inet_aton($ARGV[1]));
|
|
send($socket, "\xff\xff\xff\xffTSource Engine Query\x00", 0, $sockaddr);
|
|
my $data = "";
|
|
recv($socket, $data, 1400, 0) or (print "Unable to query server\n" and exit(1));
|
|
my ($servername, $mapname, $game, $fullname, $rest) = split(/\x00/, substr($data, 6), 5);
|
|
my $players = ord(substr($rest, 2, 1));
|
|
my $maxplayers = ord(substr($rest, 3, 1));
|
|
print "Server Name: $servername\n";
|
|
print "Players: $players / $maxplayers\n";
|
|
' "$(getQueryPort)" "${ark_MultiHome:-127.0.0.1}"
|
|
|
|
if isTheServerOnline; then
|
|
echo -e "$NORMAL" "Server online: " "$GREEN" "Yes" "$NORMAL"
|
|
echo -e "$NORMAL" "ARKServers link: " "$GREEN" "http://arkservers.net/server/${publicip}:$(getQueryPort)" "$NORMAL"
|
|
else
|
|
echo -e "$NORMAL" "Server online: " "$RED" "No" "$NORMAL"
|
|
fi
|
|
fi
|
|
|
|
getCurrentVersion
|
|
echo -e "$NORMAL" "Server version: " "$GREEN" $instver "$NORMAL"
|
|
}
|
|
|
|
getAllInstanceNames(){
|
|
declare -A instancenames
|
|
for varname in "${!configfile_@}"; do
|
|
instancename="${varname#configfile_}"
|
|
instancenames[${instancename}]="${instancename}"
|
|
done
|
|
for f in /etc/arkmanager/instances/*.cfg; do
|
|
if [ -f "${f}" ]; then
|
|
instancename="${f##*/}"
|
|
instancename="${instancename%.cfg}"
|
|
instancenames[${instancename}]="${instancename}"
|
|
fi
|
|
done
|
|
for f in ${HOME}/.config/arkmanager/instances/*.cfg; do
|
|
if [ -f "${f}" ]; then
|
|
instancename="${f##*/}"
|
|
instancename="${instancename%.cfg}"
|
|
instancenames[${instancename}]="${instancename}"
|
|
fi
|
|
done
|
|
|
|
echo "${instancenames[@]}"
|
|
}
|
|
|
|
doListAllInstances(){
|
|
if [ "$1" == "--brief" ]; then
|
|
getAllInstanceNames
|
|
else
|
|
echo "The following instances are available:"
|
|
for n in $(getAllInstanceNames); do
|
|
(
|
|
echo -n " @${n}: "
|
|
useConfig "$n"
|
|
echo "${configfile} => ${arkserverroot}"
|
|
)
|
|
done
|
|
fi
|
|
}
|
|
|
|
useConfig() {
|
|
configfile=
|
|
if [ -f "/etc/arkmanager/instances/${1}.cfg" ]; then
|
|
configfile="/etc/arkmanager/instances/${1}.cfg"
|
|
fi
|
|
if [ -f "${HOME}/.config/arkmanager/instances/${1}.cfg" ]; then
|
|
configfile="${HOME}/.config/arkmanager/instances/${1}.cfg"
|
|
fi
|
|
for varname in "${!configfile_@}"; do
|
|
if [ "configfile_$1" == "$varname" ]; then
|
|
configfile="${!varname}"
|
|
break
|
|
fi
|
|
done
|
|
if [ -z "$configfile" ]; then
|
|
echo "Error: no config files for instance $1"
|
|
exit 1
|
|
fi
|
|
if [ ! -f "$configfile" ]; then
|
|
echo "Error: config file $configfile does not exist"
|
|
exit 1
|
|
fi
|
|
source "$configfile"
|
|
if [ -z "$arkserverroot" ]; then
|
|
echo "Error: arkserverroot not set"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
showUsage() {
|
|
echo -e "Usage: arkmanager [Commands]\n"
|
|
echo "Commands can be followed by one or more @instance arguments"
|
|
echo "The special '@all' instance selects all instances"
|
|
echo "Commands may also be followed by zero or more --options"
|
|
echo
|
|
echo "Commands that take no instances:"
|
|
echo "Command Description"
|
|
echo "upgrade-tools Check for a new ARK Server Tools version and upgrades it if needed"
|
|
echo "uninstall-tools Uninstall the ARK Server Tools"
|
|
echo "useconfig <name> Sets the default instance for the commands that follow"
|
|
echo "list-instances Lists all available instances"
|
|
echo "--help Show this help"
|
|
echo "--version Show the version info of ARK Server Tools"
|
|
echo
|
|
echo "Commands that take one or more instances:"
|
|
echo "Command Description"
|
|
echo "backup Saves a backup of your server inside the backup directory"
|
|
echo "broadcast <msg> Sends a message to all users connected to server"
|
|
echo "saveworld Saves the game world to disk"
|
|
echo "rconcmd <cmd> Execute RCON command on server"
|
|
echo "checkupdate Check for a new ARK server version"
|
|
echo "install Install the ARK server files from steamcmd"
|
|
echo "installmod <modid> Installs a mod from the Steam workshop"
|
|
echo "uninstallmod <modid> Removes the mod from the Mods directory"
|
|
echo "reinstallmod <modid> Removes and re-installs a mod in the Mods directory"
|
|
echo "restart Stops the server and then starts it"
|
|
echo "run Runs the server without daemonizing"
|
|
echo "start Starts the server"
|
|
echo "stop Stops the server"
|
|
echo "status Returns the status of the current ARK server instance"
|
|
echo "update Check for a new ARK server version, if needed, stops the server, updates it, and starts it again"
|
|
echo
|
|
echo "Update command takes the below options:"
|
|
echo " --force Apply update without checking the current version"
|
|
echo " --safe Wait for server to perform world save and update."
|
|
echo " --warn Warn players before updating server"
|
|
echo " --validate Validates all ARK server files"
|
|
echo " --saveworld Saves world before update"
|
|
echo " --update-mods Updates installed and requested mods"
|
|
echo " --backup Takes a backup of the save files before updating"
|
|
echo " --downloadonly Download the mod and/or server update without applying it"
|
|
echo " Requires arkStagingDir be set to a staging directory on the same filesystem as the server"
|
|
}
|
|
|
|
#---------------------
|
|
# Main program
|
|
#---------------------
|
|
|
|
# check the configuration and throw errors or warnings if needed
|
|
checkConfig
|
|
|
|
while true; do
|
|
options=( )
|
|
allinstances=no
|
|
instances=( )
|
|
args=( )
|
|
command="$1"
|
|
shift
|
|
nrarg=0
|
|
|
|
# get the number of arguments for commands that take arguments
|
|
case "$command" in
|
|
installmod) nrarg=1; ;;
|
|
uninstallmod) nrarg=1; ;;
|
|
reinstallmod) nrarg=1; ;;
|
|
broadcast) nrarg=1; ;;
|
|
rconcmd) nrarg=1; ;;
|
|
useconfig) nrarg=1; ;;
|
|
esac
|
|
|
|
# Enumerate the options and arguments
|
|
while [ $# -ne 0 ]; do
|
|
case "$1" in
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
--args)
|
|
nrarg=$#
|
|
;;
|
|
--verbose)
|
|
verbose=1
|
|
;;
|
|
--dots)
|
|
progressDisplayType=dots
|
|
;;
|
|
--spinner)
|
|
progressDisplayType=spinner
|
|
;;
|
|
--*)
|
|
options+=( "$1" )
|
|
;;
|
|
@all)
|
|
allinstances=yes
|
|
;;
|
|
@*)
|
|
instances+=( "${1#@}" )
|
|
;;
|
|
*)
|
|
if [ $nrarg -gt 0 ]; then
|
|
args+=( "$1" )
|
|
(( nrarg-- ))
|
|
else
|
|
break
|
|
fi
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# handle non-instance separately
|
|
case "$command" in
|
|
upgrade-tools)
|
|
doUpgradeTools
|
|
exit
|
|
;;
|
|
uninstall-tools)
|
|
doUninstallTools
|
|
exit
|
|
;;
|
|
useconfig)
|
|
defaultinstance="${args[0]}"
|
|
continue
|
|
;;
|
|
list-instances)
|
|
doListAllInstances "${options[@]}"
|
|
exit
|
|
;;
|
|
--version)
|
|
echo "Version: ${arkstVersion}"
|
|
echo "Channel: ${arkstChannel}"
|
|
if [ -n "${arkstCommit}" ]; then
|
|
echo "Commit: ${arkstCommit:0:7}"
|
|
fi
|
|
echo "Blob SHA: $( (echo -ne "blob $(stat -c "%s" "$0")\0"; sed "s@^arkstCommit=.*@arkstCommit=''@" "$0") | sha1sum | cut -d' ' -f1)"
|
|
exit 1
|
|
;;
|
|
-h|--help)
|
|
showUsage
|
|
exit 1
|
|
;;
|
|
"")
|
|
echo "arkmanager v${arkstVersion}: no command specified"
|
|
showUsage
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Handle no instances being specified
|
|
if [[ "${#instances[@]}" == 0 && "$allinstances" == "no" ]]; then
|
|
if [ -n "$defaultinstance" ]; then
|
|
instances=( "$defaultinstance" )
|
|
else
|
|
echo "No instances supplied for command ${command} ${options[*]} ${args[*]}"
|
|
read -p "Do you wish to run this command for all instances?" -n 1 -r
|
|
echo
|
|
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
|
|
allinstances=yes
|
|
else
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Handle all instances being requested
|
|
if [[ "$allinstances" == "yes" ]]; then
|
|
instances=( $(getAllInstanceNames) )
|
|
fi
|
|
|
|
# Run the command for each instance requested
|
|
for instance in "${instances[@]}"; do
|
|
(
|
|
echo "Running command '${command}' for instance '${instance}'"
|
|
useConfig "$instance"
|
|
checkConfig
|
|
|
|
case "$command" in
|
|
run)
|
|
doRun
|
|
;;
|
|
start)
|
|
doStart "${options[@]}"
|
|
;;
|
|
stop)
|
|
doStop shutdown "${options[@]}"
|
|
;;
|
|
restart)
|
|
doStop restart "${options[@]}"
|
|
echo "`timestamp`: stop" >> "$logdir/$arkmanagerLog"
|
|
;;
|
|
install)
|
|
doInstall
|
|
;;
|
|
update)
|
|
doUpdate "${options[@]}"
|
|
;;
|
|
checkupdate)
|
|
checkForUpdate
|
|
;;
|
|
installmod)
|
|
doInstallMod "${args[@]}"
|
|
;;
|
|
uninstallmod)
|
|
doUninstallMod "${args[@]}"
|
|
;;
|
|
reinstallmod)
|
|
doUninstallMod "${args[@]}"
|
|
doInstallMod "${args[@]}"
|
|
;;
|
|
backup)
|
|
doBackup
|
|
;;
|
|
broadcast)
|
|
doBroadcast "${args[@]}"
|
|
;;
|
|
saveworld)
|
|
doSaveWorld
|
|
;;
|
|
rconcmd)
|
|
rconcmd "${args[@]}"
|
|
;;
|
|
status)
|
|
printStatus
|
|
;;
|
|
*)
|
|
echo -n "arkmanager v${arkstVersion}: unknown command '$command' specified"
|
|
showUsage
|
|
exit 255
|
|
;;
|
|
esac
|
|
)
|
|
laststatus=$?
|
|
if [ $laststatus -eq 255 ]; then
|
|
exit 1
|
|
elif [ $laststatus -ne 0 ]; then
|
|
status=$laststatus
|
|
fi
|
|
done
|
|
|
|
# Perform the restart portion of the restart command
|
|
if [[ "$command" == "restart" ]]; then
|
|
sleep 1
|
|
for instance in "${instances[@]}"; do
|
|
(
|
|
useConfig "$instance"
|
|
doStart "${options[@]}"
|
|
echo "`timestamp`: start" >> "$logdir/$arkmanagerLog"
|
|
echo "`timestamp`: restart" >> "$logdir/$arkmanagerLog"
|
|
)
|
|
done
|
|
fi
|
|
|
|
if [ $# -eq 0 ]; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
exit $status
|