166 lines
6.1 KiB
Markdown
166 lines
6.1 KiB
Markdown
# Headless GameServer
|
|
|
|
This target runs the authoritative `InternalGameServer` without SDL, OpenGL,
|
|
audio, Lua, or rendering dependencies. It exposes a deterministic lockstep
|
|
session through a Boost.Beast WebSocket listener (Windows and Linux).
|
|
|
|
## Requirements
|
|
|
|
- CMake >= 3.16, a C++17 compiler (MSVC, or GCC/Clang on Linux)
|
|
- Ubuntu: `sudo apt install build-essential cmake git unzip curl`
|
|
- Internet access on the first configure (see below)
|
|
|
|
Eigen and Boost are **downloaded automatically** by
|
|
`cmake/FetchDependenciesServer.cmake` into `thirdparty/` the first time CMake
|
|
configures the project (Boost zip is ~200 MB). A system Eigen3 is used if
|
|
installed; Boost is always taken from `thirdparty/boost_1_90_0` so versions match
|
|
the Windows build. Nothing else (SDL, zlib, libzip, ...) is needed.
|
|
|
|
## Build
|
|
|
|
Linux (recommended helper, 1 compile job to stay within low RAM):
|
|
|
|
```bash
|
|
server/scripts/build.sh # JOBS=2 server/scripts/build.sh for more
|
|
```
|
|
|
|
Or manually:
|
|
|
|
```bash
|
|
cmake -S server -B server/build -DCMAKE_BUILD_TYPE=Release
|
|
cmake --build server/build --target GameServer --parallel 1
|
|
ctest --test-dir server/build --output-on-failure
|
|
```
|
|
|
|
Windows:
|
|
|
|
```powershell
|
|
cmake -S server -B server/build
|
|
cmake --build server/build --config Release --target GameServer
|
|
ctest --test-dir server/build -C Release --output-on-failure
|
|
```
|
|
|
|
> **Memory note:** compiling Boost.Beast code can use 1-1.5 GB per translation
|
|
> unit. On a small VDS keep `--parallel 1`; if the compiler gets OOM-killed, add
|
|
> temporary swap (`sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile &&
|
|
> sudo mkswap /swapfile && sudo swapon /swapfile`). The running server itself
|
|
> needs only a few tens of MB.
|
|
|
|
After every build the repository's `resources/` directory is copied next to the
|
|
binary (`server/build/resources`, or `server/build/Release/resources` on
|
|
Windows). Content paths are resolved from the process working directory, so the
|
|
server must be started from the directory that contains `resources/`.
|
|
|
|
## Run
|
|
|
|
```bash
|
|
server/scripts/run-server.sh
|
|
```
|
|
|
|
This does `cd server/build` and runs
|
|
|
|
```bash
|
|
./GameServer --campaign battle_zone_multiplayer --address 127.0.0.1 --port 8081
|
|
```
|
|
|
|
Environment overrides: `CAMPAIGN`, `LOCATION`, `ADDRESS`, `PORT`, `BUILD_DIR`.
|
|
|
|
Windows equivalent:
|
|
|
|
```powershell
|
|
.\server\build\Release\GameServer.exe --campaign battle_zone_multiplayer
|
|
```
|
|
|
|
Command line options: `--campaign ID`, `--location ID`, `--campaigns PATH`
|
|
(default `resources/config/campaigns.json`), `--address IP` (default `0.0.0.0`),
|
|
`--port N` (default `8080`), `--ticks N` and `--no-sleep` (smoke tests).
|
|
|
|
The server sends one state snapshot when a player connects, then broadcasts one
|
|
numbered command bundle every 100 ms. Bundles may be empty. Destination input is
|
|
scheduled two ticks (200 ms) in the future. Tick IDs, rather than server or
|
|
client wall-clock values, define simulation time.
|
|
|
|
## Production deployment (Ubuntu, systemd + nginx)
|
|
|
|
The server listens on loopback only (`127.0.0.1:8081`); nginx terminates TLS and
|
|
proxies WebSocket traffic.
|
|
|
|
### 1. systemd service
|
|
|
|
```bash
|
|
sudo cp server/deploy/shadow-gameserver.service /etc/systemd/system/
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now shadow-gameserver
|
|
systemctl status shadow-gameserver
|
|
journalctl -u shadow-gameserver -f # logs
|
|
```
|
|
|
|
The unit caps memory at 512 MB and restarts on failure. After rebuilding run
|
|
`sudo systemctl restart shadow-gameserver`. Edit `User=`/paths in the unit if the
|
|
repository lives elsewhere. To run a second server, copy the unit with another
|
|
`PORT` (and campaign) and add a matching `/server002/` location.
|
|
|
|
### 2. nginx
|
|
|
|
```bash
|
|
sudo cp server/deploy/nginx-api.shadowoverbishkek.com.conf \
|
|
/etc/nginx/sites-available/api.shadowoverbishkek.com
|
|
sudo ln -s /etc/nginx/sites-available/api.shadowoverbishkek.com \
|
|
/etc/nginx/sites-enabled/
|
|
sudo nginx -t && sudo systemctl reload nginx
|
|
```
|
|
|
|
`location /server001/` proxies to `http://127.0.0.1:8081/` with WebSocket
|
|
upgrade headers and a 1 hour read/send timeout (the trailing slashes strip the
|
|
`/server001` prefix).
|
|
|
|
### 3. HTTPS certificate
|
|
|
|
DNS for `api.shadowoverbishkek.com` must point at the VDS first.
|
|
|
|
```bash
|
|
sudo certbot --nginx -d api.shadowoverbishkek.com
|
|
```
|
|
|
|
Certbot issues a Let's Encrypt certificate, adds the `443 ssl` block and an HTTP
|
|
to HTTPS redirect, and installs auto-renewal (`systemctl list-timers | grep certbot`).
|
|
|
|
## Connecting
|
|
|
|
Public endpoint: **`wss://api.shadowoverbishkek.com/server001/`**
|
|
|
|
Handshake check from any machine (expects `HTTP/1.1 101 Switching Protocols`):
|
|
|
|
```bash
|
|
curl -i -N --max-time 3 \
|
|
-H "Connection: Upgrade" -H "Upgrade: websocket" \
|
|
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
|
|
https://api.shadowoverbishkek.com/server001/
|
|
```
|
|
|
|
Or interactively: `npx wscat -c wss://api.shadowoverbishkek.com/server001/`.
|
|
|
|
Locally on the VDS, bypassing nginx: `ws://127.0.0.1:8081`.
|
|
|
|
> **Client limitation:** `src/game_server/WebsocketGameClient.cpp` currently
|
|
> speaks plain `ws://host:port` to `/` (no TLS, no path). Until TLS and path
|
|
> support are added to the client, the graphical game can only connect directly
|
|
> to a non-proxied server (e.g. `ws://127.0.0.1:8080`), not to the `wss://` URL.
|
|
|
|
## Troubleshooting
|
|
|
|
- `Animation metadata failed ... Invalid animation file`: the `.anim` files are
|
|
Git LFS pointer stubs (`version https://git-lfs...`). Fetch the real files with
|
|
`git lfs pull` (or copy `resources/` from a full checkout) and rebuild.
|
|
- `GameServer.LocationState` test currently fails on every platform: it expects
|
|
`battle_zone` and `battle_zone_multiplayer` to share a start-state file, but
|
|
`campaigns.json` points them to different files.
|
|
|
|
- `Server content setup failed`: the server was not started from the directory
|
|
containing `resources/` (use `run-server.sh`).
|
|
- `WebSocket listener setup failed`: port already in use (`ss -ltnp | grep 8081`).
|
|
- `502 Bad Gateway` from nginx: service not running (`systemctl status
|
|
shadow-gameserver`).
|
|
- Connection drops after ~60 s: check `proxy_read_timeout` in the nginx location.
|
|
- Compiler "Killed" / `internal compiler error`: out of memory, see the memory note.
|