docs(workspace): add ADR-0010 for net crate and async runtime

This commit is contained in:
Serkyo 2026-07-12 00:28:45 +02:00
parent f0d2269e8f
commit aaff38c784
2 changed files with 30 additions and 2 deletions

View file

@ -20,15 +20,16 @@ The canonical game-*design* specification (intent, world rules, gameplay behavio
## Workspace layout ## Workspace layout
Cargo workspace (resolver = "3", edition 2024) with four crates under `crates/`: Cargo workspace (resolver = "3", edition 2024) with six crates under `crates/`:
- `client`: binary. Windowed application using `winit` 0.30 (`ApplicationHandler` pattern, `ControlFlow::Poll`). Also pulls in `image`. Player-facing app titled "Synvael"; handles input, windowing, and drives the renderer. - `client`: binary. Windowed application using `winit` 0.30 (`ApplicationHandler` pattern, `ControlFlow::Poll`). Also pulls in `image`. Player-facing app titled "Synvael"; handles input, windowing, and drives the renderer.
- `server`: binary. Authoritative game simulation (voxel world, combat, players). Used both for dedicated multiplayer hosts and as the simulation backend for single-player. - `server`: binary. Authoritative game simulation (voxel world, combat, players). Used both for dedicated multiplayer hosts and as the simulation backend for single-player.
- `renderer`: library. Voxel/scene rendering on Vulkan via `ash`, decoupled from windowing so it can be driven by `client`. - `renderer`: library. Voxel/scene rendering on Vulkan via `ash`, decoupled from windowing so it can be driven by `client`.
- `shared`: library. Types and protocol shared between `client` and `server` (world/voxel data, network messages, combat primitives). Stays lean and dep-light; no `mlua`, no rendering, no engine internals. - `shared`: library. Types and protocol shared between `client` and `server` (world/voxel data, network messages, combat primitives). Stays lean and dep-light; no `mlua`, no rendering, no engine internals.
- `scripting`: library. Lua modding API and bindings (owns the `mlua` dependency, `UserData` wrappers around `shared` types, API table registration, mod loader). Both `client` and `server` depend on it. - `scripting`: library. Lua modding API and bindings (owns the `mlua` dependency, `UserData` wrappers around `shared` types, API table registration, mod loader). Both `client` and `server` depend on it.
- `net`: library. QUIC transport, connection lifecycle, and wire framing for the client↔server protocol; owns the async runtime (`tokio`) and the `quinn`/`rustls` dependencies. Both `client` and `server` depend on it. See [ADR-0010](docs/adr/0010-net-crate-async-runtime.md).
When adding code, keep the boundary tight: protocol/data types and game-rule primitives go in `shared`; Lua API surface and `mlua` integration in `scripting`; GPU/draw code in `renderer`; only input, windowing, and presentation glue live in `client`. Avoid growing `client` with simulation logic since it must work identically against either a local or remote `server`. When adding code, keep the boundary tight: protocol/data types and game-rule primitives go in `shared`; Lua API surface and `mlua` integration in `scripting`; GPU/draw code in `renderer`; transport and connection code in `net` (protocol message *types* stay in `shared`); only input, windowing, and presentation glue live in `client`. Avoid growing `client` with simulation logic since it must work identically against either a local or remote `server`.
## Modding API (Lua): dogfooded ## Modding API (Lua): dogfooded

View file

@ -0,0 +1,27 @@
# 0010. Dedicated `net` crate with a confined async runtime
- **Status:** Accepted
- **Date:** 2026-07-12
## Context
The network transport is QUIC via `quinn` which is an asynchronous library built on the `tokio` runtime and requires TLS 1.3 through `rustls`. These are heavy dependencies that pull an entire async ecosystem into the build.
The `shared` crate is mandated to stay lean and dependency-light: it is the protocol/data layer, holding pure serde message types with no async, rendering, or engine internals. Placing transport code in `shared` would violate that mandate and force every consumer of the protocol types to compile `tokio` and `rustls`. At the same time, the simulation is synchronous: the `server` runs a synchronous `bevy_ecs` loop and the `client` runs a synchronous `winit` event loop. Introducing an async runtime must not turn those loops async or leak `tokio` throughout the workspace.
## Decision
Transport lives in a dedicated `net` crate, separate from `shared`, and the `tokio` runtime is confined to it.
- `net` owns the `quinn`, `tokio`, and `rustls` dependencies, plus the QUIC endpoints, connection lifecycle, and wire framing.
- `shared` continues to hold only the protocol message *types* (serde, no async).
- Both `client` and `server` depend on `net`.
- The async runtime is bridged to the synchronous simulation over channels (`crossbeam-channel`), consistent with the message-passing concurrency model in `AGENTS.md`. The synchronous loops never `.await`; they send and receive protocol messages across the boundary.
## Consequences
- `shared` stays lean: consumers of the protocol types do not compile the async stack.
- The async surface is quarantined. Only `net` deals with `tokio`, keeping the `server` and `client` loops synchronous and unchanged.
- The workspace now has six crates. `net` sits between `shared` (types it carries) and the two binaries (which drive it).
- The channel bridge is an explicit boundary that must be maintained: work crossing between the async runtime and the sync simulation flows through channels, never through shared async state or by making the sim async.
- A crypto provider backend is required by `rustls`; the transport code must install one before building QUIC configuration.