├── .ansible-lint ├── .gitattributes ├── .githooks └── pre-commit ├── .github ├── FUNDING.yml └── workflows │ ├── almalinux-8.yml │ ├── almalinux-9.yml │ ├── fedora-40.yml │ ├── fedora-41.yml │ ├── lint.yml │ ├── ubuntu-22.04.yml │ └── ubuntu-24.04.yml ├── .gitignore ├── .yamllint ├── CHANGELOG.md ├── LICENSE ├── README.md ├── ansible.cfg ├── common.sh ├── group_vars └── all │ └── vars.yml ├── hosts └── placeholder ├── img ├── install.png ├── logo-small.png ├── logo.png └── uninstall.png ├── install.sh ├── install.yml ├── requirements.yml ├── roles ├── install │ ├── files │ │ ├── Config │ │ │ ├── My-Cycles.csv │ │ │ ├── My-KFWeb.ini │ │ │ ├── My-KFWebAdmin.ini │ │ │ ├── My-LinuxServer-KFEngine.ini │ │ │ ├── My-LinuxServer-KFGame.ini │ │ │ ├── My-Maps.csv │ │ │ ├── My-Mutators.csv │ │ │ ├── My-Startup.conf │ │ │ └── autokick.json │ │ ├── check-log-throttling │ │ ├── firewalld-denied.conf │ │ ├── kf2-sudo │ │ ├── kf2autokick-sudo │ │ ├── kf2watchdog-sudo │ │ ├── killinuxfloor.te │ │ ├── sedeploy.sh │ │ └── serveradmin.patch │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── autokick.yml │ │ ├── config.yml │ │ ├── epel.yml │ │ ├── firewalld.yml │ │ ├── flavor.yml │ │ ├── init.yml │ │ ├── kf2-classic.yml │ │ ├── kf2-retail.yml │ │ ├── kf2.yml │ │ ├── killinuxfloor.yml │ │ ├── main.yml │ │ ├── os.yml │ │ ├── steam.yml │ │ ├── sudo.yml │ │ ├── systemd.yml │ │ └── user.yml │ ├── templates │ │ ├── kf2.service.j2 │ │ ├── kf2autokick.service.j2 │ │ ├── kf2watchdog.service.j2 │ │ ├── killinuxfloor.cil.j2 │ │ ├── yarn.list.j2 │ │ └── yarn.repo.j2 │ └── vars │ │ └── main.yml └── uninstall │ ├── handlers │ └── main.yml │ ├── tasks │ ├── autokick.yml │ ├── config.yml │ ├── firewalld.yml │ ├── kf2.yml │ ├── killinuxfloor.yml │ ├── main.yml │ ├── selinux.yml │ ├── sudo.yml │ └── systemd.yml │ └── vars │ └── main.yml ├── share ├── kf2watchdog └── killinuxfloor ├── test.sh ├── uninstall.sh ├── uninstall.yml └── update.sh /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | skip_list: 3 | - git-latest 4 | - package-latest 5 | - fqcn[action] 6 | - fqcn[action-core] 7 | - yaml[comments] # TODO how do I restrict this to just require-starting-space? 8 | - yaml[line-length] 9 | - name # this is covered by yamllint with exceptions on include_role 10 | - risky-shell-pipe 11 | - schema[tasks] # TODO ansible-lint randomly complains, e.g. it/tftpd 12 | - role-name 13 | - schema[meta] # TODO ansible-lint believes the official dependency syntax is wrong 14 | - meta-no-info # we leave platform empty 15 | - var-naming[no-role-prefix] # TODO 16 | - var-naming[no-reserved] # TODO 17 | - partial-become[task] # become is defined at the playbook level 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.yml linguist-detectable=true 2 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | yamllint --strict . 4 | ansible-lint --strict --offline 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # These are supported funding model platforms 3 | 4 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 5 | patreon: Noobient 6 | open_collective: # Replace with a single Open Collective username 7 | ko_fi: # Replace with a single Ko-fi username 8 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | liberapay: # Replace with a single Liberapay username 11 | issuehunt: # Replace with a single IssueHunt username 12 | otechie: # Replace with a single Otechie username 13 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/workflows/almalinux-8.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: AlmaLinux 8 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | - master 9 | pull_request: 10 | branches: [] 11 | 12 | jobs: 13 | ansible-ci: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Obtain sources 17 | uses: actions/checkout@v3 18 | - name: Test Galaxy role 19 | run: > 20 | CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro 21 | --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-almalinux:8) && 22 | podman exec ${CONT_ID} /bin/bash -c 23 | "./install.sh --extra-vars 'skip_kfgame=true' <<< y && 24 | ./uninstall.sh <<< y && 25 | ./install.sh --extra-vars 'skip_kfgame=true kf2_classic=true' <<< y" && 26 | podman stop ${CONT_ID} 27 | -------------------------------------------------------------------------------- /.github/workflows/almalinux-9.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: AlmaLinux 9 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | - master 9 | pull_request: 10 | branches: [] 11 | 12 | jobs: 13 | ansible-ci: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Obtain sources 17 | uses: actions/checkout@v3 18 | - name: Test Galaxy role 19 | run: > 20 | CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro 21 | --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-almalinux:9) && 22 | podman exec ${CONT_ID} /bin/bash -c 23 | "./install.sh --extra-vars 'skip_kfgame=true' <<< y && 24 | ./uninstall.sh <<< y && 25 | ./install.sh --extra-vars 'skip_kfgame=true kf2_classic=true' <<< y" && 26 | podman stop ${CONT_ID} 27 | -------------------------------------------------------------------------------- /.github/workflows/fedora-40.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Fedora 40 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | - master 9 | pull_request: 10 | branches: [] 11 | 12 | jobs: 13 | ansible-ci: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Obtain sources 17 | uses: actions/checkout@v3 18 | - name: Test Galaxy role 19 | run: > 20 | CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro 21 | --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-fedora:40) && 22 | podman exec ${CONT_ID} /bin/bash -c 23 | "./install.sh --extra-vars 'skip_kfgame=true' <<< y && 24 | ./uninstall.sh <<< y && 25 | ./install.sh --extra-vars 'skip_kfgame=true kf2_classic=true' <<< y" && 26 | podman stop ${CONT_ID} 27 | -------------------------------------------------------------------------------- /.github/workflows/fedora-41.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Fedora 41 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | - master 9 | pull_request: 10 | branches: [] 11 | 12 | jobs: 13 | ansible-ci: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Obtain sources 17 | uses: actions/checkout@v3 18 | - name: Test Galaxy role 19 | run: > 20 | CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro 21 | --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-fedora:41) && 22 | podman exec ${CONT_ID} /bin/bash -c 23 | "./install.sh --extra-vars 'skip_kfgame=true' <<< y && 24 | ./uninstall.sh <<< y && 25 | ./install.sh --extra-vars 'skip_kfgame=true kf2_classic=true' <<< y" && 26 | podman stop ${CONT_ID} 27 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | - master 9 | pull_request: 10 | branches: [] 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Obtain sources 17 | uses: actions/checkout@v3 18 | - name: Lint sources 19 | run: > 20 | podman run --rm -v ${{ github.workspace }}:/repo bviktor/ansible-systemd-lint:latest /bin/bash -c 21 | "git config --global --add safe.directory /repo && 22 | yamllint --strict . && 23 | ansible-lint --strict" 24 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu-22.04.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ubuntu 22.04 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | - master 9 | pull_request: 10 | branches: [] 11 | 12 | jobs: 13 | ansible-ci: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Obtain sources 17 | uses: actions/checkout@v3 18 | - name: Test Galaxy role 19 | run: > 20 | CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro 21 | --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-ubuntu:22.04) && 22 | podman exec ${CONT_ID} /bin/bash -c 23 | "./install.sh --extra-vars 'skip_kfgame=true' <<< y && 24 | ./uninstall.sh <<< y && 25 | ./install.sh --extra-vars 'skip_kfgame=true kf2_classic=true' <<< y" && 26 | podman stop ${CONT_ID} 27 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu-24.04.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ubuntu 24.04 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | - master 9 | pull_request: 10 | branches: [] 11 | 12 | jobs: 13 | ansible-ci: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Obtain sources 17 | uses: actions/checkout@v3 18 | - name: Test Galaxy role 19 | run: > 20 | CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro 21 | --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-ubuntu:24.04) && 22 | podman exec ${CONT_ID} /bin/bash -c 23 | "./install.sh --extra-vars 'skip_kfgame=true' <<< y && 24 | ./uninstall.sh <<< y && 25 | ./install.sh --extra-vars 'skip_kfgame=true kf2_classic=true' <<< y" && 26 | podman stop ${CONT_ID} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ansible.log 2 | hosts/*.host 3 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | braces: 6 | max-spaces-inside: 1 7 | comments: 8 | min-spaces-from-content: 1 9 | require-starting-space: false 10 | line-length: disable 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## killinuxfloor 3.0 (2023-03-07) 2 | 3 | * Add DDoS protection 4 | * Add optional DDoS stats via `klf ddos` command 5 | * Add many details to `klf workshop` report 6 | * Add Unofficial Killing Floor 2 Patch 7 | * Add `update.sh` script to update killinuxfloor installation 8 | * Fix Steam Workshop downloads 9 | * Fix KF2 restart loop when startup includes workshop items not downloaded yet 10 | * Fix various issues when installing in a container 11 | * Fix missing workshop items when trailing newline is missing in config files 12 | * Fix various commands overriding symlinks in config 13 | * Fix SELinux deny actions on startup config 14 | * Fix Ansible Galaxy dependencies being downloaded twice during install 15 | 16 | ## killinuxfloor 2.0 (2022-12-22) 17 | 18 | * Add option to install KF2 in Classic mode (Infinite Onslaught update) 19 | * Add AlmaLinux, Fedora, Ubuntu support 20 | * Add watchdog support via `killinuxfloor watchdog` 21 | * Add GeoIP and Steam profile links in webadmin player list 22 | * Add `killinuxfloor apply` command 23 | * Port installer and uninstaller to Ansible 24 | * Update Node.js to 18 25 | * Update .NET to 6 26 | * Remove CentOS support 27 | 28 | ## killinuxfloor 1.1 (2019-05-15) 29 | 30 | * Add support for bans via `killinuxfloor ban` command 31 | * Fix failing Workshop downloads caused by old Steam libraries shipped with KF2 32 | * Fix failing `killinuxfloor status` when KF2 isn't running 33 | * Fix webadmin erroneously allowing changes to automatically generated map cycles 34 | 35 | ## killinuxfloor 1.0 (2019-01-11) 36 | 37 | * Initial release 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2023 Viktor Berke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](img/logo-small.png) 2 | 3 | | Platform | Support | Status | 4 | |---|---|---| 5 | | Linter | ✅ | [![Lint](https://github.com/noobient/killinuxfloor/actions/workflows/lint.yml/badge.svg)](https://github.com/noobient/killinuxfloor/actions/workflows/lint.yml) | 6 | | AlmaLinux 8 | ✅ | [![AlmaLinux 8](https://github.com/noobient/killinuxfloor/actions/workflows/almalinux-8.yml/badge.svg)](https://github.com/noobient/killinuxfloor/actions/workflows/almalinux-8.yml) | 7 | | AlmaLinux 9 | ✅ | [![AlmaLinux 9](https://github.com/noobient/killinuxfloor/actions/workflows/almalinux-9.yml/badge.svg)](https://github.com/noobient/killinuxfloor/actions/workflows/almalinux-9.yml) | 8 | | Fedora 40 | ✅ | [![Fedora 40](https://github.com/noobient/killinuxfloor/actions/workflows/fedora-40.yml/badge.svg)](https://github.com/noobient/killinuxfloor/actions/workflows/fedora-40.yml) | 9 | | Fedora 41 | ✅ | [![Fedora 41](https://github.com/noobient/killinuxfloor/actions/workflows/fedora-41.yml/badge.svg)](https://github.com/noobient/killinuxfloor/actions/workflows/fedora-41.yml) | 10 | | Ubuntu 22.04 | ✅ | [![Ubuntu 22.04](https://github.com/noobient/killinuxfloor/actions/workflows/ubuntu-22.04.yml/badge.svg)](https://github.com/noobient/killinuxfloor/actions/workflows/ubuntu-22.04.yml) | 11 | | Ubuntu 24.04 | ✅ | [![Ubuntu 24.04](https://github.com/noobient/killinuxfloor/actions/workflows/ubuntu-24.04.yml/badge.svg)](https://github.com/noobient/killinuxfloor/actions/workflows/ubuntu-24.04.yml) | 12 | 13 | # Documentation 14 | 15 | * **[killinuxfloor – Killing Floor 2 Server on Linux](https://noobient.com/2019/01/11/killinuxfloor-killing-floor-2-server-on-linux/)** 16 | * [Playing on the Noobient Killing Floor 2 Servers](https://noobient.com/2018/08/09/playing-on-the-noobient-killing-floor-2-servers/) 17 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = false 3 | inventory = hosts/ 4 | localhost_warning = false 5 | log_path = ansible.log 6 | force_color= true 7 | -------------------------------------------------------------------------------- /common.sh: -------------------------------------------------------------------------------- 1 | function check_os () 2 | { 3 | apt --version &> /dev/null && export PKG_MGR='apt' || export PKG_MGR='dnf' 4 | } 5 | 6 | function check_sudo () 7 | { 8 | CAN_SUDO=0 9 | sudo --version > /dev/null 2>&1 && CAN_SUDO=1 10 | 11 | if [ ${CAN_SUDO} -ne 1 ] 12 | then 13 | RUNNER=$(id --user) 14 | if [ ${RUNNER} -ne 0 ] 15 | then 16 | echo "Error! You are not root and sudo is unavailable!" 17 | exit 1 18 | else 19 | "${PKG_MGR}" -y -q install sudo > /dev/null 20 | fi 21 | fi 22 | } 23 | 24 | function check_epel () 25 | { 26 | FEDORA=0 27 | grep 'ID=fedora' /etc/os-release > /dev/null && FEDORA=1 || true 28 | 29 | if [ "${FEDORA}" -ne 1 ] && [ "${PKG_MGR}" != 'apt' ] 30 | then 31 | sudo "${PKG_MGR}" -y -q install epel-release > /dev/null 32 | fi 33 | } 34 | 35 | function install_ansible () 36 | { 37 | if [ "${PKG_MGR}" == 'apt' ] 38 | then 39 | sudo "${PKG_MGR}" -y -q install software-properties-common > /dev/null 40 | sudo add-apt-repository -y ppa:ansible/ansible > /dev/null 41 | fi 42 | 43 | sudo DEBIAN_FRONTEND=noninteractive "${PKG_MGR}" -y -q install ansible > /dev/null 44 | ansible-galaxy install --force -r requirements.yml 45 | } 46 | 47 | function find_ip () 48 | { 49 | if [ "${PKG_MGR}" == 'apt' ] 50 | then 51 | IP_PKG='iproute2' 52 | else 53 | IP_PKG='iproute' 54 | fi 55 | 56 | sudo DEBIAN_FRONTEND=noninteractive "${PKG_MGR}" -y -q install "${IP_PKG}" > /dev/null 57 | 58 | export IP_ADDR=$(ip -4 a s | grep inet | grep -v '127\.0\.0' | awk '{print $2}' | cut -d'/' -f1) 59 | } 60 | 61 | function init_klf () 62 | { 63 | check_os 64 | check_sudo 65 | check_epel 66 | install_ansible 67 | find_ip 68 | } 69 | -------------------------------------------------------------------------------- /group_vars/all/vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | steam_home: /home/steam 3 | kf2_classic: false 4 | skip_kfgame: false 5 | semodule_dir: /usr/local/etc/selinux/modules 6 | -------------------------------------------------------------------------------- /hosts/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobient/killinuxfloor/cdebb297ffbcf97132eb57588d7bef76c4180672/hosts/placeholder -------------------------------------------------------------------------------- /img/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobient/killinuxfloor/cdebb297ffbcf97132eb57588d7bef76c4180672/img/install.png -------------------------------------------------------------------------------- /img/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobient/killinuxfloor/cdebb297ffbcf97132eb57588d7bef76c4180672/img/logo-small.png -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobient/killinuxfloor/cdebb297ffbcf97132eb57588d7bef76c4180672/img/logo.png -------------------------------------------------------------------------------- /img/uninstall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobient/killinuxfloor/cdebb297ffbcf97132eb57588d7bef76c4180672/img/uninstall.png -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | read -p $'This will install killinuxfloor on this machine. Press \e[36my\e[0m to continue: ' -n 1 -r 6 | echo 7 | if [[ ! $REPLY =~ ^[Yy]$ ]] 8 | then 9 | echo 'Installation cancelled.' 10 | exit 11 | fi 12 | 13 | export ROOT="${BASH_SOURCE%/*}" 14 | source "${ROOT}/common.sh" 15 | 16 | init_klf 17 | 18 | if [ $# -ge 1 ] && [ $1 == '--classic' ] 19 | then 20 | shift 21 | ansible-playbook "${ROOT}/install.yml" --extra-vars "kf2_classic=True" "$@" 22 | else 23 | ansible-playbook "${ROOT}/install.yml" "$@" 24 | fi 25 | 26 | echo -e "\e[32mkillinuxfloor successfully installed!\e[0m" 27 | echo 28 | echo -e "You may now manage your KF2 server with the \e[36msteam\e[0m user." 29 | echo -e "To switch to the steam user, use \e[36msudo -iu steam\e[0m. Switch back with \e[36mexit\e[0m." 30 | echo -e "The configuration files can be found under \e[36m/home/steam/Config\e[0m." 31 | echo "If you have previous config backed up, you may restore them now." 32 | echo 33 | echo "List of config files:" 34 | echo -e "\e[36mMy-Cycles.csv\e[0m - User defined map cycles" 35 | echo -e "\e[33mMy-KFWeb.ini\e[0m - Webadmin settings" 36 | echo -e "\e[33mMy-KFWebAdmin.ini\e[0m - Webadmin settings" 37 | echo -e "\e[33mMy-LinuxServer-KFEngine.ini\e[0m - Starting map, takeover, etc." 38 | echo -e "\e[33mMy-LinuxServer-KFGame.ini\e[0m - Password, difficulty, length, etc." 39 | echo -e "\e[36mMy-Maps.csv\e[0m - Subscribed workshop maps" 40 | echo -e "\e[36mMy-Mutators.csv\e[0m - Subscribed workshop mutators" 41 | echo -e "\e[36mMy-Startup.conf\e[0m - Mutators to load at startup" 42 | echo -e "\e[36mautokick.json\e[0m - Auto-kick bot settings" 43 | echo 44 | echo -e "Files marked in \e[33myellow\e[0m are for all the game/server options provided by Tripwire." 45 | echo "Now they contain a few cherry-picked settings, but you can add any internal option here." 46 | echo 47 | echo -e "\e[36mkillinuxfloor\e[35m Command Reference:\e[0m run \e[36mklf help\e[0m." 48 | echo -e "\e[35mKF2 Options Reference:\e[0m see the \e[36m/home/steam/Config/Internal\e[0m counterparts (but don't edit those)." 49 | echo -e "\e[35mApplying KF2 Changes:\e[0m edit the \e[36mMy-*\e[0m files, then run \e[36mklf apply\e[0m." 50 | echo -e "\e[35mApplying Auto-Kick Changes:\e[0m edit \e[36mautokick.json\e[0m, then run \e[36mklf autokick restart\e[0m." 51 | echo 52 | echo -e "\e[35mWebadmin Access:\e[0m" 53 | echo -e "* Address:\e[36m http://${IP_ADDR}:8080/\e[0m (IP auto-detected, might differ)" 54 | echo -e "* Username:\e[36m Admin\e[0m" 55 | echo -e "* Password:\e[36m YOURPASSWORDHERE\e[0m" 56 | echo 57 | echo -e "\e[31mDon't forget to change \e[36mAdminPassword\e[31m in \e[33mMy-LinuxServer-KFGame.ini\e[31m, then \e[36mklf apply\e[31m!\e[0m" 58 | -------------------------------------------------------------------------------- /install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: "{{ target | default('127.0.0.1') }}" 3 | connection: "{% if target is defined %}ssh{% else %}local{% endif %}" 4 | become: true 5 | roles: 6 | - install 7 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - src: noobient.firewalld 3 | - src: noobient.github_release 4 | - src: noobient.logrotate 5 | - src: noobient.selinux_cil 6 | - src: noobient.servicecheck 7 | - src: noobient.thirdparty 8 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-Cycles.csv: -------------------------------------------------------------------------------- 1 | # Maps on the same line will be in the same cycle. 2 | # Separate maps with a comma. 3 | # Don't add spaces before or after commas. 4 | # Lines starting with # will be ignored. 5 | # Run "killinuxfloor apply" to apply your changes. 6 | 7 | KF-HorzineArenaRMEdition,KF-KrampusLair 8 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-KFWeb.ini: -------------------------------------------------------------------------------- 1 | # Uncomment the lines that you want to have applied to your server's config. 2 | # Run "killinuxfloor apply" to apply your changes. 3 | # For an explanation on the various options, refer to: 4 | # https://wiki.tripwireinteractive.com/index.php?title=Dedicated_Server_%28Killing_Floor_2%29 5 | 6 | [IpDrv.WebServer] 7 | # This setting enables webadmin. Don't forget to set AdminPassword in My-LinuxServer-KFGame.ini, too. 8 | # Make sure you also open port 8080 on the firewall: 9 | # sudo firewall-cmd --add-port=8080/tcp --permanent 10 | # sudo firewall-cmd --reload 11 | # You have to issue the above command with your regular user, not with 'steam'. 12 | # Then you can access your webadmin via http://YOUR_IP:8080/ 13 | bEnabled=true 14 | 15 | # Setting this too low will cause webadmin to stop responding after a while. 16 | # 64 seems to work pretty well. 17 | MaxConnections=64 18 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-KFWebAdmin.ini: -------------------------------------------------------------------------------- 1 | # Uncomment the lines that you want to have applied to your server's config. 2 | # Run "killinuxfloor apply" to apply your changes. 3 | # For an explanation on the various options, refer to: 4 | # https://wiki.tripwireinteractive.com/index.php?title=Dedicated_Server_%28Killing_Floor_2%29 5 | 6 | [WebAdmin.WebAdmin] 7 | # HTTP auth is required for the auto-kick bot to work. 8 | bHttpAuth=True 9 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-LinuxServer-KFEngine.ini: -------------------------------------------------------------------------------- 1 | # Uncomment the lines that you want to have applied to your server's config. 2 | # Run "killinuxfloor apply" to apply your changes. 3 | # For an explanation on the various options, refer to: 4 | # https://wiki.tripwireinteractive.com/index.php?title=Dedicated_Server_%28Killing_Floor_2%29 5 | 6 | [URL] 7 | # Map to start KF2 server with. Ignored if you set the map in My-Startup.conf. 8 | #ServerLocalMap=KF-KrampusLair 9 | 10 | [Engine.GameEngine] 11 | # Enable or disable server takeover. 12 | bUsedForTakeover=FALSE 13 | 14 | [IpDrv.TcpNetDriver] 15 | # required for workshop downloads, you shouldn't touch this 16 | DownloadManagers=OnlineSubsystemSteamworks.SteamWorkshopDownload 17 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-LinuxServer-KFGame.ini: -------------------------------------------------------------------------------- 1 | # Uncomment the lines that you want to have applied to your server's config. 2 | # Run "killinuxfloor apply" to apply your changes. 3 | # For an explanation on the various options, refer to: 4 | # https://wiki.tripwireinteractive.com/index.php?title=Dedicated_Server_%28Killing_Floor_2%29 5 | 6 | [Engine.GameInfo] 7 | # 0 = Normal, 1 = Hard, 2 = Suicidal, 3 = HoE 8 | #GameDifficulty=2.000000 9 | 10 | [Engine.AccessControl] 11 | # Webadmin is disabled until you set AdminPassword. 12 | AdminPassword=YOURPASSWORDHERE 13 | #GamePassword=foobar 14 | 15 | [KFGame.KFGameInfo] 16 | # 0 = 4 waves, 1 = 7 waves, 2 = 10 waves 17 | #GameLength=1 18 | #ActiveMapCycle=0 19 | BannerLink=https://i.imgur.com/GoiGeCH.png 20 | ServerMOTD=Welcome to another Killing Floor 2 server proudly hosted by killinuxfloor! 21 | WebsiteLink=https://github.com/noobient/killinuxfloor 22 | #ClanMotto= 23 | #bDisableTeamCollision=True 24 | 25 | [Engine.GameReplicationInfo] 26 | ServerName=killinuxfloor KF2 Server 27 | ShortName=killinuxfloor 28 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-Maps.csv: -------------------------------------------------------------------------------- 1 | # List of maps you want to have installed. 2 | # Add entries in "id,name" format, one map per line, without quotes. 3 | # 4 | # - name = filename of the map without the .kfm extension 5 | # - id = displayed on the workshop page in the url as ?id= 6 | # 7 | # Don't add spaces before or after commas. 8 | # Lines starting with # will be ignored. 9 | # Run "killinuxfloor apply" to apply your changes. 10 | # When new maps finish downloading, the server restarts automatically once more. 11 | 12 | 838775511,KF-HorzineArenaRMEdition 13 | 1210703659,KF-KillingPool 14 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-Mutators.csv: -------------------------------------------------------------------------------- 1 | # List of mutators you want to have installed. 2 | # This won't load the mutators, just installs them. 3 | # Add entries in "id" format, one mutator per line, without quotes. 4 | # 5 | # id = displayed on the workshop page in the url as ?id= 6 | # 7 | # Don't add spaces before or after. 8 | # Lines starting with # will be ignored. 9 | # Run "killinuxfloor apply" to apply your changes. 10 | # When new mutators finish downloading, the server restarts once more automatically. 11 | 12 | # Maxplayers Mutator - replaced by UKFP 13 | #2143104493 14 | 15 | # Classic Scoreboard - deleted from workshop due to bug 16 | #1132884083 17 | 18 | # Display Damage 19 | 2625647922 20 | 21 | # UKFP 22 | 2875147606 23 | -------------------------------------------------------------------------------- /roles/install/files/Config/My-Startup.conf: -------------------------------------------------------------------------------- 1 | # This is your Killing Floor 2 startup command line. This controls what mutators 2 | # and game modes are loaded automatically during startup. You can also change 3 | # your mutators in webadmin on-the-fly via "Change Map / Additional URL Variables". 4 | # 5 | # Please note that if you're trying to load workshop items here that have not 6 | # been downloaded yet, the command line will be ignored. Otherwise the server 7 | # would just enter a restart loop as it'd keep trying to load files that are not 8 | # present. Just check download status with 'klf status workshop', and once the 9 | # downloads have finished, restart with 'klf restart'. 10 | # 11 | # Example to start with KF-KillingPool, plus enable Display Damage and UKFP mutators, finally add some UKFP options: 12 | # 13 | # KF-KillingPool?game=KFGameContent.KFGameInfo_Endless?Mutator=DamageDisplay.DmgMut,UnofficialKFPatch.UKFPMutator?MaxPlayers=32?LoadFHUD=1?LoadYAS=1?LoadAAL=1?NoEventSkins=1?NoEDARs=1?BroadcastPickups=1?CurrentWeekly=19 14 | # 15 | # For a list of UKFP options, see: 16 | # 17 | # https://steamcommunity.com/sharedfiles/filedetails/?id=2875577642 18 | # 19 | # For more examples, please refer to: 20 | # 21 | # https://wiki.killingfloor2.com/index.php?title=Dedicated_Server_(Killing_Floor_2)#Game_Modes 22 | # 23 | # This file must contain only one line that does not start with a comment (#). 24 | # If more than one line is present, the first one is used. 25 | # 26 | # IMPORTANT NOTE: due to KF2 bug, your server might become deranked when loading 27 | # mutators, even if they're whitelisted. If this happens, check if the 28 | # 29 | # /home/steam/Steam/KF2Server/Binaries/Win64/balance_tweaks.bin 30 | # 31 | # file exists. If not, grab a fresh copy from 32 | # 33 | # C:\Program Files (x86)\Steam\steamapps\common\killingfloor2\Binaries\Win64\balance_tweaks.bin 34 | # 35 | # and it should become ranked again. 36 | 37 | KF-KillingPool?game=KFGameContent.KFGameInfo_Endless 38 | -------------------------------------------------------------------------------- /roles/install/files/Config/autokick.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": ["http://127.0.0.1:8080"], 3 | "basicAuthorization": "Admin:YOURPASSWORDHERE", 4 | "intervalCheck": 5000, 5 | "action": "kick", 6 | "minLevel": 0, 7 | "warnings": true, 8 | "warningPeriod": 20000, 9 | "warningMessage": "Minimum level is 0. Please switch perk or you'll be auto-kicked in 20 seconds." 10 | } 11 | -------------------------------------------------------------------------------- /roles/install/files/check-log-throttling: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /bin/grep -m 1 'messages lost due to rate-limiting' /var/log/messages | awk -F'[()]' '{print $2}' 4 | -------------------------------------------------------------------------------- /roles/install/files/firewalld-denied.conf: -------------------------------------------------------------------------------- 1 | :msg, regex, ".*_DROP: .* DPT=7777 .*" { 2 | action(type="omfile" file="/var/log/firewalld-denied-kf2.log" fileOwner="steam" fileGroup="steam") 3 | stop 4 | } 5 | 6 | :msg, regex, ".*_REJECT: .* DPT=7777 .*" { 7 | action(type="omfile" file="/var/log/firewalld-denied-kf2.log" fileOwner="steam" fileGroup="steam") 8 | stop 9 | } 10 | 11 | :msg, contains, "_DROP: " { 12 | action(type="omfile" file="/var/log/firewalld-denied.log" fileOwner="steam" fileGroup="steam") 13 | stop 14 | } 15 | 16 | :msg, contains, "_REJECT: " { 17 | action(type="omfile" file="/var/log/firewalld-denied.log" fileOwner="steam" fileGroup="steam") 18 | stop 19 | } 20 | -------------------------------------------------------------------------------- /roles/install/files/kf2-sudo: -------------------------------------------------------------------------------- 1 | %steam ALL=NOPASSWD: /bin/systemctl start kf2.service 2 | %steam ALL=NOPASSWD: /bin/systemctl stop kf2.service 3 | %steam ALL=NOPASSWD: /bin/systemctl restart kf2.service 4 | %steam ALL=NOPASSWD: /bin/systemctl status kf2.service 5 | %steam ALL=NOPASSWD: /bin/journalctl --system --unit=kf2.service --follow 6 | %steam ALL=NOPASSWD: /bin/systemctl daemon-reload 7 | %steam ALL=NOPASSWD: /usr/bin/firewall-cmd --get-log-denied 8 | %steam ALL=NOPASSWD: /usr/bin/firewall-cmd --set-log-denied=all 9 | %steam ALL=NOPASSWD: /usr/bin/firewall-cmd --set-log-denied=off 10 | %steam ALL=NOPASSWD: /usr/local/bin/check-log-throttling 11 | -------------------------------------------------------------------------------- /roles/install/files/kf2autokick-sudo: -------------------------------------------------------------------------------- 1 | %steam ALL=NOPASSWD: /bin/systemctl start kf2autokick.service 2 | %steam ALL=NOPASSWD: /bin/systemctl stop kf2autokick.service 3 | %steam ALL=NOPASSWD: /bin/systemctl restart kf2autokick.service 4 | %steam ALL=NOPASSWD: /bin/systemctl status kf2autokick.service 5 | %steam ALL=NOPASSWD: /bin/journalctl --system --unit=kf2autokick.service --follow 6 | -------------------------------------------------------------------------------- /roles/install/files/kf2watchdog-sudo: -------------------------------------------------------------------------------- 1 | %steam ALL=NOPASSWD: /bin/systemctl start kf2watchdog.service 2 | %steam ALL=NOPASSWD: /bin/systemctl stop kf2watchdog.service 3 | %steam ALL=NOPASSWD: /bin/systemctl restart kf2watchdog.service 4 | %steam ALL=NOPASSWD: /bin/systemctl status kf2watchdog.service 5 | %steam ALL=NOPASSWD: /bin/journalctl --system --unit=kf2watchdog.service --follow 6 | %steam ALL=NOPASSWD: /bin/systemctl enable kf2watchdog.service 7 | %steam ALL=NOPASSWD: /bin/systemctl disable kf2watchdog.service 8 | -------------------------------------------------------------------------------- /roles/install/files/killinuxfloor.te: -------------------------------------------------------------------------------- 1 | module killinuxfloor 1.0; 2 | 3 | require { 4 | type init_t; 5 | type user_home_t; 6 | type http_port_t; 7 | type unreserved_port_t; 8 | class file { open read rename execute execute_no_trans map create write append lock }; 9 | class dir { create rename rmdir reparent }; 10 | class lnk_file { open read }; 11 | class tcp_socket { name_connect }; 12 | } 13 | 14 | # allow systemd to access config files in steam's home 15 | allow init_t user_home_t:file { open read rename }; 16 | 17 | # allow systemd to read links in steam's home 18 | allow init_t user_home_t:lnk_file { open read }; 19 | 20 | # allow systemd to run KFGameSteamServer in steam's home 21 | allow init_t user_home_t:file { execute execute_no_trans map }; 22 | 23 | # allow systemd to create log files in steam's home 24 | allow init_t user_home_t:file { create write append lock }; 25 | 26 | # allow systemd to manage folders in steam's home 27 | allow init_t user_home_t:dir { create rename rmdir reparent }; 28 | 29 | # allow systemd to run webadmin and kf2 server 30 | allow init_t http_port_t:tcp_socket { name_connect }; 31 | allow init_t unreserved_port_t:tcp_socket { name_connect }; 32 | -------------------------------------------------------------------------------- /roles/install/files/sedeploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | MODULE=${1} 6 | 7 | # this will create a .mod file 8 | checkmodule -M -m -o ${MODULE}.mod ${MODULE}.te 9 | 10 | # this will create a compiled semodule 11 | semodule_package -m ${MODULE}.mod -o ${MODULE}.pp 12 | 13 | # this will install the module 14 | semodule -i ${MODULE}.pp 15 | -------------------------------------------------------------------------------- /roles/install/files/serveradmin.patch: -------------------------------------------------------------------------------- 1 | diff -ruN ServerAdmin.orig/current_player_row.inc ServerAdmin/current_player_row.inc 2 | --- ServerAdmin.orig/current_player_row.inc 2022-12-22 01:43:19.509416046 +0100 3 | +++ ServerAdmin/current_player_row.inc 2022-12-22 01:43:52.990541726 +0100 4 | @@ -1,7 +1,7 @@ 5 | 6 | <%player.teamid%>  7 | <%player.name%> 8 | - <%player.perk.name%> 9 | + <%player.perk.name%>;<%player.perk.level%>;<%player.playerkey%> 10 | <%player.score%> 11 | <%player.pawn.health%> 12 | <%player.kills%> 13 | diff -ruN ServerAdmin.orig/current_players_row.inc ServerAdmin/current_players_row.inc 14 | --- ServerAdmin.orig/current_players_row.inc 2022-12-22 01:43:19.509416046 +0100 15 | +++ ServerAdmin/current_players_row.inc 2022-12-22 03:03:42.614832917 +0100 16 | @@ -2,9 +2,9 @@ 17 | <%player.teamid%>  18 | <%player.name%> 19 | <%player.ping%> 20 | - <%player.ip%> 21 | + <%player.ip%> 22 | <%player.uniqueid%> 23 | - <%player.steamid%> 24 | + <%player.steamid%> 25 | <%player.steamname%> 26 | <%player.admin%> 27 | <%player.spectator%> 28 | diff -ruN ServerAdmin.orig/current_rules.inc ServerAdmin/current_rules.inc 29 | --- ServerAdmin.orig/current_rules.inc 2022-12-22 01:43:19.509416046 +0100 30 | +++ ServerAdmin/current_rules.inc 2022-12-22 01:43:52.990541726 +0100 31 | @@ -1,3 +1,7 @@ 32 | +
Wave
33 | +
<%wave.num%>
34 | +
Difficulty
35 | +
<%rules.difficulty.text%>
36 |
Players
37 |
<%rules.numplayers%>/<%rules.maxplayers%>
38 |
Minimum to Start
39 | diff -ruN ServerAdmin.orig/default_maplist.html ServerAdmin/default_maplist.html 40 | --- ServerAdmin.orig/default_maplist.html 2022-12-22 01:43:19.509416046 +0100 41 | +++ ServerAdmin/default_maplist.html 2022-12-22 01:43:52.990541726 +0100 42 | @@ -2,6 +2,8 @@ 43 | 44 |
45 | 46 | +

