451 lines
16 KiB
Bash
Executable File
451 lines
16 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# kf2-srv is a command line tool for managing a set of Killing Floor 2 servers.
|
||
# Copyright (C) 2019 GenZmeY
|
||
# mailto: genzmey@gmail.com
|
||
#
|
||
# This program is free software: you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation, either version 3 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# This program is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
source /etc/steamcmd/steamcmd.conf
|
||
source /etc/kf2-srv/kf2-srv.conf
|
||
|
||
ScriptFullname=$(readlink -e "$0")
|
||
ScriptName=$(echo "$ScriptFullname" | awk -F '/' '{print $NF;}')
|
||
|
||
AppNum="232130"
|
||
InstallDir="/usr/games/kf2-srv"
|
||
ActiveBranch="$InstallDir/activebranch.txt"
|
||
AppBin="$InstallDir/Binaries/Win64/KFGameSteamServer.bin.x86_64"
|
||
DefaultConfigDir="$InstallDir/KFGame/Config"
|
||
CustomMapsDir="$InstallDir/KFGame/Cache"
|
||
InstanceConfigDir="$DefaultConfigDir/instances"
|
||
InstanceConfigLnk="/etc/kf2-srv/instances"
|
||
MainConfigTemplate="/etc/kf2-srv/main.conf.template"
|
||
|
||
DiffArray=('Normal' 'Hard' 'Suicide' 'Hell')
|
||
WaveArray=('4' '7' '10')
|
||
declare -A ModeArray
|
||
ModeArray['KFGameContent.KFGameInfo_Survival']='Survival'
|
||
ModeArray['KFGameContent.KFGameInfo_WeeklySurvival']='Weekly'
|
||
ModeArray['KFGameContent.KFGameInfo_Endless']='Endless'
|
||
ModeArray['KFGameContent.KFGameInfo_Objective']='Objective'
|
||
ModeArray['KFGameContent.KFGameInfo_VersusSurvival']='Versus'
|
||
|
||
function show_help ()
|
||
{
|
||
echo "$ScriptName"
|
||
echo "Централизование управление серверами Killing Floor 2"
|
||
echo "Usage:"
|
||
echo "$ScriptName OPTION [INSTANCE]"
|
||
echo ""
|
||
echo "Mandatory arguments to long options are mandatory for short options too."
|
||
echo "TODO: English description"
|
||
echo " -n, --new INSTANCE создает новый ЭКЗЕМПЛЯР сервера"
|
||
echo " -d, --delete [INSTANCE] удаляет указанный ЭКЗМПЛЯР сервера; если"
|
||
echo " ЭКЗЕМПЛЯР не указан, удаляет все сервера"
|
||
echo " -s, --status [INSTANCE] отображает состояние указанного ЭКЗЕМПЛЯРА"
|
||
echo " сервера; если ЭКЗЕМПЛЯР не указан,"
|
||
echo " отображает состояние всех экземпляров сервера"
|
||
echo " -u, --update при первом запуске производит установку KF2;"
|
||
echo " в дальнейшем устанавливает обновления при их"
|
||
echo " наличии"
|
||
echo " -v, --validate проверяет целостность файлов, при"
|
||
echo " необходимости перекачивает их."
|
||
echo " -r, --run [OPTIONS] запускает экземпляр сервера с указанными"
|
||
echo " ПАРАМЕТРАМИ"
|
||
echo " --start [INSTANCE] запускает указанный ЭКЗЕМПЛЯР сервера; если"
|
||
echo " ЭКЗЕМПЛЯР не указан, запускает все"
|
||
echo " автозапускаемые экземпляры сервера"
|
||
echo " --stop [INSTANCE] останавливает указанный ЭКЗЕМПЛЯР сервера;"
|
||
echo " если ЭКЗЕМПЛЯР не указан, останавливает все"
|
||
echo " экземпляры сервера"
|
||
echo " --restart [INSTANCE] перезапускает указанный ЭКЗЕМПЛЯР сервера;"
|
||
echo " если ЭКЗЕМПЛЯР не указан, перезапускает"
|
||
echo " все автозапускаемые экземпляры сервера"
|
||
echo " --enable [INSTANCE] добавляет указанный ЭКЗЕМПЛЯР сервера в"
|
||
echo " автозапуск; если ЭКЗЕМПЛЯР не указан,"
|
||
echo " добавляет все экземпляры сервера в автозапуск"
|
||
echo " --disable [INSTANCE] удаляет указанный ЭКЗЕМПЛЯР сервера из"
|
||
echo " автозапуска; если ЭКЗЕМПЛЯР не указан,"
|
||
echo " удаляет все экземпляры сервера из автозапуска"
|
||
echo " --map-sync [INSTANCE] синхронизирует список сторонних карт в"
|
||
echo " конфигурационном файле ЭКЗЕМПЛЯРА с"
|
||
echo " имеющимися файлами сторонних карт; если"
|
||
echo " ЭКЗЕМПЛЯР не указан, синхронизирует все"
|
||
echo " экземпляры серверов"
|
||
echo " -h, --help display this help and exit"
|
||
}
|
||
|
||
# Use this function with non-root user only!!!
|
||
function run_as_root () # $@: Args
|
||
{
|
||
if [[ -n $(groups "$USER" | grep -Fo 'wheel') ]]; then
|
||
sudo "$ScriptFullname" $@
|
||
else
|
||
echo "You must be root or sudo-user to run this command."
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
function service_name () # $1: Instance
|
||
{
|
||
echo "kf2-srv@$1.service"
|
||
}
|
||
|
||
function show_instances ()
|
||
{
|
||
find "$InstanceConfigDir" -maxdepth 1 -mindepth 1 -type d -printf "%f\n"
|
||
}
|
||
|
||
function instance_exists () # $1: Instance
|
||
{
|
||
if show_instances | grep -qP "^.*[ ]*$1[ ]*.*$" ; then
|
||
return 0
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
function server_exists ()
|
||
{
|
||
if [[ -n $(ls "$InstallDir") ]]; then
|
||
return 0
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
function updates_aviable ()
|
||
{
|
||
# TODO: implementation
|
||
return 0
|
||
}
|
||
|
||
function new_instance () # $1: InstanceName
|
||
{
|
||
local Instance="$1"
|
||
if [[ -z "$Instance" ]]; then
|
||
echo "Name of instance must be set"
|
||
exit 1
|
||
elif ! server_exists; then
|
||
echo "You must install server first"
|
||
echo "Run \"$ScriptName --update\" to install it"
|
||
exit 1
|
||
elif instance_exists "$Instance"; then
|
||
echo "Instance $Instance already exists"
|
||
exit 1
|
||
fi
|
||
|
||
local InstanceDir="$InstanceConfigDir/$Instance"
|
||
if [[ -d "$InstanceDir" ]]; then
|
||
echo "$InstanceDir already exists"
|
||
exit 1
|
||
fi
|
||
|
||
mkdir -p "$InstanceDir/LinuxServer" && chown -R "$SteamUser:$SteamUser" "$InstanceDir"
|
||
|
||
cp -a "$MainConfigTemplate" "$InstanceDir/main.conf"
|
||
cp -a "$DefaultConfigDir/KFAI.ini" "$InstanceDir"
|
||
cp -a "$DefaultConfigDir/KFWeb.ini" "$InstanceDir"
|
||
cp -a "$DefaultConfigDir/LinuxServer-KFEngine.ini" "$InstanceDir"
|
||
cp -a "$DefaultConfigDir/LinuxServer-KFGame.ini" "$InstanceDir"
|
||
cp -a "$DefaultConfigDir/LinuxServer-KFInput.ini" "$InstanceDir"
|
||
cp -a "$DefaultConfigDir/LinuxServer-KFSystemSettings.ini" "$InstanceDir"
|
||
cp -a "$DefaultConfigDir/LinuxServer/LinuxServerEngine.ini" "$InstanceDir/LinuxServer"
|
||
cp -a "$DefaultConfigDir/LinuxServer/LinuxServerGame.ini" "$InstanceDir/LinuxServer"
|
||
cp -a "$DefaultConfigDir/LinuxServer/LinuxServerInput.ini" "$InstanceDir/LinuxServer"
|
||
cp -a "$DefaultConfigDir/LinuxServer/LinuxServerSystemSettings.ini" "$InstanceDir/LinuxServer"
|
||
|
||
echo "Instance $Instance created. See /etc/$ScriptName/instances/$Instance for edit configuration"
|
||
}
|
||
|
||
function delete_instance () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -z "$Instance" ]]; then
|
||
echo "Are you sure you want to delete all instances? [y/N]"
|
||
local Answ="N"
|
||
read Answ
|
||
if [[ "$Answ" == "y" || "$Answ" == "Y" ]]; then
|
||
for Instance in $(show_instances)
|
||
do
|
||
stop_instance "$Instance"
|
||
delete_instance "$Instance"
|
||
done
|
||
fi
|
||
elif instance_exists "$Instance"; then
|
||
local InstanceDir="$InstanceConfigDir/$Instance"
|
||
stop_instance "$Instance"
|
||
rm -rf "$InstanceDir"
|
||
echo "Instance $Instance removed"
|
||
else
|
||
echo "Instance $Instance not exists"
|
||
fi
|
||
}
|
||
|
||
function show_status_implementation () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -n "$Instance" ]]; then
|
||
if systemctl -q is-enabled $(service_name "$Instance"); then
|
||
local IsEnabled="enabled"
|
||
else
|
||
local IsEnabled="disabled"
|
||
fi
|
||
if systemctl | grep $(service_name "$Instance") | grep -q 'running' ; then
|
||
local IsRuning="running"
|
||
else
|
||
local IsRuning="dead"
|
||
fi
|
||
|
||
local Description=$(grep -P 'Description=' "$InstanceConfigDir/$Instance/main.conf" | sed -r 's/(Description=|")//g')
|
||
local GamePort=$(grep -Po '"-port=([0-9]+)' "$InstanceConfigDir/$Instance/main.conf" | grep -Po '[0-9]+$')
|
||
local WebAdminPort=$(grep -Po '"-webadminport=([0-9]+)' "$InstanceConfigDir/$Instance/main.conf" | grep -Po '[0-9]+$')
|
||
local QueryPort=$(grep -Po '"-queryport=([0-9]+)' "$InstanceConfigDir/$Instance/main.conf" | grep -Po '[0-9]+$')
|
||
local GameType=$(grep -Po 'Game=([^\?]+)' "$InstanceConfigDir/$Instance/main.conf" | sed -r 's/Game=([^?]+)/\1/g' )
|
||
local GameLength=$(grep -Po 'GameLength=([0-9]+)' "$InstanceConfigDir/$Instance/main.conf" | grep -Po '[0-9]+$')
|
||
local GameDifficulty=$(grep -Po 'Difficulty=([0-9]+)' "$InstanceConfigDir/$Instance/main.conf" | grep -Po '[0-9]+$')
|
||
|
||
local DisplayGameType=${ModeArray[$GameType]}
|
||
local DisplayGameLength=${WaveArray[$GameLength]}
|
||
local DisplayDifficulty=${DiffArray[$GameDifficulty]}
|
||
|
||
if [[ "$DisplayGameType" == 'Weekly' || \
|
||
"$DisplayGameType" == 'Endless' || \
|
||
"$DisplayGameType" == 'Versus' || \
|
||
"$DisplayGameType" == 'Objective' ]]; then
|
||
DisplayGameLength='-'
|
||
fi
|
||
|
||
if [[ "$DisplayGameType" == 'Weekly' || \
|
||
"$DisplayGameType" == 'Versus' ]]; then
|
||
DisplayDifficulty='-'
|
||
fi
|
||
|
||
echo -e "$Instance:$IsEnabled:$IsRuning:$GamePort:$QueryPort:$WebAdminPort:$DisplayGameType:$DisplayGameLength:$DisplayDifficulty:$Description"
|
||
else
|
||
echo -e "INSTANCE:AUTORUN:STATE:P_GAME:P_QUERY:P_WEB:TYPE:LEN:DIFF:DESCRIPTION"
|
||
for Instance in $(show_instances)
|
||
do
|
||
show_status_implementation "$Instance"
|
||
done
|
||
fi
|
||
}
|
||
|
||
function show_status () # $1: [InstanceName]
|
||
{
|
||
show_status_implementation "$1" | column -t -s :
|
||
}
|
||
|
||
function validate ()
|
||
{
|
||
if [[ -n "$BranchName" ]]; then
|
||
local BetaArg="-beta $BranchName"
|
||
fi
|
||
stop_instance
|
||
steamcmd +login $SteamLogin +force_install_dir $InstallDir +app_update $AppNum $BetaArg validate +exit
|
||
echo "$BranchName" > "$ActiveBranch"
|
||
start_instance
|
||
}
|
||
|
||
function update_kf2 ()
|
||
{
|
||
if [[ -n "$BranchName" ]]; then
|
||
local BetaArg="-beta $BranchName"
|
||
fi
|
||
if ! server_exists; then # First install
|
||
mkdir -p "$InstanceConfigDir" "$CustomMapsDir" && chown -R "$SteamUser:$SteamUser" "$InstallDir/KFGame"
|
||
ln -s "$InstanceConfigDir" "$InstanceConfigLnk"
|
||
echo "$BranchName" > "$ActiveBranch"
|
||
steamcmd +login $SteamLogin +force_install_dir $InstallDir +app_update $AppNum $BetaArg validate +exit
|
||
elif updates_aviable; then # Update
|
||
if [[ "$BranchName" == $(cat "$ActiveBranch") ]]; then
|
||
stop_instance
|
||
steamcmd +login $SteamLogin +force_install_dir $InstallDir +app_update $AppNum $BetaArg +exit
|
||
start_instance
|
||
else
|
||
validate
|
||
fi
|
||
fi
|
||
}
|
||
|
||
function start_instance () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -n "$Instance" ]] ; then
|
||
if instance_exists "$Instance"; then
|
||
systemctl start $(service_name "$Instance")
|
||
else
|
||
echo "Instance $Instance not exitst"
|
||
fi
|
||
else
|
||
for Instance in $(show_instances)
|
||
do
|
||
if systemctl -q is-enabled $(service_name "$Instance") ; then
|
||
start_instance "$Instance"
|
||
fi
|
||
done
|
||
fi
|
||
}
|
||
|
||
function stop_instance () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -n "$Instance" ]]; then
|
||
if instance_exists "$Instance"; then
|
||
systemctl stop $(service_name "$Instance")
|
||
else
|
||
echo "Instance $Instance not exitst"
|
||
fi
|
||
else
|
||
for Instance in $(show_instances)
|
||
do
|
||
stop_instance "$Instance"
|
||
done
|
||
fi
|
||
}
|
||
|
||
function restart_instance () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -n "$Instance" ]] ; then
|
||
if instance_exists "$Instance"; then
|
||
systemctl restart $(service_name "$Instance")
|
||
else
|
||
echo "Instance $Instance not exitst"
|
||
fi
|
||
else
|
||
for Instance in $(show_instances)
|
||
do
|
||
if systemctl -q is-enabled $(service_name "$Instance") ; then
|
||
restart_instance "$Instance"
|
||
fi
|
||
done
|
||
fi
|
||
}
|
||
|
||
function enable_instance () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -n "$Instance" ]]; then
|
||
if instance_exists "$Instance"; then
|
||
systemctl enable $(service_name "$Instance")
|
||
else
|
||
echo "Instance $Instance not exitst"
|
||
fi
|
||
else
|
||
for Instance in $(show_instances)
|
||
do
|
||
enable_instance "$Instance"
|
||
done
|
||
fi
|
||
}
|
||
|
||
function disable_instance () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -n "$Instance" ]]; then
|
||
if instance_exists "$Instance"; then
|
||
systemctl disable $(service_name "$Instance")
|
||
else
|
||
echo "Instance $Instance not exitst"
|
||
fi
|
||
else
|
||
for Instance in $(show_instances)
|
||
do
|
||
disable_instance "$Instance"
|
||
done
|
||
fi
|
||
}
|
||
|
||
function run ()
|
||
{
|
||
if [[ "$USER" == "$SteamUser" ]]; then
|
||
"$AppBin" "$@"
|
||
elif [[ -n $(groups "$USER" | grep -Fo 'wheel') ]] || [[ "$EUID" -eq 0 ]]; then
|
||
sudo -u "$SteamUser" "$AppBin" "$@"
|
||
else
|
||
echo "You must be a $SteamUser, root or sudo-user to run this command."
|
||
fi
|
||
}
|
||
|
||
function map_sync () # $1: [InstanceName]
|
||
{
|
||
local Instance="$1"
|
||
if [[ -n "$Instance" ]]; then
|
||
if instance_exists "$Instance"; then
|
||
stop_instance "$Instance"
|
||
local Config="$InstanceConfigDir/$Instance/LinuxServer-KFGame.ini"
|
||
for MapFile in $(find "$CustomMapsDir" -type f -name '*.kfm' -printf "%f\n")
|
||
do
|
||
MapName=$(echo "$MapFile" | sed -r 's|.kfm$||g')
|
||
if [[ ! -f "$Config" ]]; then
|
||
echo "$Config does not exist!"
|
||
elif grep -qP "MapName=$MapName[ $]" "$Config"; then
|
||
echo "$MapName is already in $Config."
|
||
else
|
||
echo "Adding $MapName to $Config."
|
||
echo -e "
|
||
[$MapName KFMapSummary]
|
||
MapName=$MapName
|
||
bPlayableInSurvival=True
|
||
bPlayableInWeekly=True
|
||
bPlayableInVsSurvival=True
|
||
bPlayableInEndless=True
|
||
bPlayableInObjective=False" >> "$Config"
|
||
fi
|
||
done
|
||
else
|
||
echo "Instance $Instance not exitst"
|
||
fi
|
||
else
|
||
for Instance in $(show_instances)
|
||
do
|
||
map_sync "$Instance"
|
||
done
|
||
fi
|
||
}
|
||
|
||
function map_rotate_save ()
|
||
{
|
||
# TODO: implementation
|
||
echo "Dummy..."
|
||
}
|
||
|
||
function map_rotate_load ()
|
||
{
|
||
# TODO: implementation
|
||
echo "Dummy..."
|
||
}
|
||
|
||
if [[ $# -eq 0 ]]; then show_help; exit 0; fi
|
||
case $1 in
|
||
-h|--help ) show_help; ;;
|
||
-n|--new ) if [[ "$EUID" -eq 0 ]]; then new_instance "$2"; else run_as_root $@; fi;;
|
||
-d|--delete ) if [[ "$EUID" -eq 0 ]]; then delete_instance "$2"; else run_as_root $@; fi;;
|
||
-s|--status ) show_status "$2"; ;;
|
||
-u|--update ) if [[ "$EUID" -eq 0 ]]; then update_kf2 ; else run_as_root $@; fi;;
|
||
-v|--validate ) if [[ "$EUID" -eq 0 ]]; then validate ; else run_as_root $@; fi;;
|
||
-r|--run ) run $@ ; ;;
|
||
--start ) if [[ "$EUID" -eq 0 ]]; then start_instance "$2"; else run_as_root $@; fi;;
|
||
--stop ) if [[ "$EUID" -eq 0 ]]; then stop_instance "$2"; else run_as_root $@; fi;;
|
||
--restart ) if [[ "$EUID" -eq 0 ]]; then restart_instance "$2"; else run_as_root $@; fi;;
|
||
--enable ) if [[ "$EUID" -eq 0 ]]; then enable_instance "$2"; else run_as_root $@; fi;;
|
||
--disable ) if [[ "$EUID" -eq 0 ]]; then disable_instance "$2"; else run_as_root $@; fi;;
|
||
--map-sync ) if [[ "$EUID" -eq 0 ]]; then map_sync "$2"; else run_as_root $@; fi;;
|
||
--map-rotate-save ) map_rotate_save; ;;
|
||
--map-rotate-load ) map_rotate_load; ;;
|
||
* ) echo "Command not recognized: $1"; exit 1 ;;
|
||
esac
|