# Server The authoritative server executable. ## Running ``` Usage: tw_server [--quicr-port ] [--cluster-port ] --quicr-port UDP port for player connections (default: 8101) --cluster-port UDP port for zone-server peering (default: 8102) ``` Metrics reporting to TimescaleDB stays off until `TIMESCALEDB_HOST` is set: | Variable | Default | |------------------------|----------------| | `TIMESCALEDB_HOST` | unset (off) | | `TIMESCALEDB_PORT` | `5432` | | `TIMESCALEDB_DB` | `mmo` | | `TIMESCALEDB_USER` | `mmo` | | `TIMESCALEDB_PASSWORD` | empty | | `TIMESCALEDB_TABLE` | `zone_metrics` | ## Building natively See the root `README.md`. The target is `tw_server` and the binary lands in `/modules/server/tw_server`. ## Building the image `Dockerfile` has three stages: a toolchain stage holding the C++ build environment, a build stage that compiles `tw_server` with clang, and a runtime stage that carries only the binary and its shared libraries. The build context is the repository root, so run it from there: ```bash $ docker build -f modules/server/Dockerfile -t tw_server . ``` The toolchain stage installs the compiler and tools (clang, cmake, ninja, mold, glslangValidator) and the development packages CMake looks for (Vulkan, SDL2, protobuf, libpq, libpqxx) from apt. It touches no source, so it only rebuilds when that package list changes. The build stage configures a Release build on top of it with `CC=clang` / `CXX=clang++`. glm, entt, Jolt, spdlog, expected, Catch2 and tracy are cloned while configuring, so the build needs network access. `.dockerignore` keeps the local build directories and those cloned sources out of the context. ## Reusing the toolchain Layer caching already keeps apt out of a rebuild, but the cache is local and dies with `docker builder prune`. To pin the toolchain down, build that stage on its own and tag it: ```bash $ docker build -f modules/server/Dockerfile --target toolchain -t tw_toolchain . ``` `TOOLCHAIN_IMAGE` then points the build stage at it, and the apt step is skipped outright rather than cache-hit: ```bash $ docker build -f modules/server/Dockerfile \ --build-arg TOOLCHAIN_IMAGE=tw_toolchain -t tw_server . ``` The default is the in-file `toolchain` stage, so a plain build still works standalone. Any registry tag works too, which is the useful form on CI. To carry it between machines by hand: ```bash $ docker save tw_toolchain | zstd -o tw_toolchain.tar.zst $ zstd -dc tw_toolchain.tar.zst | docker load ``` The runtime stage is `gcr.io/distroless/cc-debian13:nonroot` — glibc, libstdc++ and a `nonroot` user, no shell and no package manager. Since nothing can be installed there, the build stage walks `ldd` over the binary and stages every shared library it resolved into `/rootfs`, which the runtime stage copies in whole. glibc and the loader are filtered out of that list: the binary is built against 2.39 and the runtime image ships 2.41, which runs it, but the two must not be mixed. Nothing in the image can be executed except the server, so `docker exec` and `docker run --entrypoint` are of no use for poking around. Swap the base for `gcr.io/distroless/cc-debian13:debug-nonroot` when a busybox shell is needed. Run it, publishing both UDP ports: ```bash $ docker run --rm -p 8101:8101/udp -p 8102:8102/udp tw_server ``` Arguments after the image name reach the binary, and environment variables are passed as usual: ```bash $ docker run --rm -p 9101:9101/udp -e TIMESCALEDB_HOST=timescale \ tw_server --quicr-port 9101 ```