Note: the first 2 map cycles are generated, so you cannot make changes to them.

47 | + 48 |
49 |
50 | Map list selection 51 | @@ -9,7 +11,7 @@ 52 | 53 | <%maplists%> 54 | 55 | - 56 | + 57 |
58 |
59 | 60 | @@ -34,8 +36,8 @@ 61 | <%maplist_editor%> 62 | 63 |
64 | - 65 | - 66 | + 67 | + 68 | 69 |
70 | 71 | @@ -53,6 +55,24 @@ 72 | $('#maplistidx').change(function(){ 73 | $('#maplistselect').submit(); 74 | }); 75 | + 76 | + /* we generate 2 fixed cycles, prohibit changes on those as they'll be overriden with the next klf config */ 77 | + document.getElementById("simpleEditBtn").hidden = true; 78 | + cycleId = "<%maplistidx%>"; 79 | + if (cycleId <= 1) 80 | + { 81 | + document.getElementById("mlactions.save").hidden = true; 82 | + document.getElementById("mlactions.delete").hidden = true; 83 | + document.getElementById("jsmlctrls").hidden = true; 84 | + document.getElementById("jsmapcycle").readOnly = true; 85 | + 86 | + /* remove the delete buttons reactively 87 | + * ugly, but this way we can avoid touching js files */ 88 | + [].forEach.call(document.querySelectorAll('.deleteButton'), function (el) 89 | + { 90 | + el.style.visibility = 'hidden'; 91 | + }); 92 | + } 93 | }); 94 | 95 | //]]> 96 | diff -ruN ServerAdmin.orig/gamesummary.inc ServerAdmin/gamesummary.inc 97 | --- ServerAdmin.orig/gamesummary.inc 2022-12-22 01:43:19.509416046 +0100 98 | +++ ServerAdmin/gamesummary.inc 2022-12-22 01:43:52.990541726 +0100 99 | @@ -4,6 +4,8 @@ 100 |
<%map.title.safe%>
101 |
Players
102 |
<%rules.numplayers%>/<%rules.maxplayers%>
103 | -
Time
104 | -
<%time.elapsed%>/<%time.timelimit%>
105 | + 107 | +
Wave <%wave.num%>
108 | +
<%wave.monsters.dead%>/<%wave.monsters.total%>
109 | 110 | \ No newline at end of file 111 | diff -ruN ServerAdmin.orig/policy_bans.html ServerAdmin/policy_bans.html 112 | --- ServerAdmin.orig/policy_bans.html 2022-12-22 01:43:19.509416046 +0100 113 | +++ ServerAdmin/policy_bans.html 2022-12-22 01:43:52.990541726 +0100 114 | @@ -2,6 +2,8 @@ 115 | 116 |
117 | 118 | +

