mirror of
https://github.com/eliasstepanik/ark-ac-server-tools.git
synced 2026-01-12 10:58:28 +00:00
When `workshop_download_item` is executed, the Steam API will download not just the specified mod, but also every other mod that has ever been installed by that SteamCMD instance. Wait for those to download and update when downloading the mod.
1433 lines
40 KiB
Bash
Executable File
1433 lines
40 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.4"
|
|
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?" -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 [ ! -f "$arkserverroot/$arkserverexec" ] ; then
|
|
echo -e "[" "$YELLOW" "WARN" "$NORMAL" "]" "\tYour ARK server exec could not be found."
|
|
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() {
|
|
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);
|
|
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);
|
|
print $rcvbody, "\n";
|
|
' "$(getRconPort)" "${ark_MultiHome:-127.0.0.1}" "$(getAdminPassword)" "$1"
|
|
}
|
|
|
|
#
|
|
# Save world
|
|
#
|
|
doSaveWorld() {
|
|
rconcmd saveworld
|
|
}
|
|
|
|
#
|
|
# Exit cleanly
|
|
#
|
|
doExitServer() {
|
|
rconcmd doexit
|
|
}
|
|
|
|
#
|
|
# Broadcast message
|
|
#
|
|
doBroadcast(){
|
|
rconcmd "broadcast $1" >/dev/null
|
|
}
|
|
|
|
#
|
|
# Broadcast message with echo
|
|
#
|
|
doBroadcastWithEcho(){
|
|
echo "$1"
|
|
doBroadcast "$1"
|
|
}
|
|
|
|
#
|
|
# 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=`$steamcmdroot/$steamcmdexec +@NoPromptForPassword 1 +login ${steamlogin:-anonymous} +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
|
|
}
|
|
|
|
#
|
|
# 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
|
|
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 up"
|
|
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
|
|
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 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"
|
|
# install the server
|
|
./$steamcmdexec +@NoPromptForPassword 1 +login ${steamlogin:-anonymous} +force_install_dir "$arkserverroot" +app_update $appid validate +quit
|
|
# the current version should be the last version. We set our version
|
|
getCurrentVersion
|
|
}
|
|
|
|
#
|
|
# Waits for a configurable number of minutes before updating the server
|
|
#
|
|
doUpdateWarn(){
|
|
cd "$arkserverroot"
|
|
|
|
local pid=`getServerPID`
|
|
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 1 )
|
|
|
|
for warninterval in "${warnintervals[@]}"; do
|
|
if [ "`getServerPID`" != "$pid" ]; then
|
|
echo "Server has stopped. Aborting update"
|
|
return 1
|
|
fi
|
|
if (( arkwarnminutes > warninterval )); then
|
|
if [ -n "$msgWarnUpdateMinutes" ]; then
|
|
warnmsg="$(printf "$msgWarnUpdateMinutes" "$warnminutes")"
|
|
else
|
|
warnmsg="$(printf "This ARK server will shutdown for an update in %d minutes" "$warnminutes")"
|
|
fi
|
|
doBroadcastWithEcho "$warnmsg"
|
|
sleep $(( warnminutes - warninterval ))m
|
|
warnminutes=$warninterval
|
|
fi
|
|
done
|
|
|
|
local warnseconds=90
|
|
warnintervals=( 60 45 30 20 15 10 5 0 )
|
|
for warninterval in "${warnintervals[@]}"; do
|
|
if [ "`getServerPID`" != "$pid" ]; then
|
|
echo "Server has stopped. Aborting update"
|
|
return 1
|
|
fi
|
|
if [ -n "$msgWarnUpdateSeconds" ]; then
|
|
warnmsg="$(printf "$msgWarnUpdateSeconds" "$warnseconds")"
|
|
else
|
|
warnmsg="$(printf "This ARK server will shutdown for an update in %d seconds" "$warnseconds")"
|
|
fi
|
|
doBroadcastWithEcho "$warnmsg"
|
|
sleep $(( warnseconds - warninterval ))s
|
|
warnseconds=$warninterval
|
|
done
|
|
fi
|
|
|
|
if [ "`getServerPID`" != "$pid" ]; then
|
|
echo "Server has stopped. Aborting update"
|
|
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=
|
|
|
|
for arg in "$@"; do
|
|
if [ "$arg" == "--force" ]; then
|
|
appupdate=1
|
|
elif [ "$arg" == "--safe" ]; then
|
|
updatetype=safe
|
|
elif [ "$arg" == "--warn" ]; then
|
|
updatetype=warn
|
|
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
|
|
fi
|
|
done
|
|
|
|
echo "$$" >"${arkserverroot}/.ark-update.lock.$$"
|
|
while true; do
|
|
if ! ln "${arkserverroot}/.ark-update.lock.$$" "${arkserverroot}/.ark-update.lock"; then
|
|
local lockpid="$(<"${arkserverroot}/.ark-update.lock")"
|
|
if [ -n "$lockpid" ] && [ "$lockpid" != "$$" ] && kill -0 "$lockpid"; then
|
|
echo "Update already in progress (PID: $lockpid)"
|
|
rm -f "${arkserverroot}/.ark-update.lock.$$"
|
|
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
|
|
fi
|
|
|
|
if [ -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 ! doUpdateWarn; then
|
|
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
|
|
cd "$steamcmdroot"
|
|
./$steamcmdexec +@NoPromptForPassword 1 +login ${steamlogin:-anonymous} +force_install_dir "$arkserverroot" +app_update $appid $validate +quit
|
|
# 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
|
|
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
|
|
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/$modid"
|
|
local dlsize=0
|
|
cd "$steamcmdroot"
|
|
|
|
while true; do
|
|
./$steamcmdexec +@NoPromptForPassword 1 +login ${steamlogin:-anonymous} +workshop_download_item $mod_appid $modid +quit
|
|
if [ ! -d "$moddldir" ]; then break; fi
|
|
local newsize="`du -s "$moddldir/.." | cut -f1`"
|
|
if [ $newsize -eq $dlsize ]; then break; fi
|
|
dlsize=$newsize
|
|
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:-Linux}"
|
|
|
|
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 "$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
|
|
|
|
perl -e '
|
|
my $data;
|
|
{ local $/; $data = <STDIN>; }
|
|
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);
|
|
print pack("L< L< L< Z8 L< C L< L<", $ARGV[0], 0, 8, "ModName", 1, 0, 1, $mapfilelen);
|
|
print $mapfile;
|
|
print "\x33\xFF\x22\xFF\x02\x00\x00\x00\x01";
|
|
' $modid <"$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
|
|
}
|
|
|
|
#
|
|
# Copies server state to a backup directory
|
|
#
|
|
doBackup(){
|
|
local datestamp=`date +"%Y-%m-%d_%H.%M.%S"`
|
|
local backupdir="${arkbackupdir}/${datestamp}"
|
|
local savedir="SavedArks"
|
|
mkdir -p "$backupdir"
|
|
|
|
# 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}[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}[ ${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}[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}[ ${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}[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}[ ${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}[ ${GREEN}OK${NORMAL} ]"
|
|
else
|
|
echo -e "${NORMAL}[ ${RED}FAILED${NORMAL} ]"
|
|
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"
|
|
}
|
|
|
|
useConfig() {
|
|
if [ "$1" == "main" ]; then
|
|
return
|
|
fi
|
|
for varname in "${!configfile_@}"; do
|
|
if [ "configfile_$1" == "$varname" ]; then
|
|
source "${!varname}"
|
|
return
|
|
fi
|
|
done
|
|
source "$1"
|
|
}
|
|
|
|
#---------------------
|
|
# Main program
|
|
#---------------------
|
|
|
|
# check the configuration and throw errors or warnings if needed
|
|
checkConfig
|
|
|
|
while true; do
|
|
case "$1" in
|
|
run)
|
|
doRun
|
|
;;
|
|
start)
|
|
if [ "$2" == "--all" ]; then
|
|
doStartAll
|
|
shift
|
|
else
|
|
doStart
|
|
fi
|
|
;;
|
|
stop)
|
|
if [ "$2" == "--all" ]; then
|
|
doStopAll
|
|
shift
|
|
else
|
|
doStop
|
|
fi
|
|
;;
|
|
restart)
|
|
if [ "$2" == "--all" ]; then
|
|
doStopAll
|
|
else
|
|
doStop
|
|
fi
|
|
echo "`timestamp`: stop" >> "$logdir/$arkmanagerLog"
|
|
sleep 1
|
|
if [ "$2" == "--all" ]; then
|
|
doStartAll
|
|
shift
|
|
else
|
|
doStart
|
|
fi
|
|
echo "`timestamp`: start" >> "$logdir/$arkmanagerLog"
|
|
echo "`timestamp`: restart" >> "$logdir/$arkmanagerLog"
|
|
;;
|
|
install)
|
|
doInstall
|
|
;;
|
|
update)
|
|
args=()
|
|
|
|
while [[ "$2" =~ ^-- ]]; do
|
|
args=( "${args[@]}" "$2" )
|
|
shift
|
|
done
|
|
|
|
doUpdate "${args[@]}"
|
|
;;
|
|
checkupdate)
|
|
checkForUpdate
|
|
;;
|
|
installmod)
|
|
doInstallMod "$2"
|
|
shift
|
|
;;
|
|
backup)
|
|
doBackup
|
|
;;
|
|
broadcast)
|
|
doBroadcast "$2"
|
|
shift
|
|
;;
|
|
saveworld)
|
|
doSaveWorld
|
|
;;
|
|
rconcmd)
|
|
rconcmd "$2"
|
|
shift
|
|
;;
|
|
status)
|
|
printStatus
|
|
;;
|
|
upgrade-tools)
|
|
doUpgradeTools
|
|
;;
|
|
uninstall-tools)
|
|
doUninstallTools
|
|
;;
|
|
useconfig)
|
|
useConfig "$2"
|
|
shift
|
|
;;
|
|
--version)
|
|
echo "Version: ${arkstVersion}"
|
|
echo "Channel: ${arkstChannel}"
|
|
if [ -n "${arkstCommit}" ]; then
|
|
echo "Commit: ${arkstCommit:0:7}"
|
|
fi
|
|
exit 1
|
|
;;
|
|
-h|--help)
|
|
echo -e "Usage: arkmanager [OPTION]\n"
|
|
echo "Option 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 "restart Stops the server and then starts it"
|
|
echo "restart --all Restarts all servers specified in configfile_xxxxx"
|
|
echo "run Runs the server without daemonizing"
|
|
echo "start Starts the server"
|
|
echo "start --all Starts all servers specified in configfile_xxxxx"
|
|
echo "stop Stops the server"
|
|
echo "stop --all Stops all servers specified in configfile_xxxxx"
|
|
echo "status Returns the status of the current ARK server instance"
|
|
echo "update [OPTION ...] Check for a new ARK server version, if needed, stops the server, updates it, and starts it again"
|
|
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> Use the configuration overrides in the specified config name or file"
|
|
echo "--help Show this help"
|
|
echo "--version Show the version info of ARK Server Tools"
|
|
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"
|
|
exit 1
|
|
;;
|
|
*)
|
|
echo -n "arkmanager v${arkstVersion}: "
|
|
if [ $# -eq 0 ]; then
|
|
echo "no command specified"
|
|
else
|
|
echo "unknown command '$1' specified"
|
|
fi
|
|
echo "Try 'arkmanager -h' or 'arkmanager --help' for more information."
|
|
exit 1
|
|
;;
|
|
esac
|
|
status=$?
|
|
shift
|
|
if [ $# -eq 0 ]; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
exit $status
|