#!/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 . 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" 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 " -l, --list перечисляет все имеющиеся экземпляры сервера" 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 " --enable [INSTANCE] добавляет указанный ЭКЗЕМПЛЯР сервера в" echo " автозапуск; если ЭКЗЕМПЛЯР не указан," echo " добавляет все экземпляры сервера в автозапуск" echo " --disable [INSTANCE] удаляет указанный ЭКЗЕМПЛЯР сервера из" echo " автозапуска; если ЭКЗЕМПЛЯР не указан," echo " удаляет все экземпляры сервера из автозапуска" echo " --map-sync [INSTANCE] синхронизирует список сторонних карт в" echo " конфигурационном файле ЭКЗЕМПЛЯРА с" echo " имеющимися файлами сторонних карт; если" echo " ЭКЗЕМПЛЯР не указан, синхронизирует все" echo " экземпляры серверов" echo " --ban-sync TODO: description & implementation" echo " --map-rotate-save TODO: description & implementation" echo " --map-rotate-load TODO: description & implementation" 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 $InstanceDir 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]+$') echo -e "$Instance:$IsEnabled:$IsRuning:$GamePort:$QueryPort:$WebAdminPort:$Description" else echo -e "INSTANCE:AUTORUN:STATE:P_GAME:P_QUERY:P_WEB: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 steamcmd +login $SteamLogin +force_install_dir $InstallDir +app_update $AppNum $BetaArg validate +exit mkdir "$InstanceConfigDir" "$CustomMapsDir" && chown -R "$SteamUser:$SteamUser" "$InstallDir/KFGame" ln -s "$InstanceConfigDir" "$InstanceConfigLnk" echo "$BranchName" > "$ActiveBranch" 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 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 ban_sync () { # TODO: implementation echo "Dummy..." } 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;; -l|--list ) show_instances; ;; -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;; --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;; --ban-sync ) ban_sync; ;; --map-rotate-save ) map_rotate_save; ;; --map-rotate-load ) map_rotate_load; ;; esac