mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
4ffa1543cc
Casual/ranked queues need servers allocated per match and shut down afterwards, so cost is incurred only while a match runs - while the existing Docker and CI gates keep passing unchanged. Measured against the repo's own cosmicclash-server image rather than estimated: the runtime image is ~148 MB of content, and boot to the server_started line is ~870 ms on the container's own clock. That was taken under x86_64 emulation on an arm64 host, so it is a pessimistic bound and is recorded as one - it needs re-measuring on native Linux before it sets any timeout. Two findings that would each break a naive implementation, both hit while taking that measurement: - Godot's stdout is block-buffered off a TTY. A detached container logs nothing at all - server_started does not appear even after 35s - so an orchestrator readiness probe that greps the log hangs forever. Probe the UDP socket or flush explicitly. - --port defaults to 7777 and the Dockerfile hardcodes EXPOSE 7777/udp, so several matches cannot share a host without a port range or an address per match. Being UDP, L7 ingress routing does not apply. Also records the honest tension in 'only pay during a match': a server must listen before players connect, and image pull plus scheduling can dwarf 870 ms, so the recommendation is match-level scale-to-zero over a small warm node pool rather than node-level scale-to-zero. The rule for keeping verify-phase6 and verify-enet-integration green: every allocation feature is opt-in via a ServerConfig flag defaulting to current behaviour, with a second Compose file rather than mutating compose.phase6-smoke.yml.
99 lines
3.8 KiB
Markdown
99 lines
3.8 KiB
Markdown
# Dedicated server
|
||
|
||
Phase 6 packages a self-hosted ENet server. It does not publish an image or
|
||
binary: build from this checkout and run the generated image locally or on a
|
||
VPS. Direct-IP ENet uses UDP only; the default port is `7777`.
|
||
|
||
## Local build and verification
|
||
|
||
Docker is the primary path. It builds the stripped `Linux Dedicated Server`
|
||
export, runs it in one container, joins two independent headless clients from
|
||
two other containers, forces one server-owned goal in each of two matches,
|
||
checks both clients observed both scores and arena rotation, then drains.
|
||
|
||
```bash
|
||
make verify-phase6
|
||
```
|
||
|
||
The command prints the temporary log directory even on failure and always
|
||
removes its Compose containers. It neither pushes an image nor uploads an
|
||
artifact. The same command is the only operation in the Phase 6 GitHub Actions
|
||
workflow. Its pinned Godot build image is about 2.4 GB, so leave several GB of
|
||
Docker disk space free for its layers and the exported project.
|
||
|
||
To build and run a server manually:
|
||
|
||
```bash
|
||
docker build --target server -t cosmic-clash-server .
|
||
docker run --rm -p 7777:7777/udp cosmic-clash-server \
|
||
--port=7777 --min-players=2 --start-countdown=5
|
||
```
|
||
|
||
All server output is structured stdout/stderr. Use Docker's logging driver for
|
||
rotation; for example, configure `json-file` with `max-size` and `max-file` on
|
||
the host. Do not add in-process log rotation.
|
||
|
||
## Configuration
|
||
|
||
Every flag is printed by `--help`; unknown flags fail startup. Command-line
|
||
values override a Godot config file's `[server]` values, which override
|
||
defaults. Mount one into the container when needed:
|
||
|
||
```ini
|
||
[server]
|
||
port=7777
|
||
max-clients=12
|
||
min-players=2
|
||
start-countdown=5
|
||
arena-rotation=sequential
|
||
log-level=info
|
||
```
|
||
|
||
```bash
|
||
docker run --rm -p 7777:7777/udp \
|
||
-v "$PWD/server.cfg:/etc/cosmic-clash/server.cfg:ro" \
|
||
cosmic-clash-server --config=/etc/cosmic-clash/server.cfg
|
||
```
|
||
|
||
`--max-matches=N` drains only after match `N` ends, then exits `0`; use it for
|
||
planned restarts under a process supervisor. `--smoke-force-goal-after=<seconds>`
|
||
is a documented local-verification switch; its default `-1` disables it, and it
|
||
must not be used for normal matches.
|
||
|
||
## Native systemd deployment
|
||
|
||
Copy the exported binary and assets to `/opt/cosmic-clash`, create the
|
||
`cosmicclash` service user, place configuration at
|
||
`/etc/cosmic-clash/server.cfg`, then install
|
||
`deploy/cosmic-clash-server.service` as
|
||
`/etc/systemd/system/cosmic-clash-server.service` and enable it:
|
||
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now cosmic-clash-server
|
||
sudo journalctl -u cosmic-clash-server -f
|
||
```
|
||
|
||
Godot does not provide a GDScript SIGTERM hook. `systemctl stop`, Ctrl-C, or a
|
||
container stop terminates immediately and connected ENet clients will time out
|
||
after roughly five seconds. Prefer `--max-matches` for planned drains.
|
||
|
||
## Network and sizing
|
||
|
||
Open and forward **UDP 7777** (or the configured `--port`) in the host firewall
|
||
and any cloud security group. TCP is not used. The Phase 1 sizing estimate is
|
||
roughly 6–10 simultaneous match processes per modern core, 150–250 MB RSS per
|
||
process, and about 630 kbit/s upstream for a full six-player match; use those
|
||
as a starting point and monitor actual CPU, RSS, and egress.
|
||
|
||
Per-match autoscaling — allocating a server for one match and shutting it
|
||
down afterwards — is designed in [`docs/MATCHMAKING.md`](docs/MATCHMAKING.md)
|
||
and not yet implemented. The sizing numbers above predate that work and
|
||
should be re-measured under real concurrency before they size a bill.
|
||
|
||
This build must not be exposed to strangers yet. Slot reclaim is still keyed
|
||
by display name, so a player who knows a disconnected player's name can claim
|
||
their reserved slot. Phase 7 Steam-auth identity is the required fix. Local,
|
||
LAN, and controlled VPS verification are in scope; the public-internet phase
|
||
gate remains blocked on that identity work.
|