M2.3: the shared world (trees, pickups, pieces, chests, fires, doors, graves, clock on the server)

This commit is contained in:
Paul Bailey
2026-11-04 18:12:49 +00:00
parent 74cbf96458
commit 0c18b6d49e
17 changed files with 1672 additions and 94 deletions

View File

@@ -18,10 +18,10 @@ concrete decisions for this codebase, as it stands after M1.
## 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`,
The server accepts players, checks who they are and keeps the player list.
Since M2.3 it also runs the shared island (§5). Flags: `--server`,
`--port`, `--config`, `--server-name`, `--max-players`, `--password`,
`--world`, and `--exit-with <pid>` (quit when that process exits; the tests
`--world`, `--seed`, 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
@@ -161,8 +161,9 @@ A refused move sends the player back to their last good position.
Respawning is a flagged teleport, allowed if it lands within 25 m of the
island spawn. Normal play is never corrected, so there's no rubber-banding.
This is the Valheim model, and simpler than replaying inputs on the server.
Through-wall checks come with the shared world (M2.3), since the server
doesn't know about buildings yet.
Since M2.3 the server also refuses a move whose straight line crosses a
wall, door frame (or closed door) or gable at waist height, checked from
both ends so someone a wall was built on top of can still step out.
The server sends everyone's positions 20 times a second. Each one is stamped
with when the player moved (their clock plus a smoothed per-player offset),
@@ -177,9 +178,38 @@ Measured in `tests/test_net_world.gd` at 100 ms ± 30 ms simulated latency:
a 4 m/s walk is seen at a median of 4.2 m/s, top 5.0, with no freezes, and
it ends within a centimetre of where the walker stopped.
Until later phases, while online: animals are off (M2.5), and building,
gathering and other world changes are local only and aren't saved (M2.3,
M2.4). The HUD says so. Respawning ignores beds.
**The shared world** *(built in M2.3: `world_net.gd`, autoload WorldNet)*.
The dedicated server runs the real world scene headless with nobody of its
own in it (`world.gd` `server_mode`), and saves it like a single-player
world, in `worlds/<world>/` next to `server.json`. It owns trees and rocks
(damage, felling, regrowth), pickups, berry bushes, dropped items, every
placed piece (walls, doors, chests, graves, fires, beds), fire fuel, door
state and the clock. Weather is worked out from the seed and the clock, so
it needs no syncing.
- A client's `Actions.*` intents that change the island become requests
(`_c2s_hit`, `_c2s_place`, `_c2s_take`, `_c2s_chest_click`...). The server
checks reach (from where it last saw that player), applies the change,
and tells everyone (`_s2c_node`, `_s2c_piece_added`...). Nothing changes
on your screen until the server says so, so two people grabbing the same
stick get one stick between them.
- Whatever a change earns you (wood, a pickup, a dismantled wall and
anything that collapsed with it, items out of a chest) the server sends
to you (`_s2c_give`). What doesn't fit lands at your feet.
- Pieces have a net id that's saved with them, so they keep it across
server restarts.
- Chest clicks happen on the server (someone else may be using the chest),
which hands back what you're holding.
- On joining, your world asks for a snapshot of everything once its
scenery is up, then gets each change in order. Regrowth only happens on
the server, never on top of a player.
- The world (fires, regrowth, the clock) pauses while nobody's on.
Until later phases, while online: your inventory, stats and crafting stay on
your own game and aren't saved by the server (M2.4), so the server trusts
what you say you're putting into the world (a wall, a chest deposit, a
grave's contents) but decides everything taken out. Animals are off (M2.5).
Sleeping is off (it needs the sleep rule, M2.5). Respawning ignores beds.
The HUD says so.
**Interest.** On a 320 m worldlet with 8 players, the server sends every
entity to everyone at first. Distance-based interest is a later
@@ -208,6 +238,12 @@ worlds/<world>/
friends, name, appearance
backups/<timestamp>/ rolling copies (backups_kept)
```
*As of M2.3:* `worlds/<world>/meta.json` and `world.json` (with pieces'
net ids, no owners yet) are written next to `server.json` within 10 s of
any change, when a player leaves, and every `autosave_minutes`. (Ctrl+C
kills Godot without running any code, so there's no save on the way out.) An
existing world keeps the seed it was made with, even if `server.json`
changes. Player files, owners and backups come in M2.4 and M2.7.
- Single-player saves (today's `world.json` with a `"player"` block) are
migrated on first load. That block becomes the local player's file. The
test suite keeps an old-format save fixture to prove it.
@@ -237,7 +273,7 @@ player" or "this player":
| **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** *(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** *(done)* | 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.3** *(done)* | 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 |
| **M2.5** | wildlife and wolves server-side, projectiles, fishing, sleeping with the rule | a pack hunts two players at once; arrows hit on both screens |
| **M2.6** | server rules: PvP, raiding, locks and friends, death penalty, difficulty; admin console and chat | each preset (co-op, rivals, adversarial) behaves as described in §4 |
@@ -255,6 +291,10 @@ player" or "this player":
- **Save tests:** world and player file round trips, migration of old
saves, backups.
- The existing 9 suites keep running in single-player (loopback) mode.
- `tests/test_net_shared.gd` (M2.3) is the two-world version: this test is
one player and `tests/net_world_bot.gd` (a second process with its own
world, following commands the test writes as files) is the other. A
`--debug-regrow <k>` server flag speeds regrowth up for it.
## 11. Risks
- **Prediction feel** is the hard part. It gets an early phase (M2.2) so