Killing Floor Linux Server: a screen Launch Script, Auto-Restart, cron Updates and nginx Map Downloads
This page continues the KF1 Linux server guide with the scripts REGETT and Oyashiro shared on the killingfloor.ru forum in 2012. Example paths: server in /opt/KFDS, SteamCMD in /opt/SteamCMD. Use your own.
server.sh: start, stop and auto-restart
One file, and the only dependency is screen (sudo apt-get install screen). The server runs in its own screen session, and a "controller" uses watch to call server.sh check every 30 seconds and start the server again if the session is gone. Crashes can be logged.
#! /bin/sh
## v0.0.2
PATH=/usr/bin:/usr/sbin:/bin:/sbin
# screen session name for the server
SERVER_NAME="MyServer"
# the server's System folder
GAME_PATH="/opt/KFDS/System"
# controller name
CONTROLLER="MyController"
# folder of this script
PWD_PATH=$(dirname `readlink -f $0`)
# crash log: false or a path, e.g. /var/log/kf-crashes.txt
CRASH_LOG="false"
check(){
PID=`ps fax | grep $SERVER_NAME | grep SCREEN | awk '{print $1}'`
if [ -z "$PID" ]
then
if [ "$CRASH_LOG" != "false" ]
then
echo $(date +%d.%m.%Y%t%H:%M:%S%t)"- Game server is crashed!" >>$CRASH_LOG
fi
echo "Game server is not running!"
start
else
echo "Game server is running..."
fi
}
start(){
cd $GAME_PATH && screen -AmdS $SERVER_NAME ./ucc-bin server KF-BioticsLab.rom?game=KFmod.KFGameType?VACSecured=true?MaxPlayers=6?ini=KillingFloor.ini -nohomedir
echo "Starting Game server...!"
}
controller(){
if test -r "${PWD_PATH}/PPID"
then
echo "$CONTROLLER is running..."
else
watch -n 30 "$PWD_PATH/server.sh check" >>/dev/null &
echo $!>${PWD_PATH}/PPID
echo "Starting Game server and AutoRestart..."
fi
}
stop(){
if [ "$1" = "controller" ]
then
if test -r "${PWD_PATH}/PPID"
then
PID=`cat ${PWD_PATH}/PPID`
kill $PID
rm -rf ${PWD_PATH}/PPID
else
echo "Controller not running!"
fi
else
PID=`ps fax | grep $1 | grep SCREEN | awk '{ print $1 }'`
if [ -z "$PID" ]
then
echo "$1 not running!"
else
kill $PID
fi
fi
}
case "$1" in
start) controller ;;
stop) echo "Stopping Game server and AutoRestart..."; stop controller; stop ${SERVER_NAME} ;;
restart) stop ${SERVER_NAME}; start ;;
check) check ;;
*) echo "Usage: $0 {start|stop|restart|check}"; exit 1
esac
exit 0
Usage: ./server.sh start, ./server.sh stop, ./server.sh restart. The PPID file next to the script holds the controller's PID; do not delete it by hand. Today a systemd unit with Restart=always is the simpler way to do the same, but the script helps where systemd is not available.
screen cheat sheet
| Command | What it does |
|---|---|
screen -dmS MyServer command | Run a command in a new detached session |
screen -ls | List all sessions |
screen -r MyServer | Attach to a session (the server console) |
Ctrl+A, then D | Detach and leave the server running |
screen -wipe | Remove dead sessions |
Do not leave the server console with exit or Ctrl+C: that stops the server.
Daily updates with SteamCMD and cron
The server must be stopped before an update and started after it. SteamCMD script /opt/KFDS/update.txt:
login USERNAME PASSWORD
force_install_dir /opt/KFDS/
app_update 215360 validate
exit
215360 is the KF1 server app ID for Linux (215350 for Windows). The KF1 server needs a SteamCMD login with a regular Steam account; it does not have to own the game. Use a separate account without Steam Guard for the server: the password sits in the file in plain text, so restrict it to the server user (chmod 600 update.txt). Wrapper /opt/KFDS/updater.sh (remember chmod +x):
#!/bin/sh
/opt/KFDS/server.sh stop
/opt/SteamCMD/steamcmd.sh +runscript /opt/KFDS/update.txt
/opt/KFDS/server.sh start
Add a line to /etc/crontab, for example daily at 6:00:
00 6 * * * user /opt/KFDS/updater.sh
cron rereads its configuration without a service restart via killall -HUP cron. Instead of editing /etc/crontab you can drop the job into its own file in /etc/cron.d/.
Serving compressed maps with nginx
For fast map downloads (redirect), Apache is overkill; lightweight nginx is enough:
sudo apt-get install nginx
sudo mkdir /opt/files
Example /etc/nginx/conf.d/default.conf: the first block drops requests for any other host name, the second serves files on the server IP.
server {
listen 80;
server_name _;
return 444;
}
server {
listen 80;
server_name 111.222.333.444;
access_log off;
location ~* \.(uz2|7z|zip|rar)$ {
root /opt/files/;
expires 30d;
}
location ~ /\.ht { deny all; }
}
Check and restart with sudo nginx -t and sudo systemctl restart nginx. Put the .uz2 files in /opt/files/ and set the address as RedirectToURL in [IpDrv.HTTPDownload] of KillingFloor.ini.
To let only the game, not any browser, download the files, allow .uz2 only for the Unreal user agent:
location ~* \.uz2$ {
if ( $http_user_agent !~ Unreal ) { return 444; }
root /opt/files/;
expires 30d;
}
Keep the compressed files outside the public web root: then they can only be fetched by direct link http://host/name.uz2, and only by the game.
A simpler option: init.d and a cron check
This is how it was done in 2011 in the thread "Создание сервера на Linux" (Setting up a server on Linux, mangust and Ren, killingfloor.ru forum) on Debian. The server was installed with the now obsolete HLDSUpdateTool back then, but the autostart scheme still works when systemd is not available.
Start at boot. A script in /etc/init.d/ (for example KFautostart); replace the quoted values with your own:
#! /bin/sh
PATH=/usr/bin:/usr/sbin:/bin:/sbin
SCREEN_NAME="kfserver"
GAME_PATH="/home/KF1/system"
start()
{
cd $GAME_PATH && screen -AmdS $SCREEN_NAME ./ucc-bin server [your server launch parameters]
}
stop()
{
PID=`ps fax | grep $SCREEN_NAME | grep SCREEN | awk '{ print $1 }'`
kill $PID
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
Make the file executable and register it with update-rc.d KFautostart defaults.
Restart after a crash. A cron job checks once a minute whether the server's screen session exists and starts it if not (Ren's version):
#!/bin/sh
if [[ `screen -ls |grep screen_name` ]]
then
echo
else
launch command or script
fi
Crontab line: * * * * * /path/to/script. The server.sh script above does the same without cron, using watch.
Restart loop that keeps old logs, and rc.local. A setup by baikal_87 from the "не стартует скрипт из rc.local" (script from rc.local does not start) thread on the killingfloor.ru forum. kf1.sh starts a second script inside screen:
#! /bin/bash
screen -A -m -d -S skf ./kf2.sh
kf2.sh loops forever: it copies the previous ucc.log under a name with the date, starts the server pinned to the first core (taskset -c 0), kills the process after it exits, appends the crash time to kf_crash_times.log and starts the server again after 3 seconds:
#! /bin/bash
while true
do
cd /home/game/KILLINGFLOOR/System/; cp ucc.log /home/game/KILLINGFLOOR/ucc.log; cd /home/game/KILLINGFLOOR/; mv ucc.log ucc-$(date +%d%m%y%H%M).log
cd /home/game/KILLINGFLOOR/System/; taskset -c 0 ./ucc-bin server KF-westlondon.rom?game=KFMod.KFGameType?VACSecured=True?MaxPlayers=20?ini=KillingFLoor.ini +net_public_adr x.xxx.xx.xx -nohomedir
kill -9 $(ps ax | grep KF-westlondon | grep -v grep | awk '{print $1}')
date >> /home/game/KILLINGFLOOR/kf_crash_times.log
echo !!! Waiting 3 seconds to restart...
sleep 3
done
It worked from a terminal but not from rc.local at boot. siriushard advised putting the script path in rc.local on its own line, without extras such as taskset, before exit 0:
/home/game/KILLINGFLOOR/System/kf1.sh
exit 0
He also advised against capital letters in paths. The thread author did not report the result.
The ready-made option: LinuxGSM
Everything above (SteamCMD install, start and stop, updates, monitoring with alerts, backups, console, map compression) is handled by Daniel Gibbs's kfserver script, which grew into LinuxGSM. A translation of its documentation was posted on the forum in 2014. If you do not want to maintain your own scripts, start there. Options are compared in Server setup options when SteamCMD fails.