Roll your server

...with Blackjack and hookers!

Hosting your own Quake World server is not mandatory, but since not many of existing welcome hackers, I wrote down some notes for anyone who wishes to have their own. There are two mainstream server setups:

The mvdsv is dominant game engine choice for Quake World server. It's extremely lightweight and by default it comes with the KTX mod which gives you basic quality of life features.

FTEQW is its own beast. This all-in-one package is more extensibile and supports clients that mvdsv don't (like QuakeSpasm, Ironwail, vkQuake or original WinQuake).



0. Prerequisites

Both options require you to have VPS (obviously) with GNU\Linux installed. I would personally recommend either Debian or Rocky linux, latter of which will be this tutorial aimed at.

I would also highly recommend running your server containerised. Running a managing more configs and/or services of this nature outside of Docker can be messy. Follow your distro specific documentation for installing docker properly. For Rocky linux it would be this doc.

Last thing, you will need official content files (.pak files) with game textures, models and so on. Luckily, the game costs less than cup of coffee, so this should not be a real problem. EDIT: the game just got 30 year anniversary update which gives you additional single player content.

With that cleared, let's...

...jump to mvdsv + KTX tutorial.

...jump to FTE tutorial.


1.a mvdsv + KTX setup

On your VPS in non-root users home, create directory qwhvh and inside it create these subdirs:

mkdir qwhvh qwhvh/id1 qwhvh/ktx qwhvh/demos qwhvh/logs && cd qwhvh

