Merge pull request #472 from FezVrasta/1.6-dev.unstable

Various fixes for 1.6
This commit is contained in:
Ben Peddell 2016-05-28 06:33:04 +10:00
commit b59b705029
3 changed files with 603 additions and 133 deletions

View File

@ -152,6 +152,9 @@ instances.
`--warn`;; `--warn`;;
Warns any connected players that the server is going down Warns any connected players that the server is going down
`--warnreason`;;
Gives a reason for the shutdown. Defaults to `maintenance`
`--saveworld`;; `--saveworld`;;
Saves the world using `saveworld` - usually not Saves the world using `saveworld` - usually not
necessary, as server usually saves the world on a graceful necessary, as server usually saves the world on a graceful
@ -161,6 +164,9 @@ instances.
Runs the `stop` command followed by the `restart` command. Runs the `stop` command followed by the `restart` command.
Accepts and passes the options for those commands Accepts and passes the options for those commands
`--warnreason`;;
Gives a reason for the restart. Defaults to `a restart`
`install`:: `install`::
Downloads and installs (or validates an existing install) of Downloads and installs (or validates an existing install) of
the ARK server the ARK server
@ -205,6 +211,10 @@ instances.
Downloads the update but does not apply it. Only has effect Downloads the update but does not apply it. Only has effect
if a staging directory is set. if a staging directory is set.
`cancelshutdown`::
Cancels a pending update / shutdown / restart that was run with
the `--warn` option
`checkupdate`:: `checkupdate`::
Checks if an ARK server update is available Checks if an ARK server update is available
@ -238,6 +248,37 @@ instances.
`status`:: `status`::
Prints the status of the ARK server Prints the status of the ARK server
`install-cronjob <command>`::
Installs a cron job that executes the specified command.
This accepts any of the options the specified command accepts,
as well as the following options. In order to specify an
argument to the command (e.g. to the `broadcast` command),
use the `--arg=<arg>` option.
`--daily`;;
The command should be executed daily
`--hourly`;;
The command should be executed hourly
`--hour=<hour>`;;
Specifies one or more hours when the command should execute.
This is the hour field of the cron job.
`--minute=<minute>`;;
Specifies one or more minutes of the hour when the command
should execute. This is the minute field of the cron job.
`--enable-output`;;
Enables the output from the command - the cron daemon usually
emails this to the user specified in the cron configuration
`--arg=<arg>`;;
Specifies an argument to pass to the command
`remove-cronjob <command>`::
Removes a cron job previously installed by `install-cronjob`
Configuration files Configuration files
------------------- -------------------
@ -321,6 +362,32 @@ The following options can be overridden on a per-instance basis:
Templated messages for warnings, where `%d` is replaced with the Templated messages for warnings, where `%d` is replaced with the
number of minutes / seconds before the update / restart / shutdown number of minutes / seconds before the update / restart / shutdown
`msgWarnReason`::
`msgTimeMinutes`::
`msgTimeSeconds`::
`msgReasonUpdateApp`::
`msgReasonUpdateMod`::
`msgReasonUpdateAppMod`::
`msgReasonRestart`::
`msgReasonShutdown`::
Alternative templated messages for warnings with the following
replacement parameters:
`{reason}`;;
Valid in `msgWarnReason`, replaced at runtime with the appropriate `msgReason*` template
`{time}`;;
Valid in `msgWarnReason`, replaced at runtime with the appropriate `msgTime*` template
`{modnamesupdate}`;;
Valid in `msgReason*Mod`, replaced at runtime with a comma-delimited list of updated mod names
`{minutes}`;;
Valid in `msgTimeMinutes`, replaced at runtime with minutes remaining until shutdown
`{seconds}`;;
Valid in `msgTimeSeconds`, replaced at runtime with seconds remaining until shutdown
`logdir`:: `logdir`::
Specifies where to store log files Specifies where to store log files

View File

