Tech stack¶
Written 2026-10-27 from the project's own design docs.
What The Crawl is built with, what the vertical slice proves, and what the load tests showed. The stack was decided 2026-10-26. Version facts were checked that day; Bevy and SpacetimeDB both change every few weeks to months, so re-check any version number before relying on it.
Decided¶
The founder's decision, verbatim: "Rust all the way baby."
It was made after the options were laid out: Unreal Engine 5 (which the assistant had recommended for this vision), Bevy, Unity and Godot, with their tradeoffs. Rust everywhere was chosen knowing those tradeoffs.
| Layer | Choice | Status |
|---|---|---|
| Game client | Rust, on Bevy 0.20 | Confirmed 2026-10-26 |
| Server logic and state | SpacetimeDB, with modules written in Rust | Decided |
| Physics and destruction | A separate Rust simulation server (for example using the Rapier physics engine), with SpacetimeDB holding the results | Required by the destruction goal; not built |
| Shared rules | One Rust library of game rules used by the client, the server and the simulation | The assistant's recommendation |
SpacetimeDB is a database that also runs the game's server logic. Game code is uploaded into it as a "module", and clients call its functions ("reducers") and subscribe to the rows they care about. Every change made in a database is one transaction.
Readings (the assistant's analysis)¶
- Bevy is pre-1.0 and releases often, with breaking changes. Versions 0.18, 0.19 and 0.20 came out in January, June and October 2026, each with a migration guide. Either migrate with every release or pin a version and upgrade on purpose.
- Destruction gets built from scratch. Unreal's built-in destruction tools aren't available. The Finals-level destruction means building fracturing, structural integrity and network sync on top of Rapier or a custom solver. It was already the aim-high feature; this choice makes it harder.
- Physics can't live inside the database. SpacetimeDB runs each database's logic on one thread, transaction by transaction. Per-tick physics over many bodies doesn't fit that, so it runs on its own multi-core server and SpacetimeDB stores the durable results.
- Connecting Bevy to SpacetimeDB: SpacetimeDB ships an official Bevy template. A third-party crate,
bevy_stdb, wraps the client SDK for Bevy; it should be evaluated before depending on it. The SDK crate's name differs between versions of SpacetimeDB's documentation, so match it to the server version. - Tooling: Bevy's editor tools are far behind Unreal's or Unity's (from memory, not checked). Levels, animation and assets will lean on outside tools such as Blender. For a world generated from a seed, that matters less than usual.
- What it buys: one language end to end. One rules library for client, server and simulation; the same types everywhere; and Rust's safety and speed throughout. It suits a data-driven ability system.
- Licence note (checked 2026-10-26): the SpacetimeDB server-module crate is Apache-2.0, but the Rust client SDK is under the Business Source License (BSL). A shipped game client would link BSL code. That appears to be allowed, but it needs a lawyer's check before shipping.
The vertical slice (2026-10-26)¶
| Part | What it is |
|---|---|
crawl_rules |
The season, production and death rules as plain Rust, with a test per rule |
crawl_world |
Seeded floor generation (Floor 1's corridors, rooms, safe rooms, stairwells) and collision, shared by everything |
crawl_server |
The SpacetimeDB module: tables, reducers and the floor-collapse clock |
crawl_combat |
Earth Weaponry, the damage model, objects with weight, the five attributes, conditions and exposure. Not connected to the server or client, by choice |
crawl_net |
Generated client bindings plus connection helpers |
crawl_client |
The Bevy client: first-person or over-the-shoulder, the floor streamed in, other crawlers live, a HUD and a show ticker |
crawl_bots |
A crowd of headless bots for load tests |
The project's code rules:
- Game rules live in one library and nowhere else. The server, client and bots call it; they never re-decide a rule. Every rule gets a test named after its rule ID. Where the design is silent, the code marks an
ASSUMPTIONand lists it for the founder instead of quietly picking. - Geometry is seeded and deterministic, so every part agrees on the map without sending it over the wire.
- The server checks; the client never decides. Anything a cheat could fake (positions, deaths, loot, eligibility) is decided or validated on the server.
What's proven, and how¶
| Claim | How it was checked |
|---|---|
| The rules match the design | 28 tests, one or more per rule. Two deliberate breaks each made tests fail |
| The server can't lose track of a character at a collapse | A randomized test over 200 seasons and more than 1,000 collapses, shown to fail when one index key is removed |
| Floor generation is deterministic and walls hold | 7 tests; the wall test fails with collision disabled |
| The server enforces the rules live | A scripted season walk on a local server, read back from the database. The clock collapsed floors on its own |
| The server rejects cheats | A move into a wall and a 200 m jump were both rejected; a legal move went through |
| The client works | Scripted runs with screenshots checked by eye: walking, server and client positions matching, and taking the stairs through a collapse, twice |
| Combat matches its design | 38 tests; each rule was broken on purpose and its test failed |
Not proven: nobody has played it. Gamepad input is untested. A move through a thin wall between two position updates isn't caught yet; server-side movement authority is on the to-do list.
Load tests¶
All runs were on one Windows 11 PC with 24 logical cores, running both the server and the bots, so bot CPU competed with the server and the numbers are conservative. Bots sent 10 position updates per second each. Measured 2026-10-26. Each figure is a single run (n=1), not an average.
| Setup | Result |
|---|---|
| Everyone sees everyone (every move sent to every bot) | About 200–300 players per shared space. The limit was the fan-out of about 0.4 million updates per second, not the writes |
| Writes only (each bot watches only itself) | 400 bots, 4,000 validated moves a second, all committed, about 1.3 ms mean round trip |
| Each bot sees only nearby 64 m regions (interest management) | About 2,000 moving players on one Floor 1 database. Round trips were 1.9–3.7 ms from 800 to 1,200 bots and 370–384 ms at 2,000; at 3,000 only 52% of moves committed |
The finding that shaped the code: how a client subscribes matters as much as how many clients there are. Subscriptions written as a range, or across several columns, saturated the database's single thread at a few hundred subscribers, even though the server had idle cores. Subscriptions that test equality on one indexed column scaled. So the slice packs production, floor and region into a single number, region_key, and clients subscribe to the nine region keys around them. The explanation (that the database looks up single-column equality subscriptions by key and checks the others one by one) is inferred from these runs, not from SpacetimeDB's documentation.
Spawn density matters as much. At a 120 m spawn radius, 400 bots each saw 60–90 others and the server saturated. At 800 m they saw about 5. The spawn distance X (Worldspace, W2) sets the crowd size near spawn, and with it the load.
Levers beyond ~2,000 per floor: a lower update rate (5 per second should roughly double capacity; untested), splitting Floor 1 across several databases by region (the books' Floor 1 is already split into regions that can't reach each other), or a bigger server.
Not yet measured: hot spots (crowds at a stairwell or safe room), a real client's rendering cost at that update rate, and combat traffic.
These results matched an earlier back-of-the-envelope estimate made before any code existed: that crowd fan-out, which grows with the square of the crowd's size, runs out before transaction throughput does. That same estimate judged SpacetimeDB a strong fit for tick-based, tile-grid online games, plausible at hundreds of players per shard for continuous-movement games, and suitable for full physics only alongside a separate physics server.
Open¶
- Bevy version policy: follow each release, or pin and upgrade on purpose. The code currently pins 0.20.
See also: Rust, Bevy, and Simulation you can trust for the general principles behind the code rules.