M2.1: dedicated server, joining, identity handshake, names, lobby

- Net autoload: headless dedicated server (--server) on UDP 24242 via ENet,
  server.json preferences (flags override per run, never saved), reserved
  names in names.json
- Identity: RSA-2048 key per install (Godot has no Ed25519), player id =
  key hash, challenge/response so ids can't be faked; profile with name
  and limited appearance (tunic, hair, beard)
- Server refuses bans, allowlist misses, duplicate logins, bad or taken
  names, wrong passwords, old protocol versions and a full server, each
  with a clear reason; first player on a new server becomes admin
- Title screen: Profile, Join Game, and a lobby with server info, rules
  and who's online
- test_net_lobby: real headless server + bot processes over UDP (38
  checks); --exit-with so test servers never outlive the test
This commit is contained in:
Paul Bailey
2026-11-04 13:40:27 +00:00
parent 89683e78fd
commit f62f71658f
11 changed files with 1152 additions and 6 deletions

View File

@@ -17,6 +17,13 @@ concrete decisions for this codebase, as it stands after M1.
- **Co-op or adversarial**, chosen by server rules (§5).
## 2. Running a server
*Built in M2.1* (`scripts/net/net.gd`, `server_config.gd`, `identity.gd`).
As of M2.1 the server accepts players, checks who they are and keeps the
player list. The world itself isn't served yet (M2.3). Flags: `--server`,
`--port`, `--config`, `--server-name`, `--max-players`, `--password`,
`--world`, and `--exit-with <pid>` (quit when that process exits; the tests
use it so a crashed test never leaves a server running). Flags apply to
that run only and are never written to `server.json`.
```bash
# Dedicated server (no window, no player)
godot --headless --path . -- --server --config server.json
@@ -42,7 +49,9 @@ godot --headless --path . -- --server --world "Valley of Us" --port 24242
## 3. Players: identity and names
- **Identity:** on first run each game install generates a private key
(Ed25519) and keeps it in `user://identity.key`. The public key's hash is
(RSA-2048, since Godot's Crypto has no Ed25519) and keeps it in
`user://identity/key.pem`, with the name and appearance in
`user://identity/profile.json`. The public key's hash is
the **player id**. On joining, the client signs a server-chosen challenge,
so nobody can claim someone else's id. No passwords and no central
accounts.
@@ -199,7 +208,7 @@ player" or "this player":
| Phase | Delivers | Done when |
|---|---|---|
| **M2.0** | worldlet W0 (terrain from data, no visible change), recommended first | seed 1337 generates the same height grid as today; all suites pass |
| **M2.1** | network skeleton: `--server`, ENet on 24242, Join screen (address, name), handshake with identity and challenge, name rules, player list | two clients and a headless server on one Mac connect, see the player list, and disconnect cleanly |
| **M2.1** *(done)* | network skeleton: `--server`, ENet on 24242, Join screen (address, name), handshake with identity and challenge, name rules, player list | two clients and a headless server on one Mac connect, see the player list, and disconnect cleanly |
| **M2.2** | player bodies and name tags; predicted movement, interpolation for others | two players walk around together at 100 ms simulated latency without visible rubber-banding |
| **M2.3** | world state replicated: resource nodes, pickups, pieces, chests, fires, doors, clock, weather | one chops a tree and the other sees it fall; one builds a wall and the other walks into it |
| **M2.4** | intents as RPCs; per-player inventory, stats, diet, death and graves; per-player saves and migration | two players craft, eat, die and loot their own graves; an old single-player save loads |