@ -126,6 +126,8 @@ if [ -f "${HOME}/.arkmanager.cfg" ]; then
source "${HOME}/.arkmanager.cfg" source "${HOME}/.arkmanager.cfg"
fi fi
cd "$HOME"
lsof=lsof lsof=lsof
if [ -x /usr/sbin/lsof ]; then if [ -x /usr/sbin/lsof ]; then
lsof=/usr/sbin/lsof lsof=/usr/sbin/lsof
@ -158,6 +160,7 @@ else
install_datadir="${install_datadir:-${install_bindir%/*}/share/arkmanager}" install_datadir="${install_datadir:-${install_bindir%/*}/share/arkmanager}"
fi fi
declare -A modsrcdirs
#--------------------- #---------------------
# functions # functions
@ -366,8 +369,14 @@ function runSteamCMD(){
function runSteamCMDspinner(){ function runSteamCMDspinner(){
if [ -n "$verbose" ]; then if [ -n "$verbose" ]; then
echo printf "Executing"
runSteamCMD "$@" printf " %q" "$steamcmdroot/$steamcmdexec" +@NoPromptForPassword 1 +login ${steamlogin:-anonymous} "$@" +quit
printf "\n"
if command >&3; then
runSteamCMD "$@" | tee /dev/fd/3
else
runSteamCMD "$@"
fi
return $? return $?
else else
if [ -z "$progressDisplayType" ]; then if [ -z "$progressDisplayType" ]; then
@ -377,7 +386,11 @@ function runSteamCMDspinner(){
progressDisplayType=dots progressDisplayType=dots
fi fi
fi fi
runSteamCMD "$@" >/dev/null 2>&1 & if command >&3; then
runSteamCMD "$@" >&3 &
else
runSteamCMD "$@" >/dev/null &
fi
local scpid=$! local scpid=$!
local pos=0 local pos=0
local spinner=( '\b-' '\b/' '\b|' '\b\\' ) local spinner=( '\b-' '\b/' '\b|' '\b\\' )
@ -396,6 +409,12 @@ function runSteamCMDspinner(){
fi fi
} }
function runSteamCMDspinnerSubst(){
local fd="$1"
shift
runSteamCMDspinner "$@" 3>&1 >/dev/fd/${fd}
}
# #
# Check if a new version is available but not apply it # Check if a new version is available but not apply it
# #
@ -577,11 +596,11 @@ function numPlayersConnected(){
my $sockaddr = pack_sockaddr_in($port, inet_aton($ARGV[1])); my $sockaddr = pack_sockaddr_in($port, inet_aton($ARGV[1]));
send($socket, "\xff\xff\xff\xffTSource Engine Query\x00", 0, $sockaddr); send($socket, "\xff\xff\xff\xffTSource Engine Query\x00", 0, $sockaddr);
my $data = ""; my $data = "";
recv($socket, $data, 1400, 0) or (print "0" and exit(1)); recv($socket, $data, 1400, 0) or (print "-1" and exit(1));
my ($servername, $mapname, $game, $fullname, $rest) = split(/\x00/, substr($data, 6), 5); my ($servername, $mapname, $game, $fullname, $rest) = split(/\x00/, substr($data, 6), 5);
my $players = ord(substr($rest, 2, 1)); my $players = ord(substr($rest, 2, 1));
print "$players\n"; print "$players\n";
' "${ark_QueryPort}" "${ark_MultiHome:-127.0.0.1}" ' "$(getQueryPort)" "${ark_MultiHome:-127.0.0.1}"
} }
# #
@ -593,7 +612,17 @@ doRun() {
arkserveropts="$serverMap" arkserveropts="$serverMap"
if [ -n "$serverMapModId" ]; then if [ -n "$serverMapModId" ]; then
arkserveropts="-MapModID=$serverMapModId" 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")"
arkserveropts="${serverMap}?MapModID=${serverMapModId}"
fi fi
if [ -z "$arkserveropts" ]; then if [ -z "$arkserveropts" ]; then
@ -602,6 +631,42 @@ doRun() {
arkextraopts=( ) arkextraopts=( )
while read varname; do
val="${!varname}"
case "$varname" in
ark_*)
name="${varname#ark_}"
# 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
;;
arkopt_*)
name="${varname#arkopt_}"
val="${!varname}"
if [ -n "$val" ]; then
arkextraopts=( "${arkextraopts[@]}" "-${name}=${val}" )
fi
;;
arkflag_*)
name="${varname#arkflag_}"
arkextraopts=( "${arkextraopts[@]}" "-${name}" )
;;
esac
unset $varname
done < <(sed -n 's/^\(ark\(\|opt\|flag\)_[^= ]*\)=.*/\1/p' <"$configfile")
# bring in ark_... options # bring in ark_... options
for varname in "${!ark_@}"; do for varname in "${!ark_@}"; do
name="${varname#ark_}" name="${varname#ark_}"
@ -641,8 +706,6 @@ doRun() {
arkserveropts="${arkserveropts}?listen" arkserveropts="${arkserveropts}?listen"
# run the server in background # run the server in background
echo "`timestamp`: start" echo "`timestamp`: start"
# set max open files limit before we start the server
ulimit -n $maxOpenFiles
serverpid=0 serverpid=0
restartserver=1 restartserver=1
@ -756,10 +819,26 @@ doStartAll(){
# #
doStop() { doStop() {
if isTheServerRunning; then if isTheServerRunning; then
if [[ " $* " =~ " --warn " ]]; then local stopreason="$1"
doWarn "$1" local dowarn=
local warnreason=
local dosave=
shift
for arg in "$@"; do
case "$arg" in
--warn) dowarn=1; ;;
--warnreason=*) warnreason="${arg#*=}"; ;;
--saveworld) dosave=1; ;;
esac
done
if [[ -n "$dowarn" ]]; then
if ! doWarn "$1" "$warnreason"; then
return 1
fi
fi fi
if [[ " $* " =~ " --saveworld " ]]; then if [[ -n "$dosave" ]]; then
doSaveWorld doSaveWorld
fi fi
tput sc tput sc
@ -836,116 +915,243 @@ doInstall() {
getCurrentVersion getCurrentVersion
} }
#
# Cancels a pending shutdown
#
doCancelShutdown(){
if [ -f "${arkserverroot}/.ark-warn.lock" ]; then
local lockpid="$(<"${arkserverroot}/.ark-warn.lock")"
if [ -n "$lockpid" ]; then
kill "$lockpid"
rm -f "${arkserverroot}/.ark-warn.lock"
fi
fi
}
#
# Formats a warning message based on replacement strings
#
printWarnMessage(){
local msg
if [ -n "$msgWarnReason" ]; then
local reason
local msgtime
if [ "$3" == "minutes" ]; then
if [ -n "$msgTimeMinutes" ]; then
msgtime="${msgTimeMinutes//\{minutes\}/$4}"
else
msgtime="$4 minutes"
fi
else
if [ -n "$msgTimeSeconds" ]; then
msgtime="${msgTimeSeconds//\{seconds\}/$4}"
else
msgtime="$4 seconds"
fi
fi
msg="${msgWarnReason//\{time\}/$msgtime}"
if [ "$1" == "update" ]; then
if [ -n "$appupdate" ]; then
if [ -n "$modupdate" ]; then
if [ -n "$msgReasonUpdateAppMod" ]; then
reason="$msgReasonUpdateMod"
else
reason="an update to the game and an update to mod(s) {modnamesupdated}"
fi
else
if [ -n "$msgReasonUpdateApp" ]; then
reason="$msgReasonUpdateApp"
else
reason="an update to the game"
fi
fi
elif [ -n "$modupdate" ]; then
if [ -n "$msgReasonUpdateMod" ]; then
reason="$msgReasonUpdateMod"
else
reason="an update to mod(s) {modnamesupdated}"
fi
fi
elif [ -n "$shutdownreason" ]; then
reason="$shutdownreason"
elif [ "$1" == "restart" ]; then
if [ -n "$msgReasonRestart" ]; then
reason="$msgReasonRestart"
else
reason="a restart"
fi
else
if [ -n "$msgReasonShutdown" ]; then
reason="$msgReasonShutdown"
else
reason="maintenance"
fi
fi
reason="${reason//\{modnamesupdated\}/${modnamesupdated}}"
msg="${msg//\{reason\}/${reason}}"
printf "%s\n" "$msg"
else
if [ "$1" == "update" ]; then
if [ "$3" == "minutes" ]; then
if [ -n "$msgWarnUpdateMinutes" ]; then
msg="${msgWarnUpdateMinutes//%d/$4}"
else
msg="This ARK server will shutdown for an update in $4 minutes"
fi
else
if [ -n "$msgWarnUpdateSeconds" ]; then
msg="${msgWarnUpdateSeconds//%d/$4}"
else
msg="This ARK server will shutdown for an update in $4 seconds"
fi
fi
elif [ "$1" == "restart" ]; then
if [ "$3" == "minutes" ]; then
if [ -n "$msgWarnRestartMinutes" ]; then
msg="${msgWarnRestartMinutes//%d/$4}"
else
msg="This ARK server will shutdown for a restart in $4 minutes"
fi
else
if [ -n "$msgWarnRestartSeconds" ]; then
msg="${msgWarnRestartSeconds//%d/$4}"
else
msg="This ARK server will shutdown for a restart in $4 seconds"
fi
fi
else
if [ "$3" == "minutes" ]; then
if [ -n "$msgWarnShutdownMinutes" ]; then
msg="${msgWarnShutdownMinutes//%d/$4}"
else
msg="This ARK server will shutdown in $4 minutes"
fi
else
if [ -n "$msgWarnShutdownSeconds" ]; then
msg="${msgWarnShutdownSeconds//%d/$4}"
else
msg="This ARK server will shutdown in $4 seconds"
fi
fi
fi
fi
doBroadcastWithEcho "$msg"
}
# #
# Waits for a configurable number of minutes before updating the server # Waits for a configurable number of minutes before updating the server
# #
doWarn(){ doWarn(){
cd "$arkserverroot" cd "$arkserverroot"
local warnmsgmin (
local warnmsgsec echo "$$" >"${arkserverroot}/.ark-warn.lock.$$" 2>/dev/null
while true; do
if [ "$1" == "update" ]; then if ! ln "${arkserverroot}/.ark-warn.lock.$$" "${arkserverroot}/.ark-warn.lock" 2>/dev/null; then
if [ -n "$msgWarnUpdateMinutes" ]; then local lockpid="$(<"${arkserverroot}/.ark-warn.lock")"
warnmsgmin="$msgWarnUpdateMinutes" if [ -n "$lockpid" ] && [ "$lockpid" != "$$" ] && kill -0 "$lockpid" 2>/dev/null; then
else echo "Shutdown warning already in progress (PID: $lockpid)"
warnmsgmin="This ARK server will shutdown for an update in %d minutes" rm -f "${arkserverroot}/.ark-warn.lock.$$" 2>/dev/null
fi exit 1
if [ -n "$msgWarnUpdateSeconds" ]; then fi
warnmsgsec="$msgWarnUpdateSeconds" rm -f "${arkserverroot}/.ark-warn.lock"
else else
warnmsgsec="This ARK server will shutdown for an update in %d seconds" break
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 fi
if (( warnminutes > warninterval )); then done
sleep 1m & rm -f "${arkserverroot}/.ark-warn.lock.$$"
update_cancelled(){
if [ -n "$msgUpdateCancelled" ]; then
msg="${msgUpdateCancelled//%s/$1}"
else
msg="Shutdown cancelled by operator ($1)"
fi
doBroadcastWithEcho "${msg}"
}
trap "update_cancelled 'Ctrl+C'" SIGINT
trap "update_cancelled 'Terminated'" SIGTERM
trap "update_cancelled 'Connection Closed'" SIGHUP
trap "update_cancelled 'Quit'" SIGQUIT
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"
rm -f "${arkserverroot}/.ark-warn.lock"
return 1
fi
if (( warnminutes >= warninterval )); then
sleep 1m &
sleeppid=$!
printWarnMessage "$1" "$2" "minutes" "$warnminutes"
for (( min = warnminutes - 1; min >= warninterval; min-- )); do
numplayers=$(numPlayersConnected)
echo "There are ${numplayers} players connected"
if (( (numplayers + 0) == 0 )); then
doBroadcastWithEcho "Nobody is connected. Shutting down immediately"
rm -f "${arkserverroot}/.ark-warn.lock"
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=$! sleeppid=$!
warnmsg="$(printf "$warnmsgmin" "$warnminutes")" if [ "`getServerPID`" != "$pid" ]; then
doBroadcastWithEcho "$warnmsg" echo "Server has stopped. Aborting update"
for (( min = warnminutes - 1; min >= warninterval; min-- )); do rm -f "${arkserverroot}/.ark-warn.lock"
return 1
fi
printWarnMessage "$1" "$2" "seconds" "$warnseconds"
if (( warnseconds >= 20 )); then
numplayers=$(numPlayersConnected) numplayers=$(numPlayersConnected)
if (( numplayers + 0 == 0 )); then echo "There are ${numplayers} players connected"
echo "Nobody is connected. Shutting down immediately" if (( (numplayers + 0) == 0 )); then
doBroadcastWithEcho "Nobody is connected. Shutting down immediately"
rm -f "${arkserverroot}/.ark-warn.lock"
return 0 return 0
fi 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
fi wait $sleeppid
wait $sleeppid warnseconds=$warninterval
warnseconds=$warninterval done
done fi
fi
if [ "`getServerPID`" != "$pid" ]; then rm -f "${arkserverroot}/.ark-warn.lock"
echo "Server has stopped. Aborting $1"
return 1
fi
return 0 if [ "`getServerPID`" != "$pid" ]; then
echo "Server has stopped. Aborting $1"
return 1
fi
return 0
)
return $?
} }
# #
@ -1068,7 +1274,7 @@ doUpdate() {
fi fi
elif [ "$updatetype" == "ifempty" ]; then elif [ "$updatetype" == "ifempty" ]; then
numplayers=$(( $(numPlayersConnected) + 0 )) numplayers=$(( $(numPlayersConnected) + 0 ))
if (( numplayers == 0 )); then if (( numplayers != 0 )); then
echo "${numplayers} players are still connected" echo "${numplayers} players are still connected"
return 1 return 1
fi fi
@ -1086,7 +1292,7 @@ doUpdate() {
doSaveWorld doSaveWorld
fi fi
doStop doStop update
# If user wants to back-up, we do it here. # If user wants to back-up, we do it here.
@ -1175,9 +1381,10 @@ doDownloadMod(){
while true; do while true; do
echo -n "Downloading mod $modid" echo -n "Downloading mod $modid"
runSteamCMDspinner +workshop_download_item $mod_appid $modid local output=$(runSteamCMDspinnerSubst 5 +workshop_download_item $mod_appid $modid) 5>&1
result=$? result=$?
if [ $result -eq 0 ]; then if [ $result -eq 0 ]; then
modsrcdir="$(echo "$output" | sed -n 's@^Success. Downloaded item [0-9][0-9]* to "\([^"]*\)" .*@\1@p')"
break break
else else
echo echo
@ -1195,6 +1402,7 @@ doDownloadMod(){
if [ -f "$modsrcdir/mod.info" ]; then if [ -f "$modsrcdir/mod.info" ]; then
echo "Mod $modid downloaded" echo "Mod $modid downloaded"
modsrcdirs[$modid]="$modsrcdir"
return 0 return 0
else else
echo "Mod $modid was not successfully downloaded" echo "Mod $modid was not successfully downloaded"
@ -1220,13 +1428,21 @@ isModUpdateNeeded(){
local moddestdir="$arkserverroot/ShooterGame/Content/Mods/$modid" local moddestdir="$arkserverroot/ShooterGame/Content/Mods/$modid"
local modbranch="${mod_branch:-Windows}" local modbranch="${mod_branch:-Windows}"
if [ -n "${modsrcdirs[$modid]}" ]; then
modsrcdir="${modsrcdirs[$modid]}"
fi
for varname in "${!mod_branch_@}"; do for varname in "${!mod_branch_@}"; do
if [ "mod_branch_$modid" == "$varname" ]; then if [ "mod_branch_$modid" == "$varname" ]; then
modbranch="${!varname}" modbranch="${!varname}"
fi fi
done done
if [ \( ! -f "$moddestdir/.modbranch" \) ] || [ "$(<"$moddestdir/.modbranch")" != "$modbranch" ]; then if [ -f "$moddestdir/.modbranch" ]; then
mv "$moddestdir/.modbranch" "$moddestdir/__arkmanager_modbranch__.info"
fi
if [ \( ! -f "$moddestdir/__arkmanager_modbranch__.info" \) ] || [ "$(<"$moddestdir/__arkmanager_modbranch__.info")" != "$modbranch" ]; then
return 0 return 0
fi fi
@ -1245,17 +1461,49 @@ isModUpdateNeeded(){
return 1 return 1
} }
#
# Get the name of the specified mod
#
getModName(){
local modid=$1
local modsrcdir="$steamcmdroot/steamapps/workshop/content/$mod_appid/$modid"
if [ -n "${modsrcdirs[$modid]}" ]; then
modsrcdir="${modsrcdirs[$modid]}"
fi
modname="$(curl -s "http://steamcommunity.com/sharedfiles/filedetails/?id=${modid}" | sed -n 's|^.*<div class="workshopItemTitle">\([^<]*\)</div>.*|\1|p')"
if [ -n "$modname" ]; then
echo "$modname"
else
perl -e '
my $data;
{ local $/; $data = <STDIN>; }
my $mapnamelen = unpack("@0 L<", $data);
my $mapname = substr($data, 4, $mapnamelen - 1);
print $mapname
' <"${modsrcdir}/mod.info"
fi
}
# #
# Checks if any installed or requested mods need to be updated # Checks if any installed or requested mods need to be updated
# #
isAnyModUpdateNeeded(){ isAnyModUpdateNeeded(){
modnamesupdated=""
local ismodupdateneeded=1
for modid in $(getModIds); do for modid in $(getModIds); do
if isModUpdateNeeded $modid; then if isModUpdateNeeded $modid; then
return 0 ismodupdateneeded=0
if [ -n "$modnamesupdated" ]; then
modnamesupdated="${modnamesupdated}, "
fi
modnamesupdated="${modnamesupdated}$(getModName "$modid")"
fi fi
done done
return 1 return $ismodupdateneeded
} }
# #
@ -1267,13 +1515,21 @@ doExtractMod(){
local moddestdir="$arkserverroot/ShooterGame/Content/Mods/$modid" local moddestdir="$arkserverroot/ShooterGame/Content/Mods/$modid"
local modbranch="${mod_branch:-Windows}" local modbranch="${mod_branch:-Windows}"
if [ -n "${modsrcdirs[$modid]}" ]; then
modsrcdir="${modsrcdirs[$modid]}"
fi
for varname in "${!mod_branch_@}"; do for varname in "${!mod_branch_@}"; do
if [ "mod_branch_$modid" == "$varname" ]; then if [ "mod_branch_$modid" == "$varname" ]; then
modbranch="${!varname}" modbranch="${!varname}"
fi fi
done done
if [ \( ! -f "$moddestdir/.modbranch" \) ] || [ "$(<"$moddestdir/.modbranch")" != "$modbranch" ]; then if [ -f "$moddestdir/.modbranch" ]; then
mv "$moddestdir/.modbranch" "$moddestdir/__arkmanager_modbranch__.info"
fi
if [ \( ! -f "$moddestdir/__arkmanager_modbranch__.info" \) ] || [ "$(<"$moddestdir/__arkmanager_modbranch__.info")" != "$modbranch" ]; then
rm -rf "$moddestdir" rm -rf "$moddestdir"
fi fi
@ -1348,26 +1604,42 @@ doExtractMod(){
fi fi
done 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 ' perl -e '
my $data; my $data;
{ local $/; $data = <STDIN>; } { local $/; $data = <STDIN>; }
my $mapnamelen = unpack("@0 L<", $data); my $mapnamelen = unpack("@0 L<", $data);
my $mapname = substr($data, 4, $mapnamelen - 1); my $mapname = substr($data, 4, $mapnamelen - 1);
$mapnamelen += 4; my $nummaps = unpack("@" . ($mapnamelen + 4) . " L<", $data);
my $mapfilelen = unpack("@" . ($mapnamelen + 4) . " L<", $data); my $pos = $mapnamelen + 8;
my $mapfile = substr($data, $mapnamelen + 8, $mapfilelen); my $modname = ($ARGV[1] || $mapname) . "\x00";
print pack("L< L< L< Z8 L< C L< L<", $ARGV[0], 0, 8, "ModName", 1, 0, 1, $mapfilelen); my $modnamelen = length($modname);
print $mapfile; my $modpath = "../../../ShooterGame/Content/Mods/" . $ARGV[0] . "\x00";
my $modpathlen = length($modpath);
print pack("L< L< L< Z$modnamelen L< Z$modpathlen L<",
$ARGV[0], 0, $modnamelen, $modname, $modpathlen, $modpath,
$nummaps);
for (my $mapnum = 0; $mapnum < $nummaps; $mapnum++){
my $mapfilelen = unpack("@" . ($pos) . " L<", $data);
my $mapfile = substr($data, $mapnamelen + 12, $mapfilelen);
print pack("L< Z$mapfilelen", $mapfilelen, $mapfile);
$pos = $pos + 4 + $mapfilelen;
}
print "\x33\xFF\x22\xFF\x02\x00\x00\x00\x01"; print "\x33\xFF\x22\xFF\x02\x00\x00\x00\x01";
' $modid <"$moddestdir/mod.info" >"$moddestdir/.mod" ' $modid "$modname" <"$moddestdir/mod.info" >"${moddestdir}.mod"
if [ -f "$moddestdir/modmeta.info" ]; then if [ -f "$moddestdir/modmeta.info" ]; then
cat "$moddestdir/modmeta.info" >>"$moddestdir/.mod" cat "$moddestdir/modmeta.info" >>"${moddestdir}.mod"
else else
echo -ne '\x01\x00\x00\x00\x08\x00\x00\x00ModType\x00\x02\x00\x00\x001\x00' >>"$moddestdir/.mod" echo -ne '\x01\x00\x00\x00\x08\x00\x00\x00ModType\x00\x02\x00\x00\x001\x00' >>"${moddestdir}.mod"
fi fi
echo "$modbranch" >"$moddestdir/.modbranch" echo "$modbranch" >"$moddestdir/__arkmanager_modbranch__.info"
fi fi
} }
@ -1429,18 +1701,57 @@ doBackup(){
savedir="${ark_AltSaveDirectoryName}" savedir="${ark_AltSaveDirectoryName}"
fi fi
saverootdir="${arkserverroot}/ShooterGame/Saved"
savedcfgdir="${saverootdir}/Config/LinuxServer"
savedir="${saverootdir}/${savedir}"
# Check for the (unlikely) case that the case of the
# saved ark directory is screwed up
if [ ! -d "${savedir}" ]; then
cisavedir="$(find "${arkserverroot}" -ipath "${savedir}" | head -n1)"
if [ -n "$cisavedir" ]; then
echo -e " ${NORMAL}[ ${YELLOW}WARN${NORMAL} ] Saved arks directory capitalization is inconsistent"
savedir="${cisavedir}"
else
echo -e " ${NORMAL}[ ${RED}ERROR${NORMAL} ] Saved arks directory does not exist"
return 1
fi
fi
# ARK server uses Write-Unlink-Rename # ARK server uses Write-Unlink-Rename
echo -ne "${NORMAL} Copying ARK world file " echo -ne "${NORMAL} Copying ARK world file "
cp -p "${arkserverroot}/ShooterGame/Saved/${savedir}/${serverMap##*/}.ark" "${backupdir}/${serverMap##*/}.ark"
if [ ! -f "${backupdir}/${serverMap##*/}.ark" ]; then # Take into account screwed up casing of saved ark files
# in some environments
mapfile="$(find "${savedir}" -iname "${serverMap##*/}.ark" | head -n1)"
if [ -z "$mapfile" ]; then
sleep 2 sleep 2
cp -p "${arkserverroot}/ShooterGame/Saved/${savedir}/${serverMap##*/}.ark" "${backupdir}/${serverMap##*/}.ark" mapfile="$(find "${savedir}" -iname "${serverMap##*/}.ark" | head -n1)"
fi fi
# If both attempts fail, server may have # If both attempts fail, server may have
# crashed between unlink and rename # crashed between unlink and rename
if [ ! -f "${backupdir}/${serverMap##*/}.ark" ]; then if [ -z "$mapfile" ]; then
cp -p "${arkserverroot}/ShooterGame/Saved/${savedir}/${serverMap##*/}.tmp" "${backupdir##*/}/${serverMap##*/}.ark" mapfile="$(find "${savedir}" -iname "${serverMap##*/}.tmp" | head -n1)"
fi fi
# If neither the ark nor the tmp file exists, then the
# map name may be incorrect. Try to get any ark or tmp
# file in the saved arks directory
if [ -z "$mapfile" ]; then
mapfile="$(find "${savedir}" -iname "*.ark" | head -n1)"
if [ -z "$mapfile" ]; then
mapfile="$(find "${savedir}" -iname "*.tmp" | head -n1)"
fi
fi
if [ -f "${mapfile}" ]; then
cp -p "${mapfile}" "${backupdir}/${serverMap##*/}.ark"
fi
if [ -f "${backupdir}/${serverMap##*/}.ark" ]; then if [ -f "${backupdir}/${serverMap##*/}.ark" ]; then
echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]" echo -e "${NORMAL}\e[68G[ ${GREEN}OK${NORMAL} ]"
else else
@ -1452,7 +1763,7 @@ doBackup(){
# ARK server uses a non-blocking lock and will # ARK server uses a non-blocking lock and will
# fail to update the file if the lock fails. # fail to update the file if the lock fails.
echo -e "${NORMAL} Copying ARK profile files" echo -e "${NORMAL} Copying ARK profile files"
for f in "${arkserverroot}/ShooterGame/Saved/${savedir}/"*.arkprofile; do for f in "${savedir}/"*.arkprofile; do
echo -ne "${NORMAL} ${f##*/} " echo -ne "${NORMAL} ${f##*/} "
cp -p "${f}" "${backupdir}/${f##*/}" cp -p "${f}" "${backupdir}/${f##*/}"
if [ ! -s "${backupdir}/${f##*/}" ]; then if [ ! -s "${backupdir}/${f##*/}" ]; then
@ -1473,7 +1784,7 @@ doBackup(){
# ARK server uses Lock-Truncate-Write-Unlock # ARK server uses Lock-Truncate-Write-Unlock
echo -e "${NORMAL} Copying ARK tribe files " echo -e "${NORMAL} Copying ARK tribe files "
for f in "${arkserverroot}/ShooterGame/Saved/${savedir}/"*.arktribe; do for f in "${savedir}/"*.arktribe; do
echo -ne "${NORMAL} ${f##*/} " echo -ne "${NORMAL} ${f##*/} "
cp -p "${f}" "${backupdir}/${f##*/}" cp -p "${f}" "${backupdir}/${f##*/}"
if [ ! -s "${backupdir}/${f##*/}" ]; then if [ ! -s "${backupdir}/${f##*/}" ]; then
@ -1494,7 +1805,7 @@ doBackup(){
# ARK server uses Lock-Truncate-Write-Unlock # ARK server uses Lock-Truncate-Write-Unlock
echo -ne "${NORMAL} Copying GameUserSettings.ini " echo -ne "${NORMAL} Copying GameUserSettings.ini "
cp -p "${arkserverroot}/ShooterGame/Saved/Config/LinuxServer/GameUserSettings.ini" "${backupdir}/GameUserSettings.ini" cp -p "${savedcfgdir}/GameUserSettings.ini" "${backupdir}/GameUserSettings.ini"
if [ ! -s "${backupdir}/GameUserSettings.ini" ]; then if [ ! -s "${backupdir}/GameUserSettings.ini" ]; then
sleep 2 sleep 2
cp -p "${f}" "${backupdir}/${f##*/}" cp -p "${f}" "${backupdir}/${f##*/}"
@ -1508,7 +1819,7 @@ doBackup(){
echo -ne "${NORMAL} Copying Game.ini " echo -ne "${NORMAL} Copying Game.ini "
cp -p "${arkserverroot}/ShooterGame/Saved/Config/LinuxServer/Game.ini" "${backupdir}/Game.ini" cp -p "${savedcfgdir}/Game.ini" "${backupdir}/Game.ini"
if [ ! -s "${backupdir}/Game.ini" ]; then if [ ! -s "${backupdir}/Game.ini" ]; then
sleep 2 sleep 2
cp -p "${f}" "${backupdir}/${f##*/}" cp -p "${f}" "${backupdir}/${f##*/}"
@ -1551,6 +1862,64 @@ doBackup(){
fi fi
} }
#
# Install a cron job to execute a particular command
#
doInstallCronJob(){
hour='*'
minute='0'
cmdopts="${arkCronExtraOpts}"
cmdargs=""
output=">/dev/null 2>&1"
command="$1"
shift
for opt in "$@"; do
case "$opt" in
--daily)
;;
--hourly)
hour='*'
;;
--hour=*)
hour="${opt#--hour=}"
;;
--minute=*)
minute="${opt#--minute=}"
;;
--enable-output)
output=
;;
--arg=*)
cmdargs="${cmdargs} $(printf "%q" "${opt#--opt=}")"
;;
--*)
cmdopts="${cmdopts} $(printf "%q" "${opt}")"
;;
*)
cmdargs="${args} $(printf "%q" "${opt}")"
;;
esac
done
cronjob="${minute} ${hour} * * * arkmanager --cronjob ${command} @${instance} ${cmdopts} --args ${cmdargs} -- ${output}"
(crontab -l | \
sed -e "/ [*] [*] [*] arkmanager --cronjob ${command} @${instance} /d";
echo "${cronjob}" ) | \
crontab -
}
#
# Removes an installed cron job
#
doRemoveCronJob(){
command="$1"
crontab -l | \
sed -e "/ [*] [*] [*] arkmanager --cronjob ${command} @${instance} /d" | \
crontab -
}
# #
# Print the status of the server (running? online? version?) # Print the status of the server (running? online? version?)
@ -1687,6 +2056,8 @@ showUsage() {
echo "installmod <modid> Installs a mod from the Steam workshop" echo "installmod <modid> Installs a mod from the Steam workshop"
echo "uninstallmod <modid> Removes the mod from the Mods directory" echo "uninstallmod <modid> Removes the mod from the Mods directory"
echo "reinstallmod <modid> Removes and re-installs a mod in the Mods directory" echo "reinstallmod <modid> Removes and re-installs a mod in the Mods directory"
echo "install-cronjob <cmd> Adds a cron job using the specified command"
echo "remove-cronjob <cmd> Removes a cron job that used the specified command"
echo "restart Stops the server and then starts it" echo "restart Stops the server and then starts it"
echo "run Runs the server without daemonizing" echo "run Runs the server without daemonizing"
echo "start Starts the server" echo "start Starts the server"
@ -1722,6 +2093,26 @@ while true; do
shift shift
nrarg=0 nrarg=0
# Handle global options
case "$command" in
--verbose)
verbose=1
continue
;;
--dots)
progressDisplayType=dots
continue
;;
--spinner)
progressDisplayType=spinner
continue
;;
--cronjob)
inCronJob=true
continue
;;
esac
# get the number of arguments for commands that take arguments # get the number of arguments for commands that take arguments
case "$command" in case "$command" in
installmod) nrarg=1; ;; installmod) nrarg=1; ;;
@ -1730,6 +2121,8 @@ while true; do
broadcast) nrarg=1; ;; broadcast) nrarg=1; ;;
rconcmd) nrarg=1; ;; rconcmd) nrarg=1; ;;
useconfig) nrarg=1; ;; useconfig) nrarg=1; ;;
install-cronjob) nrarg=1; ;;
remove-cronjob) nrarg=1; ;;
esac esac
# Enumerate the options and arguments # Enumerate the options and arguments
@ -1796,6 +2189,7 @@ while true; do
if [ -n "${arkstCommit}" ]; then if [ -n "${arkstCommit}" ]; then
echo "Commit: ${arkstCommit:0:7}" echo "Commit: ${arkstCommit:0:7}"
fi fi
echo "Blob SHA: $( (echo -ne "blob $(stat -c "%s" "$0")\0"; sed "s@^arkstCommit=.*@arkstCommit=''@" "$0") | sha1sum | cut -d' ' -f1)"
exit 1 exit 1
;; ;;
-h|--help) -h|--help)
@ -1851,6 +2245,9 @@ while true; do
doStop restart "${options[@]}" doStop restart "${options[@]}"
echo "`timestamp`: stop" >> "$logdir/$arkmanagerLog" echo "`timestamp`: stop" >> "$logdir/$arkmanagerLog"
;; ;;
cancelshutdown)
doCancelShutdown "${options[@]}"
;;
install) install)
doInstall doInstall
;; ;;
@ -1885,6 +2282,12 @@ while true; do
status) status)
printStatus printStatus
;; ;;
install-cronjob)
doInstallCronJob "${args[@]}" "${options[@]}"
;;
remove-cronjob)
doRemoveCronJob "${args[@]}"
;;
*) *)
echo -n "arkmanager v${arkstVersion}: unknown command '$command' specified" echo -n "arkmanager v${arkstVersion}: unknown command '$command' specified"
showUsage showUsage

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
DAEMON=/usr/bin/arkmanager DAEMON="/usr/bin/arkmanager"
for service in $(${DAEMON} list-instances --brief); do for service in $(${DAEMON} list-instances --brief); do
case "$1" in case "$1" in