QuakeKube

QuakeKube is a Kubernetes-ified version of Quake 3 that manages a dedicated server in a Kubernetes Deployment. It uses QuakeJS to enable clients to connect and play in the browser.

Limitations

This uses files from the Quake 3 Demo. The demo doesn't allow custom games, so while you can add new maps, you couldn't say load up Urban Terror. I think with pak files from a full version of the game this would be possible, but I haven't tried it (maybe one day).

Another caveat is that the copy running in the browser is using QuakeJS. This version is an older verison of ioquake3 built with emscripten and it does not appear to be supported, nor does it still compile with any newer versions of emscripten. I believe this could be made to work again, but I haven't personally looked at how involved it would be. It is worth noting that any non-browser versions of the Quake 3 could connect to the dedicated servers.

What is this project for?

This was just made for fun and learning. It isn't trying to be a complete solution for managing Quake 3 on Kubernetes, and I am using it now as a repo of common patterns and best practices (IMO) for Go/Kubernetes projects. I think some fun additions though might be adding code to work as a Quake 3 master server, a server that exchanges information with the game client about what dedicated game servers are available, and making a controller/crd that ties it all together.

Getting Started

Quick start

With an existing K8s cluster

Deploy the example manifest:

$ kubectl apply -f https://raw.githubusercontent.com/ChrisRx/quake-kube/master/example.yaml

Without an existing K8s cluster

Start an instance of Kubernetes locally using kind):

$ kind create cluster

Deploy the example manifest:

$ kubectl apply -f https://raw.githubusercontent.com/ChrisRx/quake-kube/master/example.yaml

This can be used to get the kind node IP address:

kubectl get nodes -o jsonpath='{.items[?(@.metadata.name=="kind-control-plane")].status.addresses[?(@.type=="InternalIP")].address}'

Finally, navigate to http://<kind node ip>:30001 in the browser.

Development

Using tilt

Tilt and ctlptl can be used to quickly build and run everything.

First, using ctlptl create a new local cluster with a container registry:

ctlptl create cluster kind --registry=ctlptl-registry

This also includes the Kubernetes objects necessary to configuring a local registry. Next, simply run tilt:

tilt up

This watches for changes in project files and rebuilds as necessary.

How it works

QuakeKube makes use of ioquake for the Quake 3 dedicated server, and QuakeJS, a port of ioquake to javascript using Emscripten, to provide an in-browser game client.

Networking

The client/server protocol of Quake 3 uses UDP to synchronize game state. Browsers do not natively support sending UDP packets so QuakeJS wraps the client and dedicated server net code in websockets, allowing the browser-based clients to send messages and enable multiplayer for other clients. This ends up preventing the browser client from using any other Quake 3 dedicated server. In order to use other Quake 3 dedicated servers, a proxy handles websocket traffic coming from browser clients and translates that into UDP to the backend. This gives the flexibility of being able to talk to other existing Quake 3 servers, but also allows using ioquake (instead of the javascript translation of it), which uses considerably less CPU and memory.

QuakeKube also uses a cool trick with cmux to multiplex the client and websocket traffic into the same connection. Having all the traffic go through the same address makes routing a client to its backend much easier (since it can just use its document.location.host).

Quake 3 demo EULA

The Quake 3 dedicated server requires an End-User License Agreement be agreed to by the user before distributing the Quake 3 demo files that are used (maps, textures, etc). To ensure that the installer is aware of, and agrees to, this EULA, the flag --agree-eula must be passed to q3 server at runtime. This flag is not set by default in the container image and is therefore required for the dedicated server to pass the prompt for EULA. The example.yaml manifest demonstrates usage of this flag to agree to the EULA.

Configuration

The server and maps are configured via ConfigMap that is mounted to the container:

apiVersion: v1
kind: ConfigMap
metadata:
  name: quake3-server-config
data:
  config.yaml: |
    fragLimit: 25
    timeLimit: 15m
    game:
      motd: "Welcome to Quake Kube"
      type: FreeForAll
      forceRespawn: false
      inactivity: 10m
      quadFactor: 3
      weaponRespawn: 3
    server:
      hostname: "quakekube"
      maxClients: 12
      password: "changeme"
    maps:
    - name: q3dm7
      type: FreeForAll
    - name: q3dm17
      type: FreeForAll
    - name: q3wctf1
      type: CaptureTheFlag
      captureLimit: 8
    - name: q3tourney2
      type: Tournament
    - name: q3wctf3
      type: CaptureTheFlag
      captureLimit: 8
    - name: ztn3tourney1
      type: Tournament

The time limit and frag limit can be specified with each map (it will change it for subsequent maps in the list):

- name: q3dm17
  type: FreeForAll
  fragLimit: 30
  timeLimit: 30

Capture limit for CTF maps can also be configured:

- name: q3wctf3
  type: CaptureTheFlag
  captureLimit: 8

Any commands not captured by the config yaml can be specified in the commands section:

commands:
- seta g_inactivity 600
- seta sv_timeout 120

Add bots

Bots can be added individually to map rotations using the commands section of the config:

commands:
  - addbot crash 1
  - addbot sarge 2

The addbot server command requires the name of the bot and skill level (crash and sarge are a couple of the built-in bots).

Another way to add bots is by setting a minimum number of players to allow the server to add bots up to a certain value (removed when human players join):

bot:
  minPlayers: 8
game:
  singlePlayerSkill: 2

singlePlayerSkill can be used to set the skill level of the automatically added bots (2 is the default skill level).

Setting a password

A password should be set for the server to allow remote administration and is found in the server configuration settings:

server:
  password: "changeme"

This will allow clients to use \rcon changeme <cmd> to remotely administrate the server. To create a password that must be provided by clients to connect:

game:
  password: "letmein"

This will add an additional dialog to the in-browser client to accept the password. It will only appear if the server indicates it needs a password.

Add custom maps

The content server hosts a small upload app to allow uploading pk3 or zip files containing maps. The content server in the example.yaml shares a volume with the game server, effectively "side-loading" the map content, however, in the future the game server will introspect into the maps and make sure that it can fulfill the users map configuration before starting.

Credits

  • inolen/quakejs - The really awesome QuakeJS project that makes this possible.
  • ioquake/ioq3 - The community supported version of Quake 3 used by QuakeJS. It is licensed under the GPLv2.
  • begleysm/quakejs - Information in the README.md (very helpful) was used as a guide, as well as, some forked assets of this project (which came from quakejs-web originally) were used.
  • joz3d.net - Useful information about configuration values.