1148 lines
31 KiB
Bash
Executable File

#!/bin/bash
# ARK: survival evolved manager
#
# Original author: LeXaT
# Maintainer: FezVrasta
# Contributors: Sispheor, Atriusftw, klightspeed, lexat, puseidr
# Check the user is not currently running this script as root
if [ "$(id -u)" == "0" ]; then
echo "This script must NOT be run as root" 1>&2
exit 1
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
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}"
# Script version
arkstVersion="1.3"
arkstCommit=''
#---------------------
# 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 server admin password
#
getAdminPassword() {
if [ -n "${ark_ServerAdminPassword}" ]; then
echo "${ark_ServerAdminPassword}"
else
sed -ne '/^\[ServerSettings\]/,/^\[.*\]/{s/^ServerAdminPassword[[:space:]]*=[[:space:]]*//p;}' "${arkserverroot}/ShooterGame/Saved/Config/LinuxServer/GameUserSettings.ini"
fi
}
#
# 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 $password = $ARGV[1];
my $command = $ARGV[2];
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("127.0.0.1"));
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";
' "${ark_RCONPort}" "`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"
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!"
fi
}
#
# Check if the server need to be updated
# Return 0 if update is needed, else return 1
#
function isUpdateNeeded(){
getCurrentVersion
getAvailableVersion
if [ "$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
return $instver
}
#
# Get the current available server version on steamdb
#
function getAvailableVersion(){
rm -f "$steamcmd_appinfocache"
bnumber=`$steamcmdroot/$steamcmdexec +login 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`
return $bnumber
}
#
# 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 and visible in steam server list
#
#
function isTheServerUp(){
$lsof -i :"$ark_Port" > /dev/null
result=$?
# 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
}
#
# run function
#
doRun() {
arkserveropts="$serverMap"
if [ -n "$serverMapModId" ]; then
arkserveropts="-MapModID=$serverMapModId"
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}"
fi
done
# bring in arkflag_... flags
for varname in "${!arkflag_@}"; do
name="${varname#arkflag_}"
val="${!varname}"
if [ -n "$val" ]; then
arkextraopts=( "${arkextraopts[@]}" "-${name}" )
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
# Put the server process into the background so we can monitor it
"$arkserverroot/$arkserverexec" "$arkserveropts" "${arkextraopts[@]}" &
# Grab the server PID
serverpid=$!
# Disable auto-restart so we don't get caught in a restart loop
rm "$arkserverroot/$arkautorestartfile"
restartserver=0
while true; do
# Grab the current server PID
local pid="`getServerPid`"
if [ "$pid" == "$serverpid" ]; 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
else
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 [ "$restartserver" -ne 0 -a -f "$arkserverroot/$arkautorestartfile" ]; 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"
# kill the server with the PID
PID=`getServerPID`
kill -INT $PID
for (( i = 0; i < 10; i++ )); do
sleep 1
if ! isTheServerRunning; then
break
fi
done
if isTheServerRunning; then
tput rc
echo "Killing server..."
rm "$arkserverroot/$arkautorestartfile"
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 +login 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 "$msgWarnUpdateMinutes" ]; then
warnmsg="$(printf "$msgWarnUpdateMinutes" "$warnminutes")"
else
warnmsg="$(printf "This ARK server will shutdown for an update in %d seconds" "$warnseconds")"
fi
doBroadcastWithEcho "$warnmsg"
sleep $(( warnseconds - warninterval ))s
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=
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" == "--update-mods" ]; then
modupdate=1
fi
done
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
doStop
if [ -n "$appupdate" ]; then
cd "$steamcmdroot"
./$steamcmdexec +login 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;
}
#
# 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 +login 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"
if [ -f "$modsrcdir/mod.info" ]; then
if [ -f "$modsrcdir/LinuxNoEditor/mod.info" ]; then
modsrcdir="$modsrcdir/LinuxNoEditor"
fi
find "$modsrcdir" -type f ! -name "*.z.uncompressed_size" -printf "%P\n" | while read f; do
if [ ! -f "$moddestdir/${f%.z}" -o "$modsrcdir/$f" -nt "$moddestdir/${f%.z}" ]; then
return 0
fi
done
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"
if [ -f "$modsrcdir/mod.info" ]; then
echo "Copying files to $moddestdir"
if [ -f "$modsrcdir/LinuxNoEditor/mod.info" ]; then
modsrcdir="$modsrcdir/LinuxNoEditor"
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
fi
}
#
# Downloads mod and installs it into mods directory
#
doInstallMod(){
local modid=$1
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 online: " "$RED" "No" "$NORMAL"
else
echo -e "$NORMAL" "Server online: " "$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("127.0.0.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";
' "${ark_QueryPort}"
fi
getCurrentVersion
echo -e "$NORMAL" "Server version: " "$GREEN" $instver "$NORMAL"
}
doUpgrade() {
local sudo=sudo
if [ "$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'`
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 [[ $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[@]}"
else
exit 0
fi
elif [[ "$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[@]}"
else
exit 0
fi
else
echo "Your ARK server tools are already up to date"
fi
}
useConfig() {
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
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)
doUpgrade
;;
useconfig)
useConfig "$2"
shift
;;
-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 "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 Check for a new ARK server version, if needed, stops the server, updates it, and starts it again"
echo "update --force Apply update without checking the current version"
echo "update --safe Wait for server to perform world save and update."
echo "update --warn Warn players before updating server"
echo "update --validate Validates all ARK server files"
echo "update --update-mods Updates installed and requested mods"
echo "upgrade Check for a new ARK Server Tools version and upgrades it if needed"
echo "useconfig <name> Use the configuration overrides in the specified config name or file"
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
shift
if [ $# -eq 0 ]; then
break
fi
done