List of the currently applied bans. Use the killinuxfloor ban command to add or remove bans. Apply your changes with killinuxfloor apply.

119 | + 120 |

<%page.title%>

121 | 122 |
123 | @@ -13,7 +15,7 @@ 124 | Steam ID 125 | Community ID 126 | 127 | - 128 | + 129 | 130 | 131 | 132 | @@ -21,7 +23,7 @@ 133 | 134 | 135 | 136 | -
137 | + 147 | 148 |
149 | 150 | diff -ruN ServerAdmin.orig/policy_bans_row.inc ServerAdmin/policy_bans_row.inc 151 | --- ServerAdmin.orig/policy_bans_row.inc 2022-12-22 01:43:19.509416046 +0100 152 | +++ ServerAdmin/policy_bans_row.inc 2022-12-22 01:43:52.990541726 +0100 153 | @@ -1,15 +1,15 @@ 154 | 155 | 156 | <%ban.uniqueid%> 157 | - <%ban.steamid%> 158 | + <%ban.steamid%> 159 | <%ban.steamname%> 160 | 161 | - 162 | + 171 | 172 | -------------------------------------------------------------------------------- /roles/install/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Reload systemd configuration 3 | systemd: 4 | daemon_reload: true 5 | when: servicecheck.systemd 6 | 7 | - name: Reload rsyslog configuration 8 | systemd: 9 | name: rsyslog.service 10 | state: restarted 11 | 12 | - name: Reload journald configuration 13 | systemd: 14 | name: systemd-journald.service 15 | state: restarted 16 | -------------------------------------------------------------------------------- /roles/install/tasks/autokick.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # TODO DNF modules used to be the way, but apparently not anymore 3 | #name: "@nodejs:{{ nodejs_version }}/common" 4 | - name: Install Node.js 5 | package: 6 | name: nodejs 7 | state: latest 8 | 9 | - include_role: 10 | name: noobient.thirdparty 11 | vars: 12 | name: Yarn 13 | gpg_url: "https://dl.yarnpkg.com/{% if ansible_os_family == 'RedHat' %}rpm{% else %}debian{% endif %}/pubkey.gpg" 14 | repo_file: yarn 15 | package: yarn 16 | 17 | - name: Obtain kf2_autokick sources 18 | become_user: steam 19 | git: 20 | repo: https://github.com/Sinewyk/kf2_autokick.git 21 | dest: "{{ steam_home }}/kf2autokick" 22 | force: true 23 | register: autokick_git 24 | 25 | - name: Build kf2_autokick # noqa no-handler no-changed-when 26 | become_user: steam 27 | shell: 28 | cmd: "cd {{ steam_home }}/kf2autokick && yarn --silent --prod --frozen-lockfile" 29 | when: autokick_git.changed 30 | -------------------------------------------------------------------------------- /roles/install/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Fedora has the EPEL packages in main 3 | - include_tasks: epel.yml 4 | when: ansible_os_family != 'Debian' and ansible_distribution != 'Fedora' 5 | 6 | - name: Install config generator 7 | package: 8 | name: crudini 9 | state: latest 10 | 11 | - name: Create Config directory 12 | file: 13 | path: "{{ steam_home }}/Config" 14 | state: directory 15 | owner: steam 16 | group: steam 17 | mode: '0755' 18 | 19 | - name: Deploy default config 20 | copy: 21 | src: "Config/{{ item }}" 22 | dest: "{{ steam_home }}/Config/{{ item }}" 23 | backup: true 24 | owner: steam 25 | group: steam 26 | mode: '0644' 27 | loop: 28 | - My-Cycles.csv 29 | - My-KFWebAdmin.ini 30 | - My-KFWeb.ini 31 | - My-LinuxServer-KFEngine.ini 32 | - My-LinuxServer-KFGame.ini 33 | - My-Maps.csv 34 | - My-Mutators.csv 35 | - My-Startup.conf 36 | - autokick.json 37 | notify: Reload systemd configuration 38 | 39 | - meta: flush_handlers 40 | 41 | - name: Make symlink to internal config 42 | file: 43 | src: "{{ steam_home }}/Steam/KF2Server/KFGame/Config" 44 | dest: "{{ steam_home }}/Config/Internal" 45 | state: link 46 | force: true 47 | follow: false 48 | owner: steam 49 | group: steam 50 | -------------------------------------------------------------------------------- /roles/install/tasks/epel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # no guarantees, but this should work on most EL distributions and releases 3 | - name: Install EPEL repo 4 | dnf: 5 | name: epel-release 6 | state: latest 7 | 8 | - name: Import EPEL GPG key 9 | rpm_key: 10 | state: present 11 | key: "/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }}" 12 | -------------------------------------------------------------------------------- /roles/install/tasks/firewalld.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install rsyslog 3 | package: 4 | name: rsyslog 5 | state: latest 6 | 7 | # On AlmaLinux 8 it's part of rsyslog, after that, it's a separate package... 8 | - name: Install log rotation for system logs 9 | package: 10 | name: rsyslog-logrotate 11 | state: latest 12 | when: ansible_os_family == 'RedHat' and ansible_distribution_major_version | int >= 9 13 | 14 | - name: Enable rsyslog 15 | systemd: 16 | name: rsyslog.service 17 | state: started 18 | enabled: true 19 | 20 | - name: Redirect logging of denied packets 21 | copy: 22 | src: firewalld-denied.conf 23 | dest: /etc/rsyslog.d/firewalld-denied.conf 24 | owner: root 25 | group: root 26 | mode: '0644' 27 | notify: Reload rsyslog configuration 28 | 29 | - name: Limit journald storage 30 | ini_file: 31 | path: /etc/systemd/journald.conf 32 | section: Journal 33 | option: SystemMaxUse 34 | value: 100M 35 | no_extra_spaces: true 36 | create: true 37 | mode: '0644' 38 | owner: root 39 | group: root 40 | backup: true 41 | notify: Reload journald configuration 42 | 43 | - include_role: 44 | name: noobient.firewalld 45 | vars: 46 | service: kf2 47 | port: "{{ item }}" 48 | rate_limit: 10/m 49 | family: ipv4 50 | loop: 51 | - 7777/udp 52 | - 20560/udp 53 | - 27015/udp 54 | - 8080/tcp 55 | 56 | - include_role: 57 | name: noobient.logrotate 58 | vars: 59 | name: firewalld-denied 60 | pattern: |- 61 | /var/log/firewalld-denied.log 62 | /var/log/firewalld-denied-kf2.log 63 | retention: 7 64 | 65 | - name: Install wrapper script for checking log throttling status 66 | copy: 67 | src: check-log-throttling 68 | dest: /usr/local/bin/check-log-throttling 69 | owner: root 70 | group: root 71 | mode: '0755' 72 | -------------------------------------------------------------------------------- /roles/install/tasks/flavor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Determine install flavor 3 | debug: 4 | msg: "Installing killinuxfloor in Classic mode" 5 | when: kf2_classic|bool 6 | 7 | - name: Determine install flavor 8 | debug: 9 | msg: "Installing killinuxfloor in Retail mode" 10 | when: not kf2_classic|bool 11 | -------------------------------------------------------------------------------- /roles/install/tasks/init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Initialize KF2 config # noqa no-changed-when 3 | become_user: steam 4 | command: 5 | cmd: "{{ steam_home }}/bin/killinuxfloor {{ item }}" 6 | loop: 7 | - reset 8 | - apply 9 | when: servicecheck.systemd and not skip_kfgame 10 | -------------------------------------------------------------------------------- /roles/install/tasks/kf2-classic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable .NET backports repo 3 | apt_repository: 4 | repo: ppa:dotnet/backports 5 | when: ansible_os_family == 'Debian' 6 | 7 | - name: Install .NET Runtime 8 | package: 9 | name: "dotnet-runtime-{{ dotnet_version }}" 10 | state: latest 11 | 12 | - include_role: 13 | name: noobient.github_release 14 | vars: 15 | owner: 'SteamRE' 16 | repo: 'DepotDownloader' 17 | prefix: 'DepotDownloader-' 18 | suffix: 'framework.zip' 19 | 20 | # the zip file needs to be versioned, otherwise version bumps won't trigger a re-download 21 | # we could checksum it to avoid that, but that's more effort and maintenance 22 | - name: Obtain DepotDownloader 23 | get_url: 24 | url: "{{ github_release.url }}" 25 | dest: "{{ steam_home }}/{{ github_release.url.split('/') | last }}" 26 | owner: steam 27 | group: steam 28 | mode: '0644' 29 | register: dd_dl 30 | 31 | - name: Remove old DepotDownloader # noqa no-handler 32 | file: 33 | path: "{{ steam_home }}/depotdownloader" 34 | state: absent 35 | when: dd_dl.changed 36 | 37 | - name: Create DepotDownloader directory 38 | file: 39 | path: "{{ steam_home }}/depotdownloader" 40 | state: directory 41 | owner: steam 42 | group: steam 43 | mode: '0755' 44 | 45 | - name: Extract DepotDownloader # noqa no-handler 46 | become_user: steam 47 | unarchive: 48 | src: "{{ steam_home }}/{{ github_release.url.split('/') | last }}" 49 | dest: "{{ steam_home }}/depotdownloader" 50 | remote_src: true 51 | when: dd_dl.changed 52 | 53 | # don't use the wrapper as it lacks the shebang so ansible errors out with an exec format error 54 | - name: Install Killing Floor 2 Classic (be patient, 16 GB+) 55 | command: 56 | cmd: "dotnet {{ steam_home }}/depotdownloader/DepotDownloader.dll -app 232130 -depot 232131 -manifest 2346945547354693824 -validate -os linux -max-downloads 16 -dir {{ steam_home }}/Steam/KF2Server" 57 | no_log: true 58 | become_user: steam 59 | register: kf2_install 60 | changed_when: '"Total downloaded: 0 bytes (0 bytes uncompressed) from 1 depots" not in kf2_install.stdout' 61 | when: not skip_kfgame|bool 62 | 63 | - name: Make symlinks for the Workshop and Cache folders 64 | file: 65 | src: "{{ item.target }}" 66 | dest: "{{ item.link }}" 67 | state: link 68 | force: true 69 | follow: false 70 | owner: steam 71 | group: steam 72 | loop: 73 | - { target: '{{ steam_home }}/Steam/KF2Server/KFGame/Cache', link: '{{ steam_home }}/Cache' } 74 | - { target: '{{ steam_home }}/Steam/KF2Server/Binaries/Win64/steamapps/workshop', link: '{{ steam_home }}/Workshop' } 75 | -------------------------------------------------------------------------------- /roles/install/tasks/kf2-retail.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # w/ validate: 3 | # Success! App '232130' fully installed. 4 | # w/o validate: 5 | # Success! App '232130' already up to date. 6 | - name: Install Killing Floor 2 Retail (be patient, 28 GB+) 7 | become_user: steam 8 | command: 9 | cmd: '{{ steam_home }}/Steam/steamcmd.sh +force_install_dir ./KF2Server +login anonymous +app_update 232130 {% if (validate is defined and validate != "no") %}validate {% endif %}+exit' 10 | register: kf2_install 11 | failed_when: ("Success! App '232130' fully installed." not in kf2_install.stdout) and ("Success! App '232130' already up to date." not in kf2_install.stdout) 12 | changed_when: ("Success! App '232130' already up to date." not in kf2_install.stdout) 13 | when: not skip_kfgame|bool 14 | 15 | # - release state: released (Subscribed,Permanent,) 16 | # - owner account: xxx 17 | # - install state: Fully Installed, 18 | # - install dir: "/home/steam/Steam/KF2Server" 19 | # - mounted depots: 20 | # 1006 (5876061545293758389) 21 | # 232131 (8628312043631154953) 22 | # - size on disk: 20917712983 bytes, BuildID 4228500 23 | # - update progress: 0/0 MB 0%, Downloaded: 20/20 MB 100% - 0 KB/s 24 | # - update state: ( No Error ) 25 | # - user config: "UserConfig" 26 | - name: Check Killing Floor 2 install state 27 | become_user: steam 28 | command: 29 | cmd: '{{ steam_home }}/Steam/steamcmd.sh +force_install_dir ./KF2Server +login anonymous +app_status 232130 +exit' 30 | changed_when: false 31 | no_log: true 32 | register: kf2_status 33 | failed_when: '"install state: Fully Installed" not in kf2_status.stdout' 34 | when: not skip_kfgame|bool 35 | 36 | - name: Make symlinks for the Workshop and Cache folders 37 | file: 38 | src: "{{ item.target }}" 39 | dest: "{{ item.link }}" 40 | state: link 41 | force: true 42 | follow: false 43 | owner: steam 44 | group: steam 45 | loop: 46 | - { target: '{{ steam_home }}/Steam/KF2Server/KFGame/Cache', link: '{{ steam_home }}/Cache' } 47 | - { target: '{{ steam_home }}/Steam/KF2Server/Binaries/Win64/steamapps/workshop', link: '{{ steam_home }}/Workshop' } 48 | -------------------------------------------------------------------------------- /roles/install/tasks/kf2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: kf2-retail.yml 3 | when: not kf2_classic|bool 4 | 5 | - include_tasks: kf2-classic.yml 6 | when: kf2_classic|bool 7 | 8 | - name: Backup webadmin files # noqa command-instead-of-module 9 | command: 10 | cmd: rsync --recursive --perms --owner --group --times --delete --force --itemize-changes . ../ServerAdmin.orig 11 | chdir: "{{ steam_home }}/Steam/KF2Server/KFGame/Web/ServerAdmin" 12 | register: webadmin_backup 13 | changed_when: webadmin_backup.stdout_lines | length 14 | when: not skip_kfgame 15 | -------------------------------------------------------------------------------- /roles/install/tasks/killinuxfloor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create Steam bin directory 3 | file: 4 | path: "{{ steam_home }}/bin" 5 | state: directory 6 | owner: steam 7 | group: steam 8 | mode: '0755' 9 | 10 | - name: Obtain killinuxfloor sources 11 | become_user: steam 12 | git: 13 | repo: https://github.com/noobient/killinuxfloor.git 14 | dest: "{{ steam_home }}/killinuxfloor" 15 | force: true 16 | 17 | - name: Enable killinuxfloor commands 18 | file: 19 | src: "{{ item.target }}" 20 | dest: "{{ steam_home }}/bin/{{ item.link }}" 21 | state: link 22 | force: true 23 | owner: steam 24 | group: steam 25 | loop: 26 | - { target: '{{ steam_home }}/killinuxfloor/share/killinuxfloor', link: 'killinuxfloor' } 27 | - { target: '{{ steam_home }}/killinuxfloor/share/kf2watchdog', link: 'kf2watchdog' } 28 | - { target: '{{ steam_home }}/bin/killinuxfloor', link: 'klf' } 29 | -------------------------------------------------------------------------------- /roles/install/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: flavor.yml 3 | - include_tasks: os.yml 4 | - include_tasks: user.yml 5 | 6 | - include_role: 7 | name: noobient.selinux_cil 8 | vars: 9 | module: killinuxfloor 10 | 11 | - include_tasks: steam.yml 12 | - include_tasks: kf2.yml 13 | - include_tasks: systemd.yml 14 | - include_tasks: sudo.yml 15 | - include_tasks: firewalld.yml 16 | - include_tasks: config.yml 17 | - include_tasks: autokick.yml 18 | - include_tasks: killinuxfloor.yml 19 | - include_tasks: init.yml 20 | -------------------------------------------------------------------------------- /roles/install/tasks/os.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check distribution flavor 3 | fail: 4 | msg: 'Sorry, killinuxfloor only supports Red Hat and Debian-based distributions. Recommended OS and release: {{ klf_dist }} {{ klf_rel }}.' 5 | when: ansible_os_family != 'RedHat' and ansible_os_family != 'Debian' 6 | 7 | # Node.js requires dnf modules 8 | # we could probably mess around to implement idempotent software collection management on EL7, but... effort 9 | - name: Check package manager 10 | fail: 11 | msg: 'Sorry, killinuxfloor only supports the DNF and APT package managers.' 12 | when: ansible_os_family != 'RedHat' and ansible_os_family != 'Debian' 13 | 14 | - name: Make sure the OS is up-to-date 15 | package: 16 | name: '*' 17 | state: latest 18 | 19 | # Here we check not only the KF2 service, but also systemd state 20 | # Used in e.g. init, config, systemd 21 | - include_role: 22 | name: noobient.servicecheck 23 | vars: 24 | unit: kf2.service 25 | 26 | # we don't need to deal with autokick, the unit stops if kf2 stops 27 | - name: Make sure the KF2 service isn't running 28 | systemd: 29 | name: kf2 30 | state: stopped 31 | when: servicecheck.started 32 | -------------------------------------------------------------------------------- /roles/install/tasks/steam.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Steam dependencies 3 | dnf: 4 | name: "{{ packages }}" 5 | vars: 6 | packages: 7 | - glibc.i686 8 | - libstdc++.i686 9 | - nss-softokn-freebl.i686 10 | - which 11 | when: ansible_os_family == 'RedHat' 12 | 13 | - name: Install Steam dependencies 14 | apt: 15 | name: "{{ packages }}" 16 | vars: 17 | packages: 18 | - lib32gcc-s1 19 | - libcurl4 20 | - curl 21 | when: ansible_os_family == 'Debian' 22 | 23 | - name: Install various tools 24 | package: 25 | name: "{{ packages }}" 26 | state: latest 27 | vars: 28 | packages: 29 | - unzip 30 | - dos2unix 31 | - patch 32 | - git 33 | - tar 34 | - "{% if ansible_os_family == 'RedHat' %}langpacks-en{% else %}language-pack-en{% endif %}" 35 | - sudo 36 | - rsync 37 | - jq 38 | - acl 39 | 40 | - name: Create main Steam directory 41 | file: 42 | path: '{{ steam_home }}/Steam' 43 | state: directory 44 | owner: steam 45 | group: steam 46 | recurse: true 47 | 48 | - name: Obtain SteamCMD 49 | get_url: 50 | url: https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz 51 | dest: '{{ steam_home }}/steamcmd_linux.tar.gz' 52 | owner: steam 53 | group: steam 54 | mode: '0644' 55 | register: steamcmd_dl 56 | 57 | - name: Extract SteamCMD 58 | # owner/group seem to be bugged, use become instead 59 | become_user: steam 60 | unarchive: 61 | src: '{{ steam_home }}/steamcmd_linux.tar.gz' 62 | dest: '{{ steam_home }}/Steam' 63 | remote_src: true 64 | # this won't work if SteamCMD is downloaded but install failed 65 | # when: steamcmd_dl.changed 66 | 67 | - name: Bootstrap SteamCMD 68 | become_user: steam 69 | command: 70 | cmd: "{{ steam_home }}/Steam/steamcmd.sh --help" 71 | no_log: true 72 | register: steamcmd_init 73 | changed_when: '"Update complete, launching" not in steamcmd_init.stdout' 74 | -------------------------------------------------------------------------------- /roles/install/tasks/sudo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable sudo roles for KF2 services 3 | copy: 4 | src: "{{ item }}" 5 | dest: "/etc/sudoers.d/{{ item }}" 6 | mode: '0440' 7 | loop: 8 | - kf2-sudo 9 | - kf2autokick-sudo 10 | - kf2watchdog-sudo 11 | -------------------------------------------------------------------------------- /roles/install/tasks/systemd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install systemd 3 | package: 4 | name: systemd 5 | state: latest 6 | 7 | - name: Install KF2 services 8 | template: 9 | src: "{{ item }}.service.j2" 10 | dest: "/etc/systemd/system/{{ item }}.service" 11 | owner: root 12 | group: root 13 | mode: '0644' 14 | loop: 15 | - kf2 16 | - kf2autokick 17 | - kf2watchdog 18 | notify: Reload systemd configuration 19 | 20 | - name: Prepare systemd folder 21 | file: 22 | path: /etc/systemd/system/kf2.service.d 23 | state: directory 24 | owner: root 25 | group: root 26 | mode: '0755' 27 | 28 | - name: Install KF2 service extra files 29 | copy: 30 | src: serveradmin.patch 31 | dest: /etc/systemd/system/kf2.service.d/serveradmin.patch 32 | owner: root 33 | group: root 34 | mode: '0644' 35 | notify: Reload systemd configuration 36 | 37 | - name: Check for legacy KF2 service link 38 | stat: 39 | path: /etc/systemd/system/kf2.service.d/kf2.service.conf 40 | register: kf2_svc_conf 41 | 42 | - name: Delete legacy KF2 service link 43 | file: 44 | path: /etc/systemd/system/kf2.service.d/kf2.service.conf 45 | state: absent 46 | when: kf2_svc_conf.stat.islnk is defined and kf2_svc_conf.stat.islnk 47 | 48 | # Idempotency 49 | - name: Create KF2 systemd config # noqa risky-file-permissions 50 | file: 51 | path: /etc/systemd/system/kf2.service.d/kf2.service.conf 52 | state: touch 53 | when: (kf2_svc_conf.stat.islnk is defined and kf2_svc_conf.stat.islnk) or (not kf2_svc_conf.stat.exists) 54 | 55 | - name: Set up KF2 systemd config 56 | file: 57 | path: /etc/systemd/system/kf2.service.d/kf2.service.conf 58 | state: file 59 | owner: steam 60 | group: steam 61 | mode: '0644' 62 | notify: Reload systemd configuration 63 | 64 | - meta: flush_handlers 65 | 66 | - name: Auto-start KF2 services on boot 67 | systemd: 68 | name: "{{ item }}" 69 | enabled: true 70 | loop: 71 | - kf2.service 72 | #- kf2autokick.service 73 | #- kf2watchdog.service 74 | -------------------------------------------------------------------------------- /roles/install/tasks/user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Bash 3 | package: 4 | name: bash 5 | state: latest 6 | 7 | - name: Create dedicated steam user 8 | user: 9 | name: steam 10 | shell: /bin/bash 11 | create_home: true 12 | -------------------------------------------------------------------------------- /roles/install/templates/kf2.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=KF2 Server 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | Type=simple 7 | User=steam 8 | Group=steam 9 | # KF2 is way too noisy and writes to both journald and messages, disable it 10 | StandardOutput=file:/home/steam/Steam/logs/kf2.txt 11 | # gotta rm first because systemd can only append or overwrite from beginning, because reasons 12 | # https://github.com/systemd/systemd/issues/8983 13 | # and the file is always owned by root:root regardless of service ownership, because reaosns 14 | # https://github.com/systemd/systemd/issues/14385 15 | #ExecStartPre=/usr/bin/rm /home/steam/Steam/logs/kf2.txt 16 | # nevermind, the + preffix runs the command with full privileges 17 | # prefer truncate, that way klf log keeps working even after a service restart 18 | ExecStartPre=+/usr/bin/truncate --size=0 /home/steam/Steam/logs/kf2.txt 19 | {% if not kf2_classic %} 20 | # make sure our patches work, wildcards are prohibited so use subshell 21 | ExecStartPre=/bin/sh -c '/bin/dos2unix /home/steam/Steam/KF2Server/KFGame/Web/ServerAdmin/*' 22 | # apply the patch, ignore if already applied 23 | ExecStartPre=-/bin/patch --forward -r /tmp/kf2-patch.rej -p1 -d /home/steam/Steam/KF2Server/KFGame/Web/ServerAdmin -i /etc/systemd/system/kf2.service.d/serveradmin.patch 24 | {% endif %} 25 | # make sure KF2 uses the Steam provided library, not the ones it bundles 26 | # update: this breaks KF2 as of 2023-02-03 27 | #ExecStartPre=/bin/ln -sTf /home/steam/Steam/linux64/steamclient.so /home/steam/Steam/KF2Server/Binaries/Win64/lib64/steamclient.so 28 | # these appear to be gone, thankfully 29 | #ExecStartPre=/bin/ln -sTf /home/steam/Steam/linux64/steamclient.so /home/steam/Steam/KF2Server/linux64/steamclient.so 30 | #ExecStartPre=/bin/ln -sTf /home/steam/Steam/linux64/steamclient.so /home/steam/Steam/KF2Server/steamclient.so 31 | # delete all bundled libs, use Steam's only, it MUST be in this specific magic dir, because reasons 32 | ExecStartPre=/bin/rm -f /home/steam/Steam/KF2Server/steamclient.so 33 | ExecStartPre=/bin/rm -f /home/steam/Steam/KF2Server/Binaries/Win64/lib64/steamclient.so 34 | ExecStartPre=/bin/rm -f /home/steam/Steam/KF2Server/linux64/steamclient.so 35 | ExecStartPre=/bin/mkdir -p /home/steam/.steam/sdk64 36 | ExecStartPre=/bin/ln -sTf /home/steam/Steam/linux64/steamclient.so /home/steam/.steam/sdk64/steamclient.so 37 | # make sure the executables are actually executable 38 | ExecStartPre=/bin/chmod +x /home/steam/Steam/KF2Server/Binaries/Win64/KFGameSteamServer.bin.x86_64 39 | ExecStartPre=/bin/chmod +x /home/steam/Steam/KF2Server/Binaries/Win64/lib64/libSDL2-2.0.so.0 40 | ExecStartPre=/bin/chmod +x /home/steam/Steam/KF2Server/Binaries/Win64/lib64/libsteam_api.so 41 | # ensure Steam is up-to-date 42 | ExecStartPre=/home/steam/Steam/steamcmd.sh --help 43 | ExecStart=/home/steam/Steam/KF2Server/Binaries/Win64/KFGameSteamServer.bin.x86_64 ${KF2_PARAMS} 44 | Restart=on-failure 45 | RestartSec=5 46 | 47 | [Install] 48 | WantedBy=multi-user.target 49 | -------------------------------------------------------------------------------- /roles/install/templates/kf2autokick.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=KF2 Auto-Kick Bot 3 | # start after KF2 started 4 | After=syslog.target network.target kf2.service 5 | # autokick won't work if KF2 isn't running 6 | Requires=kf2.service 7 | # restart and stop automatically if KF2 is restarted/stopped 8 | PartOf=kf2.service 9 | 10 | [Service] 11 | Type=simple 12 | User=steam 13 | Group=steam 14 | # This fails if the admin password is not set in KFGame.ini. But that's actually 15 | # desirable, since webadmin is disabled when no admin password is set. In which 16 | # case there's no point in starting the autokick service either. 17 | ExecStartPre=/bin/bash -c "/bin/sed -i \"s/\\\"basicAuthorization\\\".*/\\\"basicAuthorization\\\": \\\"admin:$(/bin/crudini --get /home/steam/Config/My-LinuxServer-KFGame.ini Engine.AccessControl AdminPassword | sed 's/[\/&]/\\\\&/g')\\\",/g\" /home/steam/Config/autokick.json" 18 | ExecStart=/bin/node /home/steam/kf2autokick/lib/index.js --config=/home/steam/Config/autokick.json 19 | Restart=on-failure 20 | RestartSec=5 21 | 22 | [Install] 23 | WantedBy=multi-user.target 24 | -------------------------------------------------------------------------------- /roles/install/templates/kf2watchdog.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=KF2 Watchdog 3 | # start after KF2 started 4 | After=syslog.target network.target kf2.service 5 | # watchdog won't work if KF2 isn't running 6 | Requires=kf2.service 7 | # restart and stop automatically if KF2 is restarted/stopped 8 | PartOf=kf2.service 9 | 10 | [Service] 11 | Type=simple 12 | User=steam 13 | Group=steam 14 | ExecStart=/bin/bash /home/steam/bin/kf2watchdog 15 | Restart=on-failure 16 | RestartSec=5 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /roles/install/templates/killinuxfloor.cil.j2: -------------------------------------------------------------------------------- 1 | ; allow systemd to access config files in steam's home 2 | (allow init_t user_home_t (file (open read rename))) 3 | 4 | ; allow systemd to read links in steam's home 5 | (allow init_t user_home_t (lnk_file (open read))) 6 | 7 | ; allow systemd to run KFGameSteamServer in steam's home 8 | (allow init_t user_home_t (file (execute execute_no_trans map))) 9 | 10 | ; allow systemd to create log files in steam's home 11 | (allow init_t user_home_t (file (create write append lock))) 12 | 13 | ; allow systemd to ioctl on startup config 14 | (allow init_t user_home_t (file (ioctl))) 15 | 16 | ; allow systemd to manage folders in steam's home 17 | (allow init_t user_home_t (dir (create rename rmdir reparent))) 18 | 19 | ; allow systemd to run webadmin and kf2 server 20 | (allow init_t http_port_t (tcp_socket (name_connect))) 21 | (allow init_t unreserved_port_t (tcp_socket (name_connect))) 22 | -------------------------------------------------------------------------------- /roles/install/templates/yarn.list.j2: -------------------------------------------------------------------------------- 1 | deb https://dl.yarnpkg.com/debian/ stable main 2 | -------------------------------------------------------------------------------- /roles/install/templates/yarn.repo.j2: -------------------------------------------------------------------------------- 1 | [yarn] 2 | name=Yarn Repository 3 | baseurl=https://dl.yarnpkg.com/rpm/ 4 | enabled=1 5 | gpgcheck=1 6 | gpgkey=https://dl.yarnpkg.com/rpm/pubkey.gpg 7 | -------------------------------------------------------------------------------- /roles/install/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | klf_dist: 'Fedora Server' 3 | klf_rel: '41' 4 | nodejs_version: 22 5 | dotnet_version: 9.0 6 | -------------------------------------------------------------------------------- /roles/uninstall/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Reload systemd configuration 3 | systemd: 4 | daemon_reload: true 5 | when: servicecheck.systemd 6 | 7 | - name: Reload rsyslog configuration 8 | systemd: 9 | name: rsyslog.service 10 | state: restarted 11 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/autokick.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove kf2_autokick 3 | file: 4 | path: "{{ steam_home }}/kf2autokick" 5 | state: absent 6 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove symlink to internal config 3 | file: 4 | path: "{{ steam_home }}/Config/Internal" 5 | state: absent 6 | 7 | - name: Check if any KF2 config is present 8 | stat: 9 | path: "{{ steam_home }}/Config" 10 | register: config_dir 11 | 12 | # use remove to keep it idempotent on subsequent runs 13 | - name: "Backup current KF2 config as {{ steam_home }}/Config-{{ date_str }}.tgz" 14 | archive: 15 | path: "{{ steam_home }}/Config" 16 | dest: "{{ steam_home }}/Config-{{ date_str }}.tgz" 17 | remove: true 18 | # use root to be sure, steam may or may not exist 19 | owner: root 20 | group: root 21 | mode: '0644' 22 | when: config_dir.stat.exists 23 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/firewalld.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_role: 3 | name: noobient.firewalld 4 | vars: 5 | service: 'kf2' 6 | enabled: false 7 | 8 | - name: Remove firewalld log redirect 9 | file: 10 | path: /etc/rsyslog.d/firewalld-denied.conf 11 | state: absent 12 | notify: Reload rsyslog configuration 13 | 14 | - name: Remove firewalld log rotation 15 | file: 16 | path: /etc/logrotate.d/firewalld-denied 17 | state: absent 18 | 19 | - name: Remove log throttling checker wrapper script 20 | file: 21 | path: /usr/local/bin/check-log-throttling 22 | state: absent 23 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/kf2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove Workshop and Cache symlinks 3 | file: 4 | path: "{{ item }}" 5 | state: absent 6 | loop: 7 | - "{{ steam_home }}/Cache" 8 | - "{{ steam_home }}/Workshop" 9 | 10 | - name: Remove KF2 11 | file: 12 | path: "{{ steam_home }}/{{ item }}" 13 | state: absent 14 | loop: 15 | - "Steam" 16 | - ".steam" 17 | when: not skip_kfgame|bool 18 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/killinuxfloor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove killinuxfloor 3 | file: 4 | path: "{{ steam_home }}/killinuxfloor" 5 | state: absent 6 | 7 | - name: Remove killinuxfloor commands 8 | file: 9 | path: "{{ steam_home }}/bin/{{ item }}" 10 | state: absent 11 | loop: 12 | - killinuxfloor 13 | - klf 14 | - kf2-centos 15 | - kf2watchdog 16 | 17 | - name: Remove DepotDownloader 18 | file: 19 | path: "{{ steam_home }}/depotdownloader" 20 | state: absent 21 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: systemd.yml 3 | - include_tasks: sudo.yml 4 | - include_tasks: firewalld.yml 5 | - include_tasks: autokick.yml 6 | - include_tasks: killinuxfloor.yml 7 | - include_tasks: kf2.yml 8 | - include_tasks: config.yml 9 | - include_tasks: selinux.yml 10 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/selinux.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check killinuxfloor SELinux policy module status 3 | shell: 4 | cmd: semodule --list | grep killinuxfloor 5 | failed_when: false 6 | changed_when: false 7 | register: klf_selinux 8 | 9 | - name: Disable SELinux policy module # noqa no-changed-when 10 | command: 11 | cmd: semodule --remove=killinuxfloor 12 | when: klf_selinux.rc == 0 13 | 14 | - name: Delete SELinux policy module 15 | file: 16 | path: "{{ item }}" 17 | state: absent 18 | loop: 19 | - "{{ steam_home }}/selinux" 20 | - "{{ semodule_dir }}/killinuxfloor.cil" 21 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/sudo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove sudo roles for KF2 services 3 | file: 4 | path: "/etc/sudoers.d/{{ item }}" 5 | state: absent 6 | loop: 7 | - kf2-sudo 8 | - kf2autokick-sudo 9 | - kf2watchdog-sudo 10 | -------------------------------------------------------------------------------- /roles/uninstall/tasks/systemd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_role: 3 | name: noobient.servicecheck 4 | vars: 5 | unit: kf2.service 6 | 7 | - name: Stop and disable the KF2 service 8 | systemd: 9 | name: kf2.service 10 | state: stopped 11 | enabled: false 12 | when: servicecheck.exists 13 | 14 | - include_role: 15 | name: noobient.servicecheck 16 | vars: 17 | unit: kf2autokick.service 18 | 19 | - name: Stop and disable the KF2 autokick service 20 | systemd: 21 | name: kf2autokick.service 22 | state: stopped 23 | enabled: false 24 | when: servicecheck.exists 25 | 26 | - include_role: 27 | name: noobient.servicecheck 28 | vars: 29 | unit: kf2watchdog.service 30 | 31 | - name: Stop and disable the KF2 watchdog service 32 | systemd: 33 | name: kf2watchdog.service 34 | state: stopped 35 | enabled: false 36 | when: servicecheck.exists 37 | 38 | - name: Remove KF2 services 39 | file: 40 | path: "/etc/systemd/system/{{ item }}" 41 | state: absent 42 | loop: 43 | - kf2.service 44 | - kf2autokick.service 45 | - kf2watchdog.service 46 | notify: Reload systemd configuration 47 | 48 | - name: Remove service extra files 49 | file: 50 | path: /etc/systemd/system/kf2.service.d 51 | state: absent 52 | 53 | - meta: flush_handlers 54 | -------------------------------------------------------------------------------- /roles/uninstall/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | date_str: "{{ ansible_date_time.year }}{{ ansible_date_time.month }}{{ ansible_date_time.day }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}{{ ansible_date_time.second }}" 3 | -------------------------------------------------------------------------------- /share/kf2watchdog: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | echo "$(date --rfc-3339=seconds) KF2 Watchdog started" | tee -a /home/steam/Steam/logs/kf2watchdog.txt 6 | 7 | while true 8 | do 9 | # this shouldn't be needed since the watchdog service depends on the KF2 service 10 | # but it doesn't hurt to check anyway 11 | if [ $(/bin/systemctl is-active kf2.service) ] 12 | then 13 | # give enough time for map changes and even workshop -> cache flushes 14 | RET=1 15 | curl -L -s -S --connect-timeout 120 --retry 10 --retry-connrefused --retry-delay 10 localhost:8080 > /dev/null && RET=$? || true 16 | 17 | if [ ${RET} -ne 0 ] 18 | then 19 | echo "$(date --rfc-3339=seconds) KF2 restarted" | tee -a /home/steam/Steam/logs/kf2watchdog.txt 20 | sudo /bin/systemctl restart kf2.service 21 | fi 22 | fi 23 | sleep 60 24 | done 25 | -------------------------------------------------------------------------------- /share/killinuxfloor: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -Eeu 4 | 5 | function print_help () 6 | { 7 | #echo "Usage: killinuxfloor {start|stop|restart|status|update [preview]|log|config|purge |init|help}" 8 | echo "killinuxfloor - Killing Floor 2 Linux Server Installer and Manager (c) noobient" 9 | echo "" 10 | echo "Usage:" 11 | echo "------" 12 | echo -e "\e[36mklf config\e[0m \t\t insert your own settings into the internal KF2 server config without restarting KF2" 13 | echo -e "\e[36mklf apply\e[0m \t\t apply your changes instantly, equivalent to running 'config' and 'restart'" 14 | echo -e "\e[36mklf reset\e[0m \t\t reset internal config and erase the Workshop and Cache folders (don't forget to run 'config' afterwards)" 15 | echo -e "\e[36mklf start\e[0m \t\t start KF2" 16 | echo -e "\e[36mklf stop\e[0m \t\t stop KF2" 17 | echo -e "\e[36mklf restart\e[0m \t\t restart KF2" 18 | echo -e "\e[36mklf status\e[0m \t\t query the status of the KF2 service" 19 | echo -e "\e[36mklf status service\e[0m \t same as \e[36mklf status\e[0m" 20 | if [ "${CLASSIC_MODE}" -eq 1 ] 21 | then 22 | echo -e "\e[36mklf status install\e[0m \t show KF2 installation status" 23 | fi 24 | echo -e "\e[36mklf status watchdog\e[0m \t show Watchdog status" 25 | echo -e "\e[36mklf log\e[0m \t\t display the KF2 logs" 26 | echo -e "\e[36mklf workshop\e[0m \t\t show Workshop download status" 27 | echo -e "\e[36mklf purge \e[0m \t remove an installed workshop map" 28 | if [ "${CLASSIC_MODE}" -eq 1 ] 29 | then 30 | echo -e "\e[36mklf update\e[0m \t\t check for and apply KF2 updates (don't forget to run 'init' and 'config' if update found)" 31 | echo -e "\e[36mklf update preview\e[0m \t apply updates from the 'preview' branch" 32 | fi 33 | echo -e "\e[36mklf verify\e[0m \t\t verify integrity of KF2 files" 34 | if [ "${CLASSIC_MODE}" -eq 1 ] 35 | then 36 | echo -e "\e[36mklf verify preview\e[0m \t verify integrity of KF2 files in the 'preview' branch" 37 | fi 38 | echo -e "\e[36mklf backup\e[0m \t\t back up all KF2 settings" 39 | echo -e "\e[36mklf autokick {start|stop|restart|status|log}\e[0m" 40 | echo -e "\e[36mklf ban {list|add |delete }\e[0m" 41 | echo -e "\e[36mklf watchdog {enable|disable|status}\e[0m" 42 | echo -e "\e[36mklf ddos {stats|enable|disable}\e[0m" 43 | echo -e "\e[36mklf help\e[0m \t\t print this help" 44 | } 45 | 46 | function errorexit () 47 | { 48 | case $? in 49 | 1) 50 | print_help 51 | ;; 52 | 53 | 2) 54 | echo -e "\e[31merror!\e[0m" 55 | echo -e "File \e[36m${FILE_MISSING}\e[0m not found." 56 | ;; 57 | 58 | 3) 59 | echo -e "\e[31merror!\e[0m" 60 | echo -e "Invalid SteamID64. It must be 17 characters long and must contain numbers only." 61 | echo -e "If unsure, please check on \e[36mhttps://steamidfinder.com/\e[0m" 62 | ;; 63 | 64 | 4) 65 | echo -e "\e[31merror!\e[0m" 66 | echo -e "Function not supported in Classic mode. If you didn't intend to install KF2 Classic, please reinstall." 67 | ;; 68 | 69 | esac 70 | } 71 | 72 | trap errorexit EXIT 73 | 74 | # variables 75 | LIVE_CONF="${HOME}/Steam/KF2Server/KFGame/Config" 76 | OWN_CONF="${HOME}/Config" 77 | MAP_DIR="${HOME}/Steam/KF2Server/KFGame/BrewedPC/Maps" 78 | MAP_LIST="${OWN_CONF}/My-Maps.csv" 79 | MUTATOR_LIST="${OWN_CONF}/My-Mutators.csv" 80 | CYCLE_LIST="${OWN_CONF}/My-Cycles.csv" 81 | CACHE_DIR="${HOME}/Steam/KF2Server/KFGame/Cache" 82 | WORKSHOP_DIR="${HOME}/Steam/KF2Server/Binaries/Win64/steamapps/workshop" 83 | WORKSHOP_CONTENT_DIR="${WORKSHOP_DIR}/content/232090" 84 | DEPOT_FILE="${HOME}/Steam/KF2Server/.DepotDownloader/depot.config" 85 | CONF_LIST=("autokick.json" "My-Cycles.csv" "My-KFWebAdmin.ini" "My-KFWeb.ini" "My-LinuxServer-KFEngine.ini" "My-LinuxServer-KFGame.ini" "My-Maps.csv" "My-Mutators.csv" "My-Startup.conf") 86 | STARTUP_CONF="${OWN_CONF}/My-Startup.conf" 87 | SYSTEMD_CONF='/etc/systemd/system/kf2.service.d/kf2.service.conf' 88 | DDOS_LOG='/var/log/firewalld-denied-kf2.log' 89 | 90 | ECHO_DONE='echo -e \e[32mdone\e[0m.' 91 | CYCLE_START='GameMapCycles=(Maps=("' 92 | CYCLE_END='"))' 93 | 94 | # Associative arrays must be declared at the top level: 95 | # https://stackoverflow.com/a/12944790/1708230 96 | declare -A MAPS_WORKSHOP 97 | 98 | # Colors 99 | FG_BLACK=$(tput setaf 0) 100 | FG_RED=$(tput setaf 1) 101 | FG_GREEN=$(tput setaf 2) 102 | FG_YELLOW=$(tput setaf 3) 103 | FG_BLUE=$(tput setaf 4) 104 | FG_MAGENTA=$(tput setaf 5) 105 | FG_CYAN=$(tput setaf 6) 106 | FG_WHITE=$(tput setaf 7) 107 | 108 | BG_BLACK=$(tput setab 0) 109 | BG_RED=$(tput setab 1) 110 | BG_GREEN=$(tput setab 2) 111 | BG_YELLOW=$(tput setab 3) 112 | BG_BLUE=$(tput setab 4) 113 | BG_MAGENTA=$(tput setab 5) 114 | BG_CYAN=$(tput setab 6) 115 | BG_WHITE=$(tput setab 7) 116 | 117 | COLOR_RESET='\e[0m' 118 | 119 | function check_file () 120 | { 121 | FILE_MISSING=$1 122 | test -e ${FILE_MISSING} || exit 2 123 | } 124 | 125 | function check_newline() 126 | { 127 | if [[ $(tail -c1 "${1}" | wc -l) -gt 0 ]] 128 | then 129 | echo 1 130 | else 131 | echo 0 132 | fi 133 | } 134 | 135 | function sanitize_own_config () 136 | { 137 | echo -n 'Making sure own config files are formatted properly... ' 138 | for CONF_FILE in "${CONF_LIST[@]}" 139 | do 140 | if [ $(check_newline "${OWN_CONF}/${CONF_FILE}") -eq 0 ] 141 | then 142 | echo '' >> "${OWN_CONF}/${CONF_FILE}" 143 | 144 | if [ "${CONF_FILE}" == 'My-Startup.conf' ] 145 | then 146 | sudo /bin/systemctl daemon-reload 147 | fi 148 | fi 149 | done 150 | ${ECHO_DONE} 151 | } 152 | 153 | function sanitize_internal_config () 154 | { 155 | # cleanup 156 | echo -n 'Making sure internal INI files are formatted properly... ' 157 | for INI in 'LinuxServer-KFEngine.ini' 'LinuxServer-KFGame.ini' 158 | do 159 | # delete repeated newlines 160 | cp ${LIVE_CONF}/${INI} ${LIVE_CONF}/${INI}.tmp 161 | cat -s ${LIVE_CONF}/${INI}.tmp > ${LIVE_CONF}/${INI} 162 | rm ${LIVE_CONF}/${INI}.tmp 163 | 164 | # get rid of spaces around = added by crudini 165 | sed -i 's/ = /=/' ${LIVE_CONF}/${INI} 166 | done 167 | ${ECHO_DONE} 168 | } 169 | 170 | function reset_subscriptions () 171 | { 172 | # delete old workshop section from KFEngine altogether 173 | echo -n 'Deleting old workshop subscriptions... ' 174 | check_file ${LIVE_CONF}/LinuxServer-KFEngine.ini 175 | crudini --del ${LIVE_CONF}/LinuxServer-KFEngine.ini OnlineSubsystemSteamworks.KFWorkshopSteamworks 176 | # workshop header 177 | echo '[OnlineSubsystemSteamworks.KFWorkshopSteamworks]' >> ${LIVE_CONF}/LinuxServer-KFEngine.ini 178 | ${ECHO_DONE} 179 | } 180 | 181 | function subscribe_mutators () 182 | { 183 | # parse mutator list 184 | echo -n 'Subscribing to workshop mutators... ' 185 | check_file ${MUTATOR_LIST} 186 | check_file ${LIVE_CONF}/LinuxServer-KFEngine.ini 187 | while read -r line 188 | do 189 | [[ $line = \#* ]] || [ -z "$line" ] && continue # skip comments & empty lines 190 | # add entry to workshop list, can't use crudini here because parameters aren't unique 191 | echo "ServerSubscribedWorkshopItems=${line}" >> ${LIVE_CONF}/LinuxServer-KFEngine.ini 192 | done < ${MUTATOR_LIST} 193 | ${ECHO_DONE} 194 | } 195 | 196 | function subscribe_maps () 197 | { 198 | # parse map list 199 | echo -n 'Subscribing to workshop maps... ' 200 | check_file ${MAP_LIST} 201 | check_file ${LIVE_CONF}/LinuxServer-KFEngine.ini 202 | while read -r line 203 | do 204 | [[ $line = \#* ]] || [ -z "$line" ] && continue # skip comments & empty lines 205 | ID=$(awk -F "," '{print $1}' <<< ${line}) 206 | # add entry to workshop list, can't use crudini here because parameters aren't unique 207 | echo "ServerSubscribedWorkshopItems=${ID}" >> ${LIVE_CONF}/LinuxServer-KFEngine.ini 208 | done < ${MAP_LIST} 209 | ${ECHO_DONE} 210 | } 211 | 212 | function merge_config () 213 | { 214 | # merge live ini files with own custom values 215 | # these must be unique for crudini to work 216 | # the rest will be generated by this script 217 | echo -n 'Applying settings to internal config files... ' 218 | for f in $(find -L ${OWN_CONF} -maxdepth 1 -name '*.ini' -type f -printf %f\\n) 219 | do 220 | crudini --merge "${LIVE_CONF}/${f#$"My-"}" < "${OWN_CONF}/${f}" 221 | done 222 | ${ECHO_DONE} 223 | } 224 | 225 | function delete_cycles () 226 | { 227 | # delete old cycles 228 | echo -n 'Deleting old game cycles... ' 229 | check_file ${LIVE_CONF}/LinuxServer-KFGame.ini 230 | crudini --del ${LIVE_CONF}/LinuxServer-KFGame.ini KFGame.KFGameInfo GameMapCycles 231 | ${ECHO_DONE} 232 | } 233 | 234 | function find_maps_webadmin () 235 | { 236 | MAPS_WEBADMIN=() 237 | 238 | # convert multiline variable to array 239 | mapfile -t MAPS_WEBADMIN <<< $(grep ' KFMapSummary]$' ${LIVE_CONF}/LinuxServer-KFGame.ini | awk '{ print $1 }' | tr -d '[') 240 | } 241 | 242 | function delete_maps_webadmin () 243 | { 244 | echo -n 'Deleting old workshop maps from webadmin map list... ' 245 | 246 | find_maps_stock 247 | find_maps_webadmin 248 | 249 | for map in "${MAPS_WEBADMIN[@]}" 250 | do 251 | # see if array contains variable with =~, maybe faster than manual nested loop 252 | # ensure to compare case-insensitively with ,, e.g. MonsterBall vs Monsterball 253 | # also ignore Default and Manor (dir is Manor, map is VolterManor...) 254 | if [[ ! " ${MAPS_STOCK[*],,} " =~ " ${map,,} " ]] && [ "${map}" != 'KF-Default' ] && [ "${map}" != 'KF-VolterManor' ] 255 | then 256 | crudini --del ${LIVE_CONF}/LinuxServer-KFGame.ini "${map} KFMapSummary" 257 | fi 258 | done 259 | 260 | ${ECHO_DONE} 261 | } 262 | 263 | function add_maps_webadmin () 264 | { 265 | # Check map list file 266 | echo -n 'Adding workshop maps to webadmin map list... ' 267 | check_file ${MAP_LIST} 268 | 269 | while read -r line 270 | do 271 | [[ $line = \#* ]] || [ -z "$line" ] && continue # skip comments & empty lines 272 | NAME=$(awk -F "," '{print $2}' <<< ${line}) 273 | 274 | # delete old entry from KFGame list to make sure all custom entries are at the end 275 | crudini --del ${LIVE_CONF}/LinuxServer-KFGame.ini "${NAME} KFMapSummary" 276 | 277 | # add new entry to KFGame list 278 | crudini --set ${LIVE_CONF}/LinuxServer-KFGame.ini "${NAME} KFMapSummary" "MapName" "${NAME}" 279 | done < ${MAP_LIST} 280 | ${ECHO_DONE} 281 | } 282 | 283 | function add_cycle_custom () 284 | { 285 | # parse custom cycle list 286 | echo -n 'Adding custom game cycles... ' 287 | check_file ${CYCLE_LIST} 288 | while read -r line 289 | do 290 | [[ $line = \#* ]] || [ -z "$line" ] && continue # skip comments & empty lines 291 | CYCLE="${CYCLE_START}${line}${CYCLE_END}" 292 | CYCLE=$(sed 's/,/","/g' <<< ${CYCLE}) # quotes 293 | sed -i "s/\[KFGame.KFGameInfo\]/&\n${CYCLE}/" ${LIVE_CONF}/LinuxServer-KFGame.ini 294 | done < ${CYCLE_LIST} 295 | ${ECHO_DONE} 296 | } 297 | 298 | function find_maps_workshop () 299 | { 300 | MAPS_WORKSHOP=() 301 | 302 | # Parse workshop map list 303 | check_file ${MAP_LIST} 304 | while read -r line 305 | do 306 | grep -Ei '^[0-9]+,KF-[-_a-z0-9]+$' <<< "${line}" > /dev/null || continue # skip if malformed 307 | map_id=$(awk -F "," '{print $1}' <<< ${line}) 308 | map_name=$(awk -F "," '{print $2}' <<< ${line}) 309 | MAPS_WORKSHOP+=(["${map_id}"]="${map_name}") 310 | done < ${MAP_LIST} 311 | } 312 | 313 | function find_mutators_workshop () 314 | { 315 | MUTATORS_WORKSHOP=() 316 | 317 | # Parse workshop mutator list 318 | check_file ${MUTATOR_LIST} 319 | while read -r line 320 | do 321 | grep -Ei '^[0-9]+$' <<< "${line}" > /dev/null || continue # skip if malformed 322 | MUTATORS_WORKSHOP+=("${line}") 323 | done < ${MUTATOR_LIST} 324 | } 325 | 326 | function find_maps_stock () 327 | { 328 | MAPS_STOCK=() 329 | 330 | # cycle for stock maps 331 | for d in $(find ${MAP_DIR} -maxdepth 1 -mindepth 1 -type d -printf %f\\n | grep -v 'SDK\|Templates' | sort) 332 | do 333 | MAPS_STOCK+=("KF-${d}") 334 | done 335 | } 336 | 337 | function add_cycle_str () 338 | { 339 | MAPS=("$@") 340 | # skip if map list is empty, e.g. zero workshop maps 341 | if [ ${#MAPS[@]} -eq 0 ] 342 | then 343 | return 344 | fi 345 | 346 | CYCLE="${CYCLE_START}" 347 | FIRST=1 # track if we need a comma or not 348 | 349 | for item in "${MAPS[@]}" 350 | do 351 | if [ ${FIRST} -ne 1 ] 352 | then 353 | CYCLE="${CYCLE}," 354 | fi 355 | CYCLE="${CYCLE}${item}" 356 | FIRST=0 357 | 358 | done 359 | 360 | CYCLE=$(sed 's/,/","/g' <<< ${CYCLE}) # quotes 361 | CYCLE="${CYCLE}${CYCLE_END}" # terminate 362 | 363 | sed -i "s/\[KFGame.KFGameInfo\]/&\n${CYCLE}/" ${LIVE_CONF}/LinuxServer-KFGame.ini 364 | } 365 | 366 | function add_cycles () 367 | { 368 | # the cycles added here will be shown in webadmin in reverse order due to sed 369 | 370 | add_cycle_custom 371 | 372 | echo -n 'Adding map cycle for all workshop maps... ' 373 | find_maps_workshop 374 | 375 | # Convert the associative array to list 376 | MAPS_WORKSHOP_FLAT=() 377 | for map in "${MAPS_WORKSHOP[@]}" 378 | do 379 | MAPS_WORKSHOP_FLAT+=("${map}") 380 | done 381 | 382 | add_cycle_str "${MAPS_WORKSHOP_FLAT[@]}" 383 | ${ECHO_DONE} 384 | 385 | echo -n 'Adding map cycle for all stock maps... ' 386 | find_maps_stock 387 | add_cycle_str "${MAPS_STOCK[@]}" 388 | ${ECHO_DONE} 389 | 390 | if [ ${#MAPS_WORKSHOP[@]} -gt 0 ] 391 | then 392 | echo -n 'Adding map cycle for stock and workshop maps combined... ' 393 | # bash 4 magic to sort an array, also concatenate the 2 arrays in-place while we're at it 394 | readarray -t MAPS_ALL < <(printf '%s\0' "${MAPS_STOCK[@]}" "${MAPS_WORKSHOP[@]}" | sort -z | xargs -0n1) 395 | add_cycle_str "${MAPS_ALL[@]}" 396 | ${ECHO_DONE} 397 | fi 398 | } 399 | 400 | function add_bans () 401 | { 402 | echo -n "Applying bans... " 403 | check_file ${LIVE_CONF}/LinuxServer-KFGame.ini 404 | check_file ${OWN_CONF}/My-LinuxServer-KFGame.ini 405 | 406 | # delete all previous bans 407 | sed -i '/^BannedIDs.*/d' ${LIVE_CONF}/LinuxServer-KFGame.ini 408 | 409 | # insert bans after match 410 | # sed is no good here because of line breaks 411 | # can't do in-place, so use temp file 412 | # should be eliminated once gawk 4.1.0 pushed to centos 413 | awk -v OWN_CONF="${OWN_CONF}" '{print} $0 == "[Engine.AccessControl]" {system("grep ^BannedIDs "OWN_CONF"/My-LinuxServer-KFGame.ini")}' ${LIVE_CONF}/LinuxServer-KFGame.ini > ${LIVE_CONF}/LinuxServer-KFGame.ini.tmp 414 | mv -f ${LIVE_CONF}/LinuxServer-KFGame.ini.tmp ${LIVE_CONF}/LinuxServer-KFGame.ini 415 | ${ECHO_DONE} 416 | } 417 | 418 | function add_geoip () 419 | { 420 | GEOIP_CITY=$(curl -s "https://api.geoiplookup.net/?query=$(curl -s https://ipecho.net/plain)&json=true" | jq -r '.city') # stupid mcedit syntax hilight " 421 | 422 | if [ ! -z "${GEOIP_CITY+x}" ] && [ "${#GEOIP_CITY}" -gt 0 ] 423 | then 424 | sed -i "s/^ServerName=.*/& @ ${GEOIP_CITY}/" "${LIVE_CONF}/LinuxServer-KFGame.ini" 425 | fi 426 | } 427 | 428 | function regen_config () 429 | { 430 | sanitize_own_config 431 | 432 | merge_config 433 | reset_subscriptions 434 | delete_maps_webadmin 435 | 436 | subscribe_mutators 437 | subscribe_maps 438 | add_maps_webadmin 439 | add_bans 440 | add_geoip 441 | 442 | delete_cycles 443 | add_cycles 444 | 445 | sanitize_internal_config 446 | 447 | apply_systemd_config 1 448 | 449 | echo -e "\e[32mKilling Floor 2 server configuration applied successfully!\e[0m" 450 | } 451 | 452 | function purge_map () 453 | { 454 | echo "Purging map with ID ${1}... " 455 | 456 | echo -n 'Finding map name corresponding to ID... ' 457 | check_file ${MAP_LIST} 458 | MAP_NAME=$(grep ^${1} ${MAP_LIST} | cut -d',' -f2) 459 | echo "${MAP_NAME}." 460 | 461 | echo -n 'Deleting from KFGame.ini... ' 462 | check_file ${LIVE_CONF}/LinuxServer-KFGame.ini 463 | crudini --del ${LIVE_CONF}/LinuxServer-KFGame.ini "${MAP_NAME} KFMapSummary" 464 | ${ECHO_DONE} 465 | 466 | echo -n 'Deleting from KFEngine.ini... ' 467 | check_file ${LIVE_CONF}/LinuxServer-KFEngine.ini 468 | sed -i "/ServerSubscribedWorkshopItems=${1}/d" ${LIVE_CONF}/LinuxServer-KFEngine.ini 469 | ${ECHO_DONE} 470 | 471 | echo -n 'Deleting from My-Maps.csv... ' 472 | check_file ${OWN_CONF}/My-Maps.csv 473 | sed -i --follow-symlinks "/^${1},${MAP_NAME}/d" ${OWN_CONF}/My-Maps.csv 474 | ${ECHO_DONE} 475 | 476 | echo 'Performing complete config regeneration... ' 477 | regen_config 478 | 479 | echo -n 'Deleting map files from workshop and cache... ' 480 | rm -rf "${HOME}/Cache/${1}" 481 | rm -rf "${HOME}/Workshop/content/232090/${1}" 482 | sed -i "/${1}/,+5d" "${HOME}/Workshop/appworkshop_232090.acf" 483 | ${ECHO_DONE} 484 | 485 | echo "The ${MAP_NAME} map with ID ${1} has been completely purged." 486 | } 487 | 488 | function start_kf2 () 489 | { 490 | apply_systemd_config 0 491 | sudo /bin/systemctl start kf2.service 492 | } 493 | 494 | function stop_kf2 () 495 | { 496 | sudo /bin/systemctl stop kf2.service 497 | } 498 | 499 | function reset_kf2 () 500 | { 501 | stop_kf2 502 | 503 | # nuke old config 504 | rm -f ${LIVE_CONF}/KF*.ini 505 | rm -f ${LIVE_CONF}/LinuxServer-*.ini 506 | 507 | # nuke cache 508 | if [ -d ${CACHE_DIR} ] && [ ! -z "$(ls -A ${CACHE_DIR})" ] 509 | then 510 | echo -n 'Removing Cache content... ' 511 | rm -rf ${CACHE_DIR}/* 512 | ${ECHO_DONE} 513 | fi 514 | 515 | # nuke workshop 516 | if [ -d ${WORKSHOP_DIR} ] && [ ! -z "$(ls -A ${WORKSHOP_DIR})" ] 517 | then 518 | echo -n 'Removing Workshop content... ' 519 | rm -rf ${WORKSHOP_DIR}/* 520 | ${ECHO_DONE} 521 | fi 522 | 523 | apply_systemd_config 1 524 | 525 | echo -n 'Waiting for default INI files to be generated... ' 526 | start_kf2 527 | 528 | # we need KFWeb.ini to enable webadmin, which in turn generates KFWebAdmin.ini 529 | while [ ! -f "${LIVE_CONF}/KFWeb.ini" ] 530 | do 531 | sleep 1 532 | done 533 | 534 | # temporarily enable webadmin 535 | crudini --set "${LIVE_CONF}/KFWeb.ini" IpDrv.WebServer bEnabled true 536 | 537 | stop_kf2 538 | start_kf2 539 | 540 | # make sure all other files exist as well, otherwise klf config will be useless 541 | while [ ! -f "${LIVE_CONF}/KFWebAdmin.ini" ] || [ ! -f "${LIVE_CONF}/LinuxServer-KFEngine.ini" ] || [ ! -f "${LIVE_CONF}/LinuxServer-KFGame.ini" ] 542 | do 543 | sleep 1 544 | done 545 | 546 | # restore the original value 547 | crudini --set "${LIVE_CONF}/KFWeb.ini" IpDrv.WebServer bEnabled false 548 | 549 | stop_kf2 550 | 551 | ${ECHO_DONE} 552 | 553 | echo -e "\e[32mKilling Floor 2 has been reset successfully!\e[0m" 554 | echo 555 | echo -e "You may run \e[36mklf apply\e[0m now to apply your settings again." 556 | echo -e "You can check workshop content download status with \e[36mklf workshop\e[0m." 557 | } 558 | 559 | function update_kf2 () 560 | { 561 | if [ "${CLASSIC_MODE}" -eq 1 ] 562 | then 563 | exit 4 564 | fi 565 | 566 | # hack: gotta use -beta without a branch name to force non-beta 567 | # https://forums.tripwireinteractive.com/forum/killing-floor-2/kf2-news-and-announcements/news-and-announcements-af/2321016-summer-sideshow-2018-treacherous-skies?p=2321049#post2321049 568 | case $# in 569 | 0) 570 | steamcmd.sh +force_install_dir ./KF2Server +login anonymous +app_update 232130 -beta +exit 571 | ;; 572 | 573 | 1) 574 | steamcmd.sh +force_install_dir ./KF2Server +login anonymous +app_update 232130 -beta $1 +exit 575 | ;; 576 | esac 577 | } 578 | 579 | function check_integrity () 580 | { 581 | if [ "${CLASSIC_MODE}" -eq 1 ] 582 | then 583 | dotnet "${HOME}/depotdownloader/DepotDownloader.dll" -app 232130 -depot 232131 -manifest 2346945547354693824 -validate -os linux -max-downloads 32 -dir "${HOME}/Steam/KF2Server" 584 | else 585 | case $# in 586 | 0) 587 | steamcmd.sh +force_install_dir ./KF2Server +login anonymous +app_update 232130 validate -beta +exit 588 | ;; 589 | 590 | 1) 591 | steamcmd.sh +force_install_dir ./KF2Server +login anonymous +app_update 232130 validate -beta $1 +exit 592 | ;; 593 | esac 594 | fi 595 | } 596 | 597 | function autokick () 598 | { 599 | case $1 in 600 | start) 601 | sudo /bin/systemctl start kf2autokick.service 602 | ;; 603 | 604 | stop) 605 | sudo /bin/systemctl stop kf2autokick.service 606 | ;; 607 | 608 | restart) 609 | sudo /bin/systemctl restart kf2autokick.service 610 | ;; 611 | 612 | status) 613 | sudo /bin/systemctl status kf2autokick.service || true 614 | ;; 615 | 616 | log) 617 | sudo /bin/journalctl --system --unit=kf2autokick.service --follow 618 | ;; 619 | 620 | *) 621 | exit 1 622 | 623 | esac 624 | } 625 | 626 | function check_install () 627 | { 628 | if [ "${CLASSIC_MODE}" -eq 1 ] 629 | then 630 | exit 4 631 | fi 632 | 633 | steamcmd.sh +force_install_dir ./KF2Server +login anonymous +app_status 232130 +exit 634 | } 635 | 636 | function count_lines () 637 | { 638 | RET=0 639 | 640 | while read -r line 641 | do 642 | [[ $line = \#* ]] || [ -z "$line" ] && continue # skip comments & empty lines 643 | ((RET+=1)) 644 | done < $1 645 | } 646 | 647 | # $1 = link 648 | # $2 = text 649 | # $3 = insert line break or not 650 | function echo_link () 651 | { 652 | if [ ${3} == true ] 653 | then 654 | echo -e "\e]8;;${1}\a${2}\e]8;;\a" 655 | else 656 | echo -en "\e]8;;${1}\a${2}\e]8;;\a" 657 | fi 658 | } 659 | 660 | # $1: 661 | # - 0 = print details report 662 | # - 1 = return boolean download state 663 | # 664 | # Return value: 665 | # - 0 = some items are missing 666 | # - 1 = all items are downloaded 667 | function check_workshop () 668 | { 669 | if [ "${1}" -eq 0 ] 670 | then 671 | check_file "${LIVE_CONF}/LinuxServer-KFEngine.ini" 672 | 673 | WORKSHOP_ITEMS_APPLIED=1 674 | WORKSHOP_DL_MAPS=0 675 | WORKSHOP_DL_MUTATORS=0 676 | CACHE_DL_MAPS=0 677 | CACHE_DL_MUTATORS=0 678 | 679 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 680 | echo -e "${BG_GREEN}Configured workshop maps: ${COLOR_RESET}" 681 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 682 | fi 683 | 684 | find_maps_workshop 685 | 686 | for id in "${!MAPS_WORKSHOP[@]}" 687 | do 688 | WORKSHOP_STATE='✅' 689 | CACHE_STATE='✅' 690 | ITEM_COLOR='' 691 | WORKSHOP_SIZE='0K' 692 | CACHE_SIZE='0K' 693 | 694 | if [ ! -d "${WORKSHOP_CONTENT_DIR}/${id}" ] || [ ! $(find "${WORKSHOP_CONTENT_DIR}/${id}" -name '*.kfm' -type f | head -1 | grep .) ] 695 | then 696 | if [ "${1}" -eq 1 ] 697 | then 698 | echo 0 699 | return 700 | fi 701 | 702 | WORKSHOP_STATE='❌' 703 | ITEM_COLOR="${BG_RED}" 704 | else 705 | if [ "${1}" -eq 0 ] 706 | then 707 | WORKSHOP_SIZE=$(du -sh ${WORKSHOP_CONTENT_DIR}/${id} | awk '{ print $1 }') 708 | ((WORKSHOP_DL_MAPS+=1)) 709 | fi 710 | fi 711 | 712 | if [ ! -d "${CACHE_DIR}/${id}" ] || [ ! $(find "${CACHE_DIR}/${id}" -name '*.kfm' -type f | grep .) ] 713 | then 714 | if [ "${1}" -eq 1 ] 715 | then 716 | echo 0 717 | return 718 | fi 719 | 720 | CACHE_STATE='❌' 721 | ITEM_COLOR="${BG_RED}" 722 | else 723 | if [ "${1}" -eq 0 ] 724 | then 725 | CACHE_SIZE=$(du -sh ${CACHE_DIR}/${id} | awk '{ print $1 }') 726 | ((CACHE_DL_MAPS+=1)) 727 | fi 728 | fi 729 | 730 | if [ "${1}" -eq 0 ] 731 | then 732 | # Check if item is applied to live config 733 | grep "^ServerSubscribedWorkshopItems=${id}" "${LIVE_CONF}/LinuxServer-KFEngine.ini" > /dev/null || WORKSHOP_ITEMS_APPLIED=0 734 | 735 | # Coloring 736 | echo -n "${ITEM_COLOR}" 737 | # Padding 738 | printf "%-16.16s%-32.32s" "${id}" "${MAPS_WORKSHOP[${id}]}" 739 | # Color reset, status indicators, sizes 740 | echo -en "${COLOR_RESET}" 741 | printf "%-4.4s%-4.4s%-5.5s%-5.5s" "${WORKSHOP_STATE}" "${WORKSHOP_SIZE}" " ${CACHE_STATE}" "${CACHE_SIZE} " 742 | # Steam workshop link 743 | echo_link "https://steamcommunity.com/sharedfiles/filedetails/?id=${id}" "🌐" true 744 | fi 745 | done 746 | 747 | if [ "${1}" -eq 0 ] 748 | then 749 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 750 | printf "%-48.48s%-8.8s%-8.8s" "Downloaded to Workshop & Cache:" "${WORKSHOP_DL_MAPS}/${#MAPS_WORKSHOP[@]}" "${CACHE_DL_MAPS}/${#MAPS_WORKSHOP[@]}" 751 | echo 752 | fi 753 | 754 | if [ "${1}" -eq 0 ] 755 | then 756 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 757 | echo -e "${BG_GREEN}Configured workshop mutators: ${COLOR_RESET}" 758 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 759 | fi 760 | 761 | find_mutators_workshop 762 | 763 | for mut in "${MUTATORS_WORKSHOP[@]}" 764 | do 765 | WORKSHOP_STATE='✅' 766 | CACHE_STATE='✅' 767 | ITEM_COLOR='' 768 | WORKSHOP_SIZE='0K' 769 | CACHE_SIZE='0K' 770 | 771 | if [ ! -d "${WORKSHOP_CONTENT_DIR}/${mut}" ] 772 | then 773 | if [ "${1}" -eq 1 ] 774 | then 775 | echo 0 776 | return 777 | fi 778 | 779 | WORKSHOP_STATE='❌' 780 | ITEM_COLOR="${BG_RED}" 781 | mut_file='' 782 | else 783 | mut_file=$(find "${WORKSHOP_CONTENT_DIR}/${mut}" -name '*.u' -type f -printf "%f\n" | head -1) 784 | 785 | if [ "${1}" -eq 0 ] 786 | then 787 | WORKSHOP_SIZE=$(du -sh ${WORKSHOP_CONTENT_DIR}/${mut} | awk '{ print $1 }') 788 | ((WORKSHOP_DL_MUTATORS+=1)) 789 | fi 790 | fi 791 | 792 | if [ ! $(grep . <<< ${mut_file}) ] 793 | then 794 | if [ "${1}" -eq 1 ] 795 | then 796 | echo 0 797 | return 798 | fi 799 | 800 | WORKSHOP_STATE='❌' 801 | ITEM_COLOR="${BG_RED}" 802 | fi 803 | 804 | if [ ! -d "${CACHE_DIR}/${mut}" ] || [ ! $(find "${CACHE_DIR}/${mut}" -name '*.u' -type f | head -1 | grep .) ] 805 | then 806 | if [ "${1}" -eq 1 ] 807 | then 808 | echo 0 809 | return 810 | fi 811 | 812 | CACHE_STATE='❌' 813 | ITEM_COLOR="${BG_RED}" 814 | else 815 | if [ "${1}" -eq 0 ] 816 | then 817 | CACHE_SIZE=$(du -sh ${CACHE_DIR}/${mut} | awk '{ print $1 }') 818 | ((CACHE_DL_MUTATORS+=1)) 819 | fi 820 | fi 821 | 822 | if [ "${1}" -eq 0 ] 823 | then 824 | # Check if item is applied to live config 825 | grep "^ServerSubscribedWorkshopItems=${mut}" "${LIVE_CONF}/LinuxServer-KFEngine.ini" > /dev/null || WORKSHOP_ITEMS_APPLIED=0 826 | 827 | # Coloring 828 | echo -n "${ITEM_COLOR}" 829 | 830 | # Padding 831 | printf "%-16.16s" "${mut}" 832 | 833 | if [ -z ${mut_file} ] 834 | then 835 | printf "%-32.32s" 'N/A' 836 | else 837 | printf "%-32.32s" "${mut_file%.*}" 838 | fi 839 | 840 | # Color reset, status indicators, sizes 841 | echo -en "${COLOR_RESET}" 842 | printf "%-4.4s%-4.4s%-5.5s%-5.5s" "${WORKSHOP_STATE}" "${WORKSHOP_SIZE}" " ${CACHE_STATE}" "${CACHE_SIZE} " 843 | # Steam workshop link 844 | echo_link "https://steamcommunity.com/sharedfiles/filedetails/?id=${mut}" "🌐" true 845 | fi 846 | done 847 | 848 | if [ "${1}" -eq 0 ] 849 | then 850 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 851 | printf "%-48.48s%-8.8s%-8.8s" "Downloaded to Workshop & Cache:" "${WORKSHOP_DL_MUTATORS}/${#MUTATORS_WORKSHOP[@]}" "${CACHE_DL_MUTATORS}/${#MUTATORS_WORKSHOP[@]}" 852 | echo 853 | fi 854 | 855 | #echo -e "${FG_CYAN}------------------------------------------------------${COLOR_RESET}" 856 | #echo '(Legend: ID, name, download state, cache state, web)' 857 | 858 | # Find items that are present but not subscribed to anymore 859 | if [ "${1}" -eq 0 ] && [ -d "${WORKSHOP_CONTENT_DIR}" ] 860 | then 861 | UNKNOWN_WORKSHOP=() 862 | 863 | for dl_item in $(find ${WORKSHOP_CONTENT_DIR} -mindepth 1 -maxdepth 1 -type d -printf '%f\n') 864 | do 865 | SUBBED_ITEM=0 866 | 867 | for dl_mut in "${MUTATORS_WORKSHOP[@]}" 868 | do 869 | if [ "${dl_item}" == "${dl_mut}" ] 870 | then 871 | SUBBED_ITEM=1 872 | fi 873 | done 874 | 875 | for dl_map in "${!MAPS_WORKSHOP[@]}" 876 | do 877 | if [ "${dl_item}" == "${dl_map}" ] 878 | then 879 | SUBBED_ITEM=1 880 | fi 881 | done 882 | 883 | if [ "${SUBBED_ITEM}" -eq 0 ] 884 | then 885 | UNKNOWN_WORKSHOP+=("${dl_item}") 886 | fi 887 | done 888 | 889 | 890 | if [ "${#UNKNOWN_WORKSHOP[@]}" -gt 0 ] 891 | then 892 | echo -e "${FG_RED}------------------------------------------------------------------${COLOR_RESET}" 893 | echo -e "${BG_RED}Unknown items in workshop: (purge with ${BG_CYAN}klf purge ${BG_RED}) ${COLOR_RESET}" 894 | echo -e "${FG_RED}------------------------------------------------------------------${COLOR_RESET}" 895 | 896 | for item in "${UNKNOWN_WORKSHOP[@]}" 897 | do 898 | # Padding 899 | printf "%-64.64s" "${item}" 900 | # Steam workshop link 901 | echo_link "https://steamcommunity.com/sharedfiles/filedetails/?id=${item}" "🌐" true 902 | done 903 | fi 904 | fi 905 | 906 | if [ "${1}" -eq 0 ] && [ -d "${CACHE_DIR}" ] 907 | then 908 | UNKNOWN_CACHE=() 909 | 910 | for dl_item in $(find ${CACHE_DIR} -mindepth 1 -maxdepth 1 -type d -printf '%f\n') 911 | do 912 | SUBBED_ITEM=0 913 | 914 | for dl_mut in "${MUTATORS_WORKSHOP[@]}" 915 | do 916 | if [ "${dl_item}" == "${dl_mut}" ] 917 | then 918 | SUBBED_ITEM=1 919 | fi 920 | done 921 | 922 | for dl_map in "${!MAPS_WORKSHOP[@]}" 923 | do 924 | if [ "${dl_item}" == "${dl_map}" ] 925 | then 926 | SUBBED_ITEM=1 927 | fi 928 | done 929 | 930 | if [ "${SUBBED_ITEM}" -eq 0 ] 931 | then 932 | UNKNOWN_CACHE+=("${dl_item}") 933 | fi 934 | done 935 | 936 | if [ "${#UNKNOWN_CACHE[@]}" -gt 0 ] 937 | then 938 | echo -e "${FG_RED}------------------------------------------------------------------${COLOR_RESET}" 939 | echo -e "${BG_RED}Unknown items in cache: (purge with ${BG_CYAN}klf purge ${BG_RED}) ${COLOR_RESET}" 940 | echo -e "${FG_RED}------------------------------------------------------------------${COLOR_RESET}" 941 | 942 | for item in "${UNKNOWN_CACHE[@]}" 943 | do 944 | # Padding 945 | printf "%-64.64s" "${item}" 946 | # Steam workshop link 947 | echo_link "https://steamcommunity.com/sharedfiles/filedetails/?id=${item}" "🌐" true 948 | done 949 | fi 950 | fi 951 | 952 | # Check if configured items are pushed to internal config 953 | if [ "${1}" -eq 0 ] 954 | then 955 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 956 | echo -e "${BG_GREEN}Subscription status: ${COLOR_RESET}" 957 | echo -e "${FG_GREEN}------------------------------------------------------------------${COLOR_RESET}" 958 | 959 | if [ "${WORKSHOP_ITEMS_APPLIED}" -eq 0 ] 960 | then 961 | echo -e "${BG_RED}Some subscriptions are not applied yet, run ${BG_CYAN}klf apply${BG_RED}. ${COLOR_RESET}" 962 | else 963 | echo -e "All item subscriptions are successfully applied." 964 | fi 965 | fi 966 | 967 | if [ "${1}" -eq 0 ] 968 | then 969 | echo 970 | fi 971 | 972 | if [ "${1}" -eq 1 ] 973 | then 974 | echo 1 975 | fi 976 | } 977 | 978 | function check_workshop_old () 979 | { 980 | echo -n "Checking Workshop subscriptions... " 981 | 982 | check_file ${MUTATOR_LIST} 983 | count_lines ${MUTATOR_LIST} 984 | MUT_COUNT=${RET} 985 | 986 | check_file ${MAP_LIST} 987 | count_lines ${MAP_LIST} 988 | MAP_COUNT=${RET} 989 | 990 | SUB_COUNT=0 991 | ((SUB_COUNT=MUT_COUNT+MAP_COUNT)) 992 | 993 | WORKSHOP_COUNT=0 994 | CACHE_COUNT=0 995 | 996 | if [ -d ${WORKSHOP_CONTENT_DIR} ] 997 | then 998 | WORKSHOP_COUNT=$(ls -1 ${WORKSHOP_CONTENT_DIR} | wc -l) 999 | fi 1000 | 1001 | if [ ${WORKSHOP_COUNT} -eq ${SUB_COUNT} ] 1002 | then 1003 | WORKSHOP_STR='\e[32m' 1004 | else 1005 | WORKSHOP_STR='\e[31m' 1006 | fi 1007 | 1008 | if [ -d ${CACHE_DIR} ] 1009 | then 1010 | CACHE_COUNT=$(ls -1 ${CACHE_DIR} | wc -l) 1011 | fi 1012 | 1013 | if [ ${CACHE_COUNT} -eq ${SUB_COUNT} ] 1014 | then 1015 | CACHE_STR='\e[32m' 1016 | else 1017 | CACHE_STR='\e[31m' 1018 | fi 1019 | 1020 | WORKSHOP_SPACE=$(du -sm ${WORKSHOP_DIR} | awk '{print $1}') 1021 | CACHE_SPACE=$(du -sm ${CACHE_DIR} | awk '{print $1}') 1022 | 1023 | ${ECHO_DONE} 1024 | 1025 | echo -e "Subscribed mutators: \t ${MUT_COUNT}" 1026 | echo -e "Subscribed maps: \t ${MAP_COUNT}" 1027 | echo -e "All subscribed items: \t ${SUB_COUNT}" 1028 | echo '----------------------------' 1029 | echo -e "Items in Workshop: \t ${WORKSHOP_STR}${WORKSHOP_COUNT}\e[0m (${WORKSHOP_SPACE} MB)" 1030 | echo -e "Items in Cache: \t ${CACHE_STR}${CACHE_COUNT}\e[0m (${CACHE_SPACE} MB)" 1031 | } 1032 | 1033 | function check_watchdog () 1034 | { 1035 | sudo /bin/systemctl status kf2watchdog.service || true 1036 | } 1037 | 1038 | function check_status () 1039 | { 1040 | case $1 in 1041 | service) 1042 | sudo /bin/systemctl status kf2.service || true 1043 | ;; 1044 | 1045 | install) 1046 | check_install 1047 | ;; 1048 | 1049 | watchdog) 1050 | check_watchdog 1051 | ;; 1052 | 1053 | workshop) 1054 | check_workshop 0 1055 | ;; 1056 | 1057 | *) 1058 | exit 1 1059 | esac 1060 | } 1061 | 1062 | function save_backup () 1063 | { 1064 | DATE_STR=$(date +%Y%m%d-%H%M%S) 1065 | BACKUP_FILE="${HOME}/Config-${DATE_STR}.tgz" 1066 | echo -ne "Backing up current KF2 config as \e[36m${BACKUP_FILE}\e[0m... " 1067 | tar czfh ${BACKUP_FILE} -C ${HOME} Config/*.* 1068 | ${ECHO_DONE} 1069 | } 1070 | 1071 | function steamID3_to_steamID64 () 1072 | { 1073 | # steamID64 = "7656" + (steamID3 + 1197960265728) 1074 | ID64=$1 1075 | ((ID64+=1197960265728)) 1076 | ID64="7656${ID64}" 1077 | } 1078 | 1079 | function steamID64_to_steamID3 () 1080 | { 1081 | # steamID3 = substr(steamID64, 4) - 1197960265728 1082 | ID3=${1:4} 1083 | ((ID3-=1197960265728)) 1084 | } 1085 | 1086 | function ban_applied () 1087 | { 1088 | check_file ${LIVE_CONF}/LinuxServer-KFGame.ini 1089 | RET=0 1090 | grep "^BannedIDs=(Uid=(A=${1},B=17825793))" ${LIVE_CONF}/LinuxServer-KFGame.ini > /dev/null && RET=1 || true 1091 | } 1092 | 1093 | function list_bans () 1094 | { 1095 | check_file ${OWN_CONF}/My-LinuxServer-KFGame.ini 1096 | MISSING=0 1097 | 1098 | echo -e "SteamID3\tSteamID64\t\tApplied" 1099 | echo '-----------------------------------------------' 1100 | for line in $(grep ^BannedIDs ${OWN_CONF}/My-LinuxServer-KFGame.ini) 1101 | do 1102 | # extract steamID3 from banlist entry 1103 | ID3=$(echo $line | cut -d= -f4 | cut -d, -f1) 1104 | echo -en "[U:1:${ID3}]\t" 1105 | steamID3_to_steamID64 "${ID3}" 1106 | echo -en "${ID64}\t " 1107 | ban_applied "${ID3}" 1108 | 1109 | if [ ${RET} -eq 0 ] 1110 | then 1111 | echo -e "\e[31mNo\e[0m" 1112 | MISSING=1 1113 | else 1114 | echo -e "\e[32mYes\e[0m" 1115 | fi 1116 | done 1117 | 1118 | if [ ${MISSING} -eq 1 ] 1119 | then 1120 | echo 1121 | echo -e "Some bans are still not applied. Don't forget to run \e[36mklf apply\e[0m!" 1122 | fi 1123 | } 1124 | 1125 | function check_steamID64 () 1126 | { 1127 | echo -n 'Checking if specified ID is a valid SteamID64... ' 1128 | 1129 | if [ ${#1} -ne 17 ] 1130 | then 1131 | exit 3 1132 | fi 1133 | 1134 | [[ $1 == ?(-)+([0-9]) ]] || exit 3 1135 | 1136 | ${ECHO_DONE} 1137 | } 1138 | 1139 | function add_ban () 1140 | { 1141 | check_steamID64 $1 1142 | echo -en "Adding \e[36m$1\e[0m to ban list... " 1143 | 1144 | check_file ${OWN_CONF}/My-LinuxServer-KFGame.ini 1145 | steamID64_to_steamID3 $1 1146 | BAN_STR="BannedIDs=(Uid=(A=${ID3},B=17825793))" 1147 | 1148 | # simple way to skip if already added 1149 | grep ^${BAN_STR} ${OWN_CONF}/My-LinuxServer-KFGame.ini > /dev/null || sed -i --follow-symlinks "s/\[Engine.AccessControl\]/&\n${BAN_STR}/" ${OWN_CONF}/My-LinuxServer-KFGame.ini 1150 | 1151 | ${ECHO_DONE} 1152 | echo 'New list of banned users:' 1153 | echo 1154 | list_bans 1155 | } 1156 | 1157 | function delete_ban () 1158 | { 1159 | check_steamID64 $1 1160 | echo -en "Removing \e[36m$1\e[0m from ban list... " 1161 | 1162 | check_file ${OWN_CONF}/My-LinuxServer-KFGame.ini 1163 | steamID64_to_steamID3 $1 1164 | BAN_STR="BannedIDs=(Uid=(A=${ID3},B=17825793))" 1165 | sed -i "/^${BAN_STR}/d" ${OWN_CONF}/My-LinuxServer-KFGame.ini 1166 | 1167 | ${ECHO_DONE} 1168 | echo 'New list of banned users:' 1169 | echo 1170 | list_bans 1171 | 1172 | # list_bans() can't detect removal, always print this 1173 | echo 1174 | echo -e "To apply the removal, don't forget to run \e[36mklf apply\e[0m!" 1175 | } 1176 | 1177 | function handle_bans () 1178 | { 1179 | case $1 in 1180 | list) 1181 | list_bans 1182 | ;; 1183 | 1184 | add) 1185 | if [ $# -ne 2 ] 1186 | then 1187 | exit 1 1188 | fi 1189 | add_ban $2 1190 | ;; 1191 | 1192 | del|delete) 1193 | if [ $# -ne 2 ] 1194 | then 1195 | exit 1 1196 | fi 1197 | delete_ban $2 1198 | ;; 1199 | 1200 | *) 1201 | exit 1 1202 | esac 1203 | } 1204 | 1205 | function handle_watchdog () 1206 | { 1207 | case $1 in 1208 | enable) 1209 | sudo /bin/systemctl enable kf2watchdog.service 1210 | sudo /bin/systemctl restart kf2watchdog.service 1211 | ;; 1212 | 1213 | disable) 1214 | sudo /bin/systemctl stop kf2watchdog.service 1215 | sudo /bin/systemctl disable kf2watchdog.service 1216 | ;; 1217 | 1218 | status) 1219 | check_watchdog 1220 | ;; 1221 | 1222 | *) 1223 | exit 1 1224 | esac 1225 | } 1226 | 1227 | function get_startup_args () 1228 | { 1229 | check_file "${STARTUP_CONF}" 1230 | 1231 | # Pattern is KF- and then at least one alphanumeric character. 1232 | # Gotta do these quirks to ensure there's only one line fetched. 1233 | RET=0 1234 | grep -E '^KF-[[:alnum:]]+.*$' "${STARTUP_CONF}" > /dev/null || RET=1 1235 | 1236 | if [ "${RET}" -eq 0 ] 1237 | then 1238 | grep -E '^KF-[[:alnum:]]+.*$' "${STARTUP_CONF}" | head -1 1239 | else 1240 | echo 0 1241 | fi 1242 | } 1243 | 1244 | function find_startup_map () 1245 | { 1246 | STARTUP_ARGS=$(get_startup_args) 1247 | 1248 | if [ "$STARTUP_ARGS}" == '0' ] 1249 | then 1250 | echo 0 1251 | else 1252 | echo $(echo ${STARTUP_ARGS} | cut -d'?' -f1) 1253 | fi 1254 | } 1255 | 1256 | function startup_map_is_stock () 1257 | { 1258 | # If no params are set, it can only be a stock map, most likely Biotics Lab 1259 | STARTUP_MAP=$(find_startup_map) 1260 | 1261 | if [ "${STARTUP_MAP}" == '0' ] 1262 | then 1263 | echo 1 1264 | return 1265 | fi 1266 | 1267 | find_maps_stock 1268 | 1269 | for map in "${MAPS_STOCK[@]}" 1270 | do 1271 | if [ "${map}" == "${STARTUP_MAP}" ] 1272 | then 1273 | echo 1 1274 | return 1275 | fi 1276 | done 1277 | 1278 | echo 0 1279 | } 1280 | 1281 | function startup_mutators_exist () 1282 | { 1283 | STARTUP_ARGS=$(get_startup_args) 1284 | 1285 | if [ "$STARTUP_ARGS}" == '0' ] 1286 | then 1287 | echo 0 1288 | return 1289 | fi 1290 | 1291 | echo "${STARTUP_ARGS}" | grep -E '^KF-[[:alnum:]]+.*\?[mM]utator=.+$' > /dev/null && echo 1 || echo 0 1292 | } 1293 | 1294 | function gen_systemd_config () 1295 | { 1296 | echo '[Service]' 1297 | 1298 | # Custom startup map is used but workshop downloads are incomplete 1299 | # OR custom startup mutators are used but workshop downloads are incomplete 1300 | # OR startup args are missing or malformed 1301 | if ([ $(startup_map_is_stock) -eq 0 ] && [ $(check_workshop 1) -eq 0 ]) || \ 1302 | ([ $(startup_mutators_exist) -eq 1 ] && [ $(check_workshop 1) -eq 0 ]) || \ 1303 | [ $(get_startup_args) == '0' ] 1304 | then 1305 | echo 'Environment="KF2_PARAMS="' 1306 | else 1307 | echo "Environment=\"KF2_PARAMS=$(get_startup_args)\"" 1308 | fi 1309 | } 1310 | 1311 | function apply_systemd_config () 1312 | { 1313 | if [ "${1}" -eq 1 ] 1314 | then 1315 | echo -n "Applying startup command line... " 1316 | fi 1317 | 1318 | gen_systemd_config > "${SYSTEMD_CONF}" 1319 | sudo /bin/systemctl daemon-reload 1320 | 1321 | if [ "${1}" -eq 1 ] 1322 | then 1323 | ${ECHO_DONE} 1324 | fi 1325 | } 1326 | 1327 | function get_os_id () 1328 | { 1329 | grep '^ID=' /etc/os-release | awk -F'=' '{ print $2}' | tr -d '"' 1330 | } 1331 | 1332 | function check_log_throttling () 1333 | { 1334 | RET=0 1335 | which check-log-throttling &> /dev/null && RET=1 || true 1336 | if [ ${RET} -ne 1 ] 1337 | then 1338 | return 1339 | fi 1340 | 1341 | OS_ID=$(get_os_id) 1342 | 1343 | case "${OS_ID}" in 1344 | fedora|almalinux) 1345 | : 1346 | ;; 1347 | 1348 | *) 1349 | return 1350 | ;; 1351 | esac 1352 | 1353 | printf "%-18.18s" "Log throttled:" 1354 | 1355 | LOG_LIMIT=$(sudo /usr/local/bin/check-log-throttling) 1356 | 1357 | if [ "${LOG_LIMIT}" != '' ] 1358 | then 1359 | echo -e "${FG_RED}yes${COLOR_RESET}" 1360 | printf "%-18.18s" "Log limits:" 1361 | echo -e "${LOG_LIMIT}" 1362 | else 1363 | echo -e "${FG_GREEN}no" 1364 | fi 1365 | 1366 | echo -en "${COLOR_RESET}" 1367 | } 1368 | 1369 | function get_ddos_stats () 1370 | { 1371 | if [ ! -f "${DDOS_LOG}" ] 1372 | then 1373 | ATTACK_COUNT=0 1374 | ATTACKER_COUNT=0 1375 | LOG_SIZE='0' 1376 | else 1377 | ATTACK_COUNT=$(cat ${DDOS_LOG} | wc -l) 1378 | ATTACKER_COUNT=$(cat ${DDOS_LOG} | awk '{ print $10 }' | sort | uniq | wc -l) 1379 | LOG_SIZE=$(du -h ${DDOS_LOG} | awk '{ print $1 }') 1380 | fi 1381 | 1382 | echo -e "${FG_GREEN}-----------------------------------------------------${COLOR_RESET}" 1383 | echo -e "${BG_GREEN}Today's DDoS stats: ${COLOR_RESET}" 1384 | echo -e "${FG_GREEN}-----------------------------------------------------${COLOR_RESET}" 1385 | 1386 | printf "%-18.18s" "Denied packets:" 1387 | if [ ${ATTACK_COUNT} -eq 0 ] 1388 | then 1389 | echo -n "${FG_GREEN}" 1390 | else 1391 | echo -n "${FG_RED}" 1392 | fi 1393 | printf "%'d" "${ATTACK_COUNT}" 1394 | echo -e "${COLOR_RESET}" 1395 | 1396 | printf "%-18.18s" "Unique IPs:" 1397 | if [ ${ATTACKER_COUNT} -eq 0 ] 1398 | then 1399 | echo -n "${FG_GREEN}" 1400 | else 1401 | echo -n "${FG_RED}" 1402 | fi 1403 | printf "%'d" "${ATTACKER_COUNT}" 1404 | echo -e "${COLOR_RESET}" 1405 | 1406 | printf "%-18.18s" "Log size:" 1407 | if [ ${LOG_SIZE} == '0' ] 1408 | then 1409 | echo -n "${FG_GREEN}" 1410 | else 1411 | echo -n "${FG_RED}" 1412 | fi 1413 | echo -e "${LOG_SIZE}${COLOR_RESET}" 1414 | 1415 | check_log_throttling 1416 | 1417 | echo 1418 | } 1419 | 1420 | function handle_ddos () 1421 | { 1422 | case $1 in 1423 | stats) 1424 | FW_LOG=$(sudo /usr/bin/firewall-cmd --get-log-denied) 1425 | 1426 | if [ "${FW_LOG}" != 'all' ] 1427 | then 1428 | echo -e "${FG_RED}DDoS logging is disabled.${COLOR_RESET} Use ${FG_CYAN}klf ddos enable${COLOR_RESET} to enable it.\n" 1429 | 1430 | echo -e "Note: ${FG_GREEN}this option does not affect DDoS protection${COLOR_RESET}, this only switches the" 1431 | echo -e "logging of denied packets on and off.\n" 1432 | 1433 | echo -e "${FG_RED}Warning:${COLOR_RESET} depending on the number of attacks, this may use a considerable amount" 1434 | echo -e "of disk space. Logs are automatically rotated daily, so olders logs will be" 1435 | echo -e "compressed and eventually discarded, but in extreme cases, ${FG_RED}the current day's log" 1436 | echo -e "may consume several GBs${COLOR_RESET}.\n" 1437 | else 1438 | get_ddos_stats 1439 | fi 1440 | ;; 1441 | 1442 | enable) 1443 | sudo /usr/bin/firewall-cmd --set-log-denied=all 1444 | ;; 1445 | 1446 | disable) 1447 | sudo /usr/bin/firewall-cmd --set-log-denied=off 1448 | ;; 1449 | 1450 | *) 1451 | exit 1 1452 | esac 1453 | } 1454 | 1455 | # determine if classic install or current 1456 | CLASSIC_MODE=0 1457 | if [ -f "${DEPOT_FILE}" ] 1458 | then 1459 | CLASSIC_MODE=1 1460 | fi 1461 | 1462 | # check argument count 1463 | if [ "$#" -lt 1 ] || [ "$#" -gt 3 ] 1464 | then 1465 | exit 1 1466 | fi 1467 | 1468 | # do away with 'changed on disk' warnings once and for all 1469 | # TODO this shouldn't be needed since the startup config change? 1470 | #sudo /bin/systemctl daemon-reload 1471 | 1472 | # Add steamcmd.sh to PATH - symlinking won't work, it's trying to be too smart. 1473 | PATH="${PATH}:${HOME}/Steam" 1474 | 1475 | # main() 1476 | case $1 in 1477 | start) 1478 | start_kf2 1479 | ;; 1480 | 1481 | stop) 1482 | stop_kf2 1483 | ;; 1484 | 1485 | restart) 1486 | apply_systemd_config 0 1487 | sudo /bin/systemctl restart kf2.service 1488 | ;; 1489 | 1490 | status) 1491 | if [ $# -eq 2 ] 1492 | then 1493 | check_status $2 1494 | else 1495 | check_status service 1496 | fi 1497 | ;; 1498 | 1499 | update) 1500 | if [ $# -eq 2 ] 1501 | then 1502 | update_kf2 $2 1503 | else 1504 | update_kf2 1505 | fi 1506 | ;; 1507 | 1508 | log) 1509 | #sudo /bin/journalctl --system --unit=kf2.service --follow 1510 | tail -f /home/steam/Steam/logs/kf2.txt 1511 | ;; 1512 | 1513 | config) 1514 | regen_config 1515 | ;; 1516 | 1517 | purge) 1518 | purge_map $2 1519 | ;; 1520 | 1521 | reset) 1522 | reset_kf2 1523 | ;; 1524 | 1525 | verify) 1526 | if [ $# -eq 2 ] 1527 | then 1528 | check_integrity $2 1529 | else 1530 | check_integrity 1531 | fi 1532 | ;; 1533 | 1534 | backup) 1535 | save_backup 1536 | ;; 1537 | 1538 | autokick) 1539 | autokick $2 1540 | ;; 1541 | 1542 | ban) 1543 | if [ $# -eq 1 ] 1544 | then 1545 | handle_bans list 1546 | else 1547 | handle_bans "${@:2}" 1548 | fi 1549 | ;; 1550 | 1551 | apply) 1552 | regen_config 1553 | sudo /bin/systemctl restart kf2.service 1554 | ;; 1555 | 1556 | watchdog) 1557 | handle_watchdog $2 1558 | ;; 1559 | 1560 | ddos) 1561 | if [ $# -eq 1 ] 1562 | then 1563 | handle_ddos stats 1564 | else 1565 | handle_ddos "${@:2}" 1566 | fi 1567 | ;; 1568 | 1569 | workshop) 1570 | check_status workshop 1571 | ;; 1572 | 1573 | help) 1574 | print_help 1575 | ;; 1576 | 1577 | *) 1578 | exit 1 1579 | 1580 | esac 1581 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | function check_container () 6 | { 7 | "${exec}" exec -it "${CONT_ID}" bash 8 | } 9 | 10 | trap 'check_container' ERR 11 | 12 | function find_exec () 13 | { 14 | RET=0 15 | podman --version &> /dev/null && RET=1 || true 16 | 17 | if [ ${RET} -eq 1 ] 18 | then 19 | echo 'podman' 20 | else 21 | RET=0 22 | docker --version &> /dev/null && RET=1 || true 23 | 24 | if [ ${RET} -eq 1 ] 25 | then 26 | echo 'docker' 27 | else 28 | echo '' 29 | fi 30 | fi 31 | } 32 | 33 | export exec=$(find_exec) 34 | 35 | if [ -z "${exec}" ] 36 | then 37 | echo -e "\e[31mError! Container executable not found. Ensure Podman (preferred) or Docker is installed and running.\e[0m" 38 | exit 1 39 | fi 40 | 41 | if [ $# -lt 1 ] 42 | then 43 | platform="fedora:41" 44 | else 45 | platform="${1}" 46 | fi 47 | 48 | echo -e "\nRunning tests on:\n - Platform: \e[36m${platform}\e[0m\n" 49 | 50 | # Fire up instance 51 | CONT_ID=$(${exec} run --rm -v $(pwd):/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro --tmpfs /tmp --tmpfs /run --privileged --detach "bviktor/ansible-systemd-${platform}") 52 | # Determine package manager 53 | export PKG_MGR=$(${exec} exec ${CONT_ID} /bin/bash -c "apt --version &> /dev/null && echo 'apt' || echo 'dnf'") 54 | # Run the tests 55 | "${exec}" exec ${CONT_ID} /bin/bash -c "./install.sh --extra-vars 'skip_kfgame=true' <<< y" 56 | "${exec}" exec ${CONT_ID} /bin/bash -c "DEBIAN_FRONTEND=noninteractive ${PKG_MGR} -y -q install firewalld > /dev/null && systemctl start firewalld.service" 57 | "${exec}" exec ${CONT_ID} /bin/bash -c "./uninstall.sh <<< y" 58 | "${exec}" exec ${CONT_ID} /bin/bash -c "./install.sh --extra-vars 'skip_kfgame=true kf2_classic=true' <<< y" 59 | # Let us check stuff before exiting 60 | check_container 61 | # Stop instance 62 | "${exec}" stop "${CONT_ID}" 63 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | read -p $'This will uninstall killinuxfloor from this machine. Press \e[36my\e[0m to continue: ' -n 1 -r 6 | echo 7 | if [[ ! $REPLY =~ ^[Yy]$ ]] 8 | then 9 | echo 'Uninstallation cancelled.' 10 | exit 11 | fi 12 | 13 | export ROOT="${BASH_SOURCE%/*}" 14 | source "${ROOT}/common.sh" 15 | 16 | init_klf 17 | 18 | ansible-playbook "${ROOT}/uninstall.yml" "$@" 19 | 20 | echo -e "\e[32mkillinuxfloor successfully uninstalled.\e[0m" 21 | -------------------------------------------------------------------------------- /uninstall.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: "{{ target | default('127.0.0.1') }}" 3 | connection: "{% if target is defined %}ssh{% else %}local{% endif %}" 4 | become: true 5 | roles: 6 | - uninstall 7 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./install.sh --extra-vars "skip_kfgame=True" 4 | --------------------------------------------------------------------------------