├── .envrc ├── .github └── workflows │ └── docker.yaml ├── .gitignore ├── Dockerfile ├── ExtendHumiliation.smx ├── ExtendHumiliation.sp ├── LICENSE ├── README.md ├── configs.sh ├── curl.ext.so ├── flake.lock ├── flake.nix ├── maps.sh ├── pause.smx ├── plugins.sh ├── soap-dm.zip ├── socket_3.0.1.zip └── sourcemod.sh /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: docker-build 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | - 'master' 8 | repository_dispatch: 9 | types: [ build ] 10 | 11 | jobs: 12 | docker: 13 | runs-on: ubuntu-22.04 14 | steps: 15 | - name: Set up Docker Buildx 16 | uses: docker/setup-buildx-action@v2 17 | - name: Login to DockerHub 18 | uses: docker/login-action@v2 19 | with: 20 | username: ${{ secrets.DOCKERHUB_USERNAME }} 21 | password: ${{ secrets.DOCKERHUB_TOKEN }} 22 | - name: Build and push 23 | id: docker_build 24 | uses: docker/build-push-action@v4 25 | with: 26 | push: true 27 | tags: spiretf/docker-comp-server:latest 28 | - name: Image digest 29 | run: echo ${{ steps.docker_build.outputs.digest }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | .direnv 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | from spiretf/docker-tf2-server 2 | maintainer Robin Appelman 3 | 4 | ADD ./socket_3.0.1.zip ./ExtendHumiliation.smx / 5 | ADD ./maps.sh ./sourcemod.sh ./plugins.sh ./configs.sh $SERVER/ 6 | RUN $SERVER/maps.sh \ 7 | && $SERVER/sourcemod.sh \ 8 | && $SERVER/plugins.sh \ 9 | && $SERVER/configs.sh 10 | 11 | EXPOSE 27015/udp 27015/tcp 27021/tcp 27020/udp 12 | 13 | WORKDIR /home/$USER/hlserver 14 | 15 | ENTRYPOINT ["./tf.sh"] 16 | CMD ["+sv_pure", "1", "+mapcycle", "mapcycle_quickplay_payload.txt", "+map", "cp_badlands", "+maxplayers", "24"] 17 | -------------------------------------------------------------------------------- /ExtendHumiliation.smx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiretf/docker-comp-server/da619be499002314a59b0e43c3a8ab8a39e918b6/ExtendHumiliation.smx -------------------------------------------------------------------------------- /ExtendHumiliation.sp: -------------------------------------------------------------------------------- 1 | public Plugin:myinfo = 2 | { 3 | name = "Bonus Round Time Extend", 4 | author = "Mr. Random", 5 | description = "Create cvars to extend bonusroundtime beyond 15 sec.", 6 | version = "1.0", 7 | url = ":(" 8 | } 9 | 10 | new Handle:gBonusRoundTime = INVALID_HANDLE; 11 | new Int:bonustime = 0; 12 | 13 | ConVar g_cvTime; 14 | 15 | 16 | public OnPluginStart() { 17 | CreateTimer(1.0, UpdateCvar, _, TIMER_REPEAT); 18 | gBonusRoundTime = FindConVar("mp_bonusroundtime"); 19 | g_cvTime = CreateConVar("bonus_time", "15", "The length of bonus round time"); 20 | if (gBonusRoundTime != INVALID_HANDLE) 21 | { 22 | SetConVarBounds(gBonusRoundTime, ConVarBound_Upper, true, g_cvTime.FloatValue); 23 | ServerCommand("sm_cvar","mp_bonusroundtime",g_cvTime.FloatValue); 24 | 25 | } 26 | else 27 | { 28 | PrintToServer("Cvar failed to be acquired"); 29 | //somehow tell sourcemod the plugin has crashed. 30 | } 31 | } 32 | public Action UpdateCvar(Handle timer) 33 | { 34 | bonustime = g_cvTime.IntValue; 35 | if (gBonusRoundTime != INVALID_HANDLE) 36 | { 37 | SetConVarBounds(gBonusRoundTime, ConVarBound_Upper, true, g_cvTime.FloatValue); 38 | ServerCommand("mp_bonusroundtime %d", bonustime); 39 | } 40 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Robin Appelman 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Team Fortress 2 + Competitive + Docker 2 | 3 | Docker image for a competitive tf2 server. 4 | 5 | ``` 6 | docker pull spiretf/docker-comp-server 7 | ``` 8 | 9 | See the [base server](https://github.com/spiretf/docker-tf2-server) for additional options 10 | 11 | The server image comes with the etf2l and ugc configs and a set of usefull sourcemod plugins 12 | 13 | ## Addons and Plugins 14 | 15 | - MetaMod:Source 16 | - SourceMod 17 | - SOAP-DM 18 | - MedicStatus 19 | - SupStats 20 | - LogsTF 21 | - Pause 22 | - RecordSTV 23 | - WaitForSTV 24 | - AFK 25 | - RestoreScore 26 | - FixStvSlot 27 | - Updater 28 | - [WebRCON](https://github.com/spiretf/webrcon) 29 | - [whitelist.tf downloader](https://github.com/spiretf/sm_whitelist) 30 | - [missing map downloader](https://github.com/spiretf/mapdownloader) 31 | - [Demos.tf](https://demos.tf) 32 | - [autoexec](https://github.com/spiretf/autoexec) 33 | - [ProperPregame](https://github.com/AJagger/ProperPregame) 34 | - [tf2-comp-fixes](https://github.com/ldesgoui/tf2-comp-fixes) 35 | 36 | ## A Note on Maps 37 | 38 | This image only has `cp_badlands` in the image, any other map that you try to switch to are automatically downloaded by the [map downloader plugin](https://github.com/spiretf/mapdownloader) 39 | -------------------------------------------------------------------------------- /configs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd $HOME/hlserver/tf2/tf/cfg 5 | 6 | ugc_cfg_version_9=$(wget -q -O - "https://www.ugcleague.com/files_tf2h.cfm" | grep -oP "UGC_HL_cfg_(.*)\.zip" | grep -m1 -oP "v\d{6,8}") 7 | ugc_cfg_version_6=$(wget -q -O - "https://www.ugcleague.com/files_tf26.cfm" | grep -oP "UGC_6v6_cfg_(.*)\.zip" | grep -m1 -oP "v\d{6,8}") 8 | ugc_cfg_version_4=$(wget -q -O - "https://www.ugcleague.com/files_tf24.cfm" | grep -oP "UGC_4v4_cfg_(.*)\.zip" | grep -m1 -oP "v\d{6,8}") 9 | 10 | wget -nv "https://github.com/ETF2L/gameserver-configs/releases/latest/download/etf2l_configs.zip" 11 | unzip etf2l_configs.zip 12 | 13 | wget -nv --header "User-Agent: Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.11" "https://www.ugcleague.com/files/configs/UGC_HL_cfg_${ugc_cfg_version_9}.zip" 14 | wget -nv --header "User-Agent: Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.11" "https://www.ugcleague.com/files/configs/UGC_6v6_cfg_${ugc_cfg_version_6}.zip" 15 | wget -nv --header "User-Agent: Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.11" "https://www.ugcleague.com/files/configs/UGC_4v4_cfg_${ugc_cfg_version_4}.zip" 16 | 17 | unzip -oj UGC_HL_cfg_${ugc_cfg_version_9}.zip 18 | unzip -oj UGC_6v6_cfg_${ugc_cfg_version_6}.zip 19 | unzip -oj UGC_4v4_cfg_${ugc_cfg_version_4}.zip 20 | 21 | # dont set the server and stv name 22 | sed -i '/hostname/d' ugc_*_custom.cfg 23 | sed -i '/tv_name/d' ugc_*_custom.cfg 24 | sed -i '/tv_title/d' ugc_*_custom.cfg 25 | 26 | # WaitForSTV handles the stv delay better 27 | sed -i 's/tv_delaymapchange_protect\s*"\?1"\?/tv_delaymapchange_protect 0/g' *.cfg 28 | 29 | rm *.zip 30 | -------------------------------------------------------------------------------- /curl.ext.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiretf/docker-comp-server/da619be499002314a59b0e43c3a8ab8a39e918b6/curl.ext.so -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1728502498, 6 | "narHash": "sha256-PcHsk4n1cKYBePd6mmu/OkC/AqKlocYRpXQv3OlzasA=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "7e1395464c01adb6be404b315202366f73305ace", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "id": "nixpkgs", 14 | "ref": "release-24.05", 15 | "type": "indirect" 16 | } 17 | }, 18 | "root": { 19 | "inputs": { 20 | "nixpkgs": "nixpkgs", 21 | "spire": "spire", 22 | "utils": "utils" 23 | } 24 | }, 25 | "spire": { 26 | "inputs": { 27 | "nixpkgs": [ 28 | "nixpkgs" 29 | ], 30 | "utils": [ 31 | "utils" 32 | ] 33 | }, 34 | "locked": { 35 | "lastModified": 1711219822, 36 | "narHash": "sha256-gVxfFaWqK04qIqjGIZnGWE/0QTr5wZdeGudlGqnDfVI=", 37 | "owner": "spiretf", 38 | "repo": "nix", 39 | "rev": "6007526a96065796476a0b489fe0e259adae1bb9", 40 | "type": "github" 41 | }, 42 | "original": { 43 | "owner": "spiretf", 44 | "repo": "nix", 45 | "type": "github" 46 | } 47 | }, 48 | "systems": { 49 | "locked": { 50 | "lastModified": 1681028828, 51 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 52 | "owner": "nix-systems", 53 | "repo": "default", 54 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 55 | "type": "github" 56 | }, 57 | "original": { 58 | "owner": "nix-systems", 59 | "repo": "default", 60 | "type": "github" 61 | } 62 | }, 63 | "utils": { 64 | "inputs": { 65 | "systems": "systems" 66 | }, 67 | "locked": { 68 | "lastModified": 1726560853, 69 | "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", 70 | "owner": "numtide", 71 | "repo": "flake-utils", 72 | "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", 73 | "type": "github" 74 | }, 75 | "original": { 76 | "owner": "numtide", 77 | "repo": "flake-utils", 78 | "type": "github" 79 | } 80 | } 81 | }, 82 | "root": "root", 83 | "version": 7 84 | } 85 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | utils.url = "github:numtide/flake-utils"; 4 | nixpkgs.url = "nixpkgs/release-24.05"; 5 | spire.url = "github:spiretf/nix"; 6 | spire.inputs.nixpkgs.follows = "nixpkgs"; 7 | spire.inputs.utils.follows = "utils"; 8 | }; 9 | 10 | outputs = { 11 | self, 12 | nixpkgs, 13 | utils, 14 | spire, 15 | }: 16 | utils.lib.eachSystem spire.systems (system: let 17 | overlays = [spire.overlays.default]; 18 | pkgs = (import nixpkgs) { 19 | inherit system overlays; 20 | }; 21 | inherit (pkgs) lib; 22 | spEnv = pkgs.sourcepawn.buildEnv (with pkgs.sourcepawn.includes; [sourcemod]); 23 | in rec { 24 | packages = rec { 25 | inherit spEnv; 26 | }; 27 | devShells.default = pkgs.mkShell { 28 | nativeBuildInputs = with pkgs; [spEnv]; 29 | }; 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /maps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd $HOME/hlserver/tf2/tf/maps 5 | 6 | maps=() 7 | 8 | for map in ${maps[@]}; do 9 | wget -nv "http://fakkelbrigade.eu/maps/$map.bsp" 10 | done 11 | -------------------------------------------------------------------------------- /pause.smx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiretf/docker-comp-server/da619be499002314a59b0e43c3a8ab8a39e918b6/pause.smx -------------------------------------------------------------------------------- /plugins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd $HOME/hlserver/tf2/tf 5 | 6 | # SteamWorks 7 | wget -nv "https://github.com/KyleSanderson/SteamWorks/releases/download/1.2.3c/package-lin.tgz" -O "steamworks.tar.gz" 8 | tar -xf steamworks.tar.gz --strip-components 1 9 | rm steamworks.tar.gz 10 | 11 | # SOAP-DM 12 | wget -nv "https://github.com/sapphonie/SOAP-TF2DM/archive/master.zip" -O "soap-dm.zip" 13 | unzip -o soap-dm.zip 14 | cp -r SOAP-TF2DM-master/* ./ 15 | rm -r SOAP-TF2DM-master 16 | rm soap-dm.zip 17 | 18 | # Improved-Match-Timer 19 | wget -nv "https://github.com/b4nnyBot/Progressive-Ruleset-Timer-Plugins/archive/master.zip" -O "improved-match-timer.zip" 20 | unzip -o improved-match-timer.zip 21 | cp -r Progressive-Ruleset-Timer-Plugins-main/* ./ 22 | rm -r Progressive-Ruleset-Timer-Plugins-main 23 | rm improved-match-timer.zip 24 | 25 | # Socket 26 | 27 | cp /socket_3.0.1.zip socket.zip 28 | unzip -o socket.zip 29 | rm socket.zip 30 | 31 | # tf2-comp-fixes 32 | 33 | wget -nv https://github.com/ldesgoui/tf2-comp-fixes/releases/download/v1.16.19/tf2-comp-fixes.zip 34 | unzip -o tf2-comp-fixes.zip 35 | rm tf2-comp-fixes.zip 36 | 37 | # srctvplus 38 | cd $HOME/hlserver/tf2/tf/addons 39 | 40 | wget -nv https://github.com/dalegaard/srctvplus/releases/download/v3.0/srctvplus.vdf 41 | wget -nv https://github.com/dalegaard/srctvplus/releases/download/v3.0/srctvplus.so 42 | 43 | # Curl 44 | cd $HOME/hlserver/tf2/tf/addons/sourcemod 45 | 46 | wget -nv "https://github.com/sapphonie/SM-neocurl-ext/releases/download/v2.0.0-beta/sm-neocurl.zip" 47 | unzip -o sm-neocurl.zip 48 | rm sm-neocurl.zip 49 | 50 | # Websockets 51 | 52 | wget -nv "https://github.com/peace-maker/sm-websocket/archive/master.zip" -O "sm-websocket.zip" 53 | unzip -o sm-websocket.zip 54 | cp -r sm-websocket-master/* ./ 55 | rm -r sm-websocket-master 56 | rm sm-websocket.zip 57 | 58 | # Medic stats, SupStats, LogsTF, RecordSTV, WaitForStv, AFK, RestoreScore, FixStvSlot and Updater 59 | cd $HOME/hlserver/tf2/tf/addons/sourcemod/plugins 60 | 61 | wget -nv --header="Accept: text/html" --user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0" "http://sourcemod.krus.dk/f2-sourcemod-plugins.zip" 62 | unzip -o f2-sourcemod-plugins.zip 63 | rm f2-sourcemod-plugins.zip 64 | 65 | # WebRCON 66 | wget -nv https://github.com/spiretf/webrcon/raw/master/plugin/webrcon.smx 67 | 68 | # whitelist.tf downloader 69 | wget -nv https://github.com/spiretf/sm_whitelist/raw/master/plugin/whitelisttf.smx 70 | 71 | # map downloader 72 | wget -nv https://github.com/spiretf/mapdownloader/raw/master/plugin/mapdownloader.smx 73 | 74 | # demos.tf uploader 75 | wget -nv https://github.com/demostf/plugin/raw/ba1b642b92585af142a885af7159fa652916c6e7/demostf.smx 76 | 77 | # autoexec 78 | wget -nv https://github.com/spiretf/autoexec/raw/master/plugin/autoexec.smx 79 | 80 | ## Pause 81 | wget -nv https://github.com/spiretf/docker-comp-server/raw/master/pause.smx 82 | 83 | ## NoCheats 84 | wget -nv https://github.com/spiretf/nocheats/raw/master/plugin/nocheats.smx 85 | 86 | ## NoChat 87 | wget -nv https://github.com/spiretf/nochat/raw/main/plugin/nochat.smx 88 | 89 | ## SdrConnect 90 | wget -nv https://github.com/spiretf/sdrconnect/raw/main/plugin/sdrconnect.smx 91 | 92 | ## SetTeam 93 | wget -nv https://github.com/spiretf/setteam/raw/master/plugin/setteam.smx 94 | 95 | ## Who 96 | wget -nv https://github.com/spiretf/who/raw/refs/heads/main/plugin/who.smx 97 | 98 | # proper-pregame 99 | wget -nv https://github.com/AJagger/ProperPregame/raw/master/addons/sourcemod/plugins/properpregame.smx 100 | 101 | # ExtendHumiliation 102 | 103 | cp /ExtendHumiliation.smx . 104 | 105 | chmod 0664 *.smx 106 | 107 | printf "\nsm plugins unload properpregame\n" >> $HOME/hlserver/tf2/tf/cfg/sourcemod/soap_live.cfg 108 | printf "\nsm plugins load properpregame\n" >> $HOME/hlserver/tf2/tf/cfg/sourcemod/soap_notlive.cfg 109 | 110 | cd $HOME/hlserver/tf2/tf 111 | 112 | wget -nv https://github.com/l-Aad-l/updated-pause-plugin/releases/download/v1.4.2/updated-pause-plugin.zip 113 | unzip -o updated-pause-plugin.zip 114 | rm updated-pause-plugin.zip 115 | 116 | -------------------------------------------------------------------------------- /soap-dm.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiretf/docker-comp-server/da619be499002314a59b0e43c3a8ab8a39e918b6/soap-dm.zip -------------------------------------------------------------------------------- /socket_3.0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiretf/docker-comp-server/da619be499002314a59b0e43c3a8ab8a39e918b6/socket_3.0.1.zip -------------------------------------------------------------------------------- /sourcemod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd $HOME/hlserver/tf2/tf 5 | 6 | mm_url=$(wget -q -O - "https://www.metamodsource.net/downloads.php?branch=master" | grep -oP -m1 "https://[a-z.]+/mmsdrop/[0-9.]+/mmsource-(.*)-linux.tar.gz") 7 | sm_url=$(wget -q -O - "http://www.sourcemod.net/downloads.php?branch=master" | grep -oP -m1 "https://[a-z.]+/smdrop/[0-9.]+/sourcemod-(.*)-linux.tar.gz") 8 | 9 | wget -nv $mm_url 10 | wget -nv $sm_url 11 | 12 | tar -xvzf mmsource-*-linux.tar.gz 13 | tar -xvzf sourcemod-*-linux.tar.gz 14 | 15 | rm *.tar.gz 16 | 17 | # prevent automatic map switch 18 | rm addons/sourcemod/plugins/{nextmap.smx,funcommands.smx,funvotes.smx} 19 | --------------------------------------------------------------------------------