diff --git a/docs/README.md b/docs/README.md index 90a7bad..c970692 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,6 +39,8 @@ Each subsystem note should name the design topic it implements (by title, e.g. " - [`adr/0007-declarative-content-via-modding-api.md`](adr/0007-declarative-content-via-modding-api.md): declarative content loads through the modding API. - [`adr/0008-split-coordinate-entity-positions.md`](adr/0008-split-coordinate-entity-positions.md): split-coordinate entity positions. - [`adr/0009-baseline-relative-sparse-chunk-persistence.md`](adr/0009-baseline-relative-sparse-chunk-persistence.md): baseline-relative sparse chunk persistence. + - [`adr/0010-net-crate-async-runtime.md`](adr/0010-net-crate-async-runtime.md): dedicated `net` crate with a confined async runtime. + - [`adr/0011-authority-stream-for-server-pushed-state.md`](adr/0011-authority-stream-for-server-pushed-state.md): a dedicated authority stream for server-pushed state. - [`adr/template.md`](adr/template.md): template for new decisions. Subsystem notes: @@ -46,6 +48,8 @@ Subsystem notes: - [`packs.md`](packs.md): data packs & resource packs (load order, layout, resolution). - [`rendering.md`](rendering.md): rendering & coordinate gotchas (Vulkan clip space, Blender/glTF import). - [`chunk_streaming.md`](chunk_streaming.md): chunk streaming and async worker pipeline. +- [`meshing.md`](meshing.md): greedy meshing, the mesh worker pool, and frustum culling. +- [`diagnostics.md`](diagnostics.md): runtime statistics collection and the debug panel. - [`save_format.md`](save_format.md): chunk persistence, region-file layout, save actor, and load pipeline. Further subsystem notes are added here as systems are implemented and locked. diff --git a/docs/chunk_streaming.md b/docs/chunk_streaming.md index 5d0fa8e..ecfa5c2 100644 --- a/docs/chunk_streaming.md +++ b/docs/chunk_streaming.md @@ -64,13 +64,15 @@ The server-side pump is `chunk_stream_task`; its client mirror is `client_chunk_ Each connected client is tracked by a `ClientStream` holding its `ChunkSink`, its current desired set (radius-clamped to `SERVER_MAX_RADIUS`), and its `sent` set. On each subscription, `desired_diff(previous, new)` yields the load list (`new − previous`) and drop list (`previous − new`); a `ChunkMessage::Drop` is emitted for every already-**sent** chunk that left the set. Newly-desired chunks are **not** sent immediately — chunk loads are async, so `ClientStream::flush` runs each tick and delivers every desired-but-unsent chunk that has since become resident, retrying on later ticks until the pool returns it. +Delivery is bounded by `MAX_DELIVERIES_PER_TICK` (32 chunks per client per tick). Encoding a chunk is the expensive part of `flush`, and a client whose subscription has just jumped can have hundreds of chunks pending at once; without a cap that backlog is encoded in a single tick and shows up directly as a tick overrun. The budget counts chunks **actually encoded**, so a tick where most of the desired set is still in flight is not charged for work it did not do. The fixed count is a placeholder for a time budget, which becomes necessary once per-chunk cost varies with LOD. + ### Self-contained payloads (all-air diff) `ChunkMessage::Chunk` carries a `ChunkData` (the sparse, baseline-relative form; see [ADR-0009](adr/0009-baseline-relative-sparse-chunk-persistence.md)). Because the client runs **no** worldgen (the server owns world content; worldgen never runs client-side), it cannot reconstruct a worldgen baseline to diff against. So delivered chunks are diffed against an **all-air baseline** (`Chunk::default()`): the edits become the chunk's full non-air content, and the client materializes each payload against its own all-air `Chunk::default()`. This makes every delivery self-contained, at the cost of not exploiting the deterministic baseline for compression — a compression concern deferred to the LOD/compression pass. ### Client application -The client subscribes with its own `LOAD_RADIUS` (so the server's per-client resident set matches what the client keeps) whenever its center chunk changes. It drains deliveries under a per-frame meshing budget: `ChunkMessage::Chunk` → materialize → `generate_mesh` → `insert_mesh` (skipping empty meshes); `ChunkMessage::Drop` → `remove_mesh`. It **also** evicts chunks outside `LOAD_RADIUS` locally, independent of the server `Drop`, so memory stays bounded even if the server is slow. +The client subscribes with its own `LOAD_RADIUS` (so the server's per-client resident set matches what the client keeps) whenever its center chunk changes. Deliveries are drained under per-frame budgets: a `ChunkMessage::Chunk` is materialized and the position (plus its six neighbours) is queued for meshing, while a `ChunkMessage::Drop` removes the mesh. Meshing itself runs on a worker pool rather than inline, so the client retains chunk voxels after upload; that pipeline is described in [`meshing.md`](meshing.md). The client **also** evicts chunks outside `LOAD_RADIUS` locally, independent of the server `Drop`, so memory stays bounded even if the server is slow. ## Multiplayer