Copy your PAK0.pak and PAK1.pak files from Quake game files inside the id1/ dir. Easiest way to do it (if you don't have SFTP already established) is to temporarily upload them on the web and curl them directly to the directory.

When done, create main config files for the server.

touch ktx/server.cfg docker-compose.yml Dockerfile
Bellow, I will paste working configs for these three files.

Dockerfile:

FROM debian:bookworm-slim AS build RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential cmake git ca-certificates libcurl4-openssl-dev && \ rm -rf /var/lib/apt/lists/* WORKDIR /src RUN git clone --depth 1 https://github.com/QW-Group/mvdsv.git && \ cd mvdsv && \ cmake -B build -DCMAKE_BUILD_TYPE=Release . && \ cmake --build build -j$(nproc) RUN git clone --depth 1 https://github.com/QW-Group/ktx.git && \ cd ktx && \ cmake -B build -DCMAKE_BUILD_TYPE=Release -DBOT_SUPPORT=ON . && \ cmake --build build -j$(nproc) FROM debian:bookworm-slim RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl4 ca-certificates && \ rm -rf /var/lib/apt/lists/* && \ useradd -m -u 1000 qw WORKDIR /qw COPY --from=build /src/mvdsv/build/mvdsv ./mvdsv COPY --from=build /src/ktx/build/qwprogs.so ./ktx/qwprogs.so COPY --from=build /src/ktx/resources/example-configs/ktx/bots ./ktx/bots RUN mkdir -p id1 qw ktx demos logs && chown -R qw:qw /qw USER qw EXPOSE 27500/udp ENTRYPOINT ["./mvdsv", "-port", "27500", "-game", "ktx", "+set", "sv_progtype", "1", \ "+set", "sv_progsname", "qwprogs", "+maxclients", "12", "+exec", "server.cfg"]

docker-compose.yml:

services: qwhvh: build: . container_name: qwhvh restart: unless-stopped mem_limit: 1g # hard cap the container to use at most 1 GB RAM mem_reservation: 256m # soft target to keep it around 256 MB dns: # pin explicit DNS servers for the container (more in pt 3. Known issues) - 1.1.1.1 - 8.8.8.8 ports: - "27500:27500/udp" # Quake World (mandatory) 27500 is standard but you may choose your own #- "28000:28000/tcp" # QuakeTV (optional) volumes: - ./id1:/qw/id1:ro,Z - ./ktx:/qw/ktx:Z - ./demos:/qw/demos:Z - ./logs:/qw/logs:Z

server.cfg: (note, change placeholder values)

hostname "yourserver.example" // the server's display name, shown in the clients' server browser sv_admininfo "your-email" // admin contact info serverinfo welcome "hello world" // this will be printed on master server NOT to players sv_speedcheck 0 // speed-hack detection sv_minping 0 // don't punish low/odd pings sv_kicktop 0 // kdetect specific packet-abuse exploit sv_unfake 0 // detects "fakeshaft"/aim-deception packet tricks sv_cheats 0 // engine built-in console cheats off (no god/noclip/give) allow_download 1 // master download switch allow_download_skins 1 // allow downloading custom skins, allow_download_models 1 // models, allow_download_sounds 1 // sounds, allow_download_maps 1 // maps, allow_download_demos 1 // and recordings // KTX set k_allow_fbskins 1 // allow fullbright skins set k_allow_hud 1 // allow custom HUDs set k_allow_fakeshaft 1 // allow fakeshaft (essential) set k_allow_hideplayers 1 // allow client-side hiding of players set k_allow_pext 1 // allow protocol extensions set k_allow_scripts 1 // allow client scripting // bots (frogbots) set k_fb_skill 7 // bot skill 0-20; 7 is default set k_fb_chat 0 // set to 0 otherwise it clutters the chat set k_defmode ffa // FFA is standard for HvH set k_free_mode 5 // who can call free modes (5 = all) set k_allowed_free_modes 255 // which modes are allowed (255 = all, incl. FFA) set k_motd1 "welcome to yourserver" // line1 set k_motd2 "hack vs hack" // line2 set k_motd3 "happy gibbing" // line3 set k_motd_time 5 // all 3 lines stay shown for 5 seconds set k_maxclients 6 // optional? the Dockerfile and docker-compose seems overide this set k_maxspectators 6 // same as above sv_forcespec_onfull 1 // when players full, extra joins become spectators - rcon_password "CHANGE-ME" // your password for managing server from the client sv_crypt_rcon 1 // encrypt rcon traffic so the password isn't sent in the clear sv_phs 1 // potentially-hearable-set sound optimization setmaster master.quakeservers.net:27000 // master server set to quakeservers, but you may add more floodprotmsg "Easy easy now" // message shown to players who trigger flood protection floodprot 4 4 10 // max 4 messages per 4 seconds, then mute for 10 seconds map dm3 // starting map

To configure the game modes, we need to build the container first. From inside the qwhvh directory run:

docker compose build --no-cache

Now we may modify the game mode configs as the KTX generated the files needed. These can be found in ktx/configs/usermodes/. Check out example game mode configs in the KTX project repository.

Let's start the server.

docker compose up -d

Then open the game ports on firewall.

sudo firewall-cmd --permanent --add-port=27500/udp --reload

And open the port in VPS application firewall settings in your account dashboard as well.

That's it. Happy hacking!



1.b FTEQW setup

NOTE: For the purposes of this tutorial, I will assume that you didn't decided to run both mvdsv and FTE servers at once. If you did however, don't forget to change the server root directory name and port numbers, otherwise you would run into problems.

On your VPS in non-root users home, create directory qwhvh and inside it create these subdirs:

mkdir qwhvh qwhvh/id1 qwhvh/id1/maps && cd qwhvh

Copy your PAK0.pak and PAK1.pak files from Quake game files inside the id1/ dir. Easiest way to do it (if you don't have SFTP already established) is to temporarily upload them on the web and curl them directly to the directory.

When done, create main config files for the server.

touch id1/server.cfg docker-compose.yml Dockerfile

Bellow, I will paste working configs for these three files.

Dockerfile:

# ---- BUILD STAGE ---- FROM debian:bookworm-slim AS build RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential git ca-certificates \ zlib1g-dev libpng-dev libjpeg-dev && \ rm -rf /var/lib/apt/lists/* WORKDIR /src RUN git clone --depth 1 https://github.com/fte-team/fteqw.git RUN cd fteqw/engine && make sv-rel -j$(nproc) FROM debian:bookworm-slim RUN apt-get update && apt-get install -y --no-install-recommends \ zlib1g libpng16-16 libjpeg62-turbo ca-certificates && \ rm -rf /var/lib/apt/lists/* && \ useradd -m -u 1000 qw WORKDIR /qw COPY --from=build /src/fteqw/engine/release/fteqw-sv ./fteqw-sv RUN mkdir -p id1 && chown -R qw:qw /qw USER qw EXPOSE 27500/udp ENTRYPOINT ["./fteqw-sv", "-basedir", "/qw", "+set", "sv_port", "27500", "+exec", "server.cfg"]

docker-compose.yml

services: qwhvh2: build: . container_name: qwhvh restart: unless-stopped mem_limit: 1g # hard cap the container to use at most 1 GB RAM mem_reservation: 256m # soft target to keep it around 256 MB dns: # pin explicit DNS servers for the container (more in pt 3. Known issues) - 1.1.1.1 - 8.8.8.8 ports: - "27500:27500/udp" # Quake World (mandatory) 27500 is standard but you may choose your own #- "28000:28000/tcp" # QuakeTV (optional) volumes: - ./id1:/qw/id1:ro,Z

server.cfg ( I am not sure how to do comments in this config, but the titles are pretty selfexplanatory...)

hostname "your server" sv_cheats 0 ruleset_allow_fbmodels 1 ruleset_allow_in 1 deathmatch 1 teamplay 0 timelimit 10 maxclients 14 maxspectators 6 map dm4

Now we are ready to build.

docker compose build --no-cache

And start the server.

docker compose up -d

Let's not forget to open the port on firewall.

sudo firewall-cmd --permanent --add-port=27500/udp --reload

And open the port in VPS application firewall settings in your account dashboard as well.

That's it. Happy hacking!



2. Customizing the server

Maps

Most common way to customize your server in competetive hack vs hack is adding custom maps and textures. To add custom map, simply curl the .bsp file you want from the quakeworld.nu map index directly to your maps directory.

For both server setups that would be:

curl -o qwhvh/id1/maps/custom-map.bsp https://maps.quakeworld.nu/all/custom-map.bsp


3. Known issues

Sometimes the server shows offline status, but is actually running. Logs show Setting nomaster mode and SV_BroadcastUpdateServerList: No master servers configured, repeated on every restart. The container simply lost DNS resolution and can't turn master server FLDN into an IP, and keeps falling back to "nomaster". This occur when no explicit DNS servers are marked down in docker-compose.yml or if something on the host disrupts Dockers networking (firewall reload or cronjob).

To fix this, simply re-build the container.