Stratum is an experimental multiplayer variable turn-based voxel engine. Nothing moves on its own: a fireball hangs in the air, a falling tree stops mid-topple, a fire waits to burn — until a player acts, and then the whole world advances one tick together. Every rule is decided on the server; the client only draws.
Between actions nothing happens. Projectiles, falling debris, burning logs and cooldowns are all frozen mid-state. Walk away from the keyboard and the world waits for you — and for everyone else.
A step, a cast, a use. When a player acts, the server advances one tick and every live thing moves its share: the fireball flies its 300 voxels, the tree falls a little further, the fire eats a little more. Turns have no fixed length in seconds — a quiet world sits still, a busy one ticks every 200 ms.
There is no turn order. Everyone acts whenever they like; players who act inside the same 200 ms join the same tick. The result plays like real time when the room is busy and like a turn-based game when it isn't — and the client replays every tick at the same pace regardless of how they arrived.
Owns the clock, and the clock is stopped by default. Time advances one action tick when any player acts — at most once per 200 ms however many act at once, so a crowd can't make the world run faster than one person. Wren scripts define actors, projectile types, skills and what fire does; the server carves the world, resolves hits, and tells witnesses what happened.
Zig, GLFW and OpenGL. Raymarches the world and every actor directly from voxel DAGs — no meshes. Paces incoming facts by their tick so a burst replays at nominal speed, predicts nothing, and infers nothing from assets. Runs on Linux and Windows; keyboard, mouse and gamepad.
One hash-DAG octree per 2048³ cell, 4×4×4 voxel leaves deduplicated across the whole world. Trees, walls and props are baked into the same tree and indexed by a small registry, so carving, felling and burning are ordinary DAG edits that server and client apply identically.
The server holds the only copy of the world that matters. Players send intents — move here, cast this, use that — and the server answers with facts: a player moved, a projectile appeared, a tree lost its top. It never tells a client what to compute; it tells it what happened, stamped with the tick it happened on. Facts are LZ4-compressed JSON on a netstring stream; world chunks and the registry of destructible placements go out on a second channel so a 30 MB cell never queues behind a step.
Validated first: range, line of sight, cooldown, rage. A rejected cast costs the caster one error fact and nobody else anything. An accepted one advances the tick.
Advances every live thing one tick: projectile sweeps, debris on its curves, fire on its subjects, monster AI on its detection timers. Carves the world. Decides who witnessed what.
Each client gets only what is inside its vicinity — a known set the server diffs per tick — capped at what a client can draw, nearest first. Nothing is ever sent twice.
| Stage | What happens |
|---|---|
| Governed tick | The first accepted action after 200 ms opens a new tick; every action inside the same 200 ms joins it. So the tick rate is bounded by the clock, not by the player count, and a hundred players in one square still produce five ticks a second at most. |
| Projectile sweep | Each live projectile advances its governed speed along a deterministic path and is swept as a sphere against coarse octree nodes of the world — first solid voxel in the first touched node is the hit — and against actor bounding boxes. Position history a few ticks deep lets a shot be judged against where things were when the caster saw them. |
| Carve and settle | A hit carves a sphere out of the instance it struck — never the terrain beneath — then finds what the carve left disconnected. The largest pieces become rigid bodies with a motion curve; the rest is reported as dust. A tall piece severed at the base topples about its bottom edge, away from the blast. |
| Fire | Two axes per subject: what it emits on contact, and whether it is burning. A fire-tagged hit rolls the target's flammability; a burning piece consumes 3 % of itself per tick at rest and publishes its shrink every five. Fire moves by contact only — nothing spreads by adjacency. |
| Interest management | One vicinity radius for everything. Per witness the server keeps a known set of actors, projectiles and pieces and emits appear / disappear as it changes; projectile facts are scoped by the projectile's own position, so its end is always delivered to whoever can see where it ended. |
| World edits | Three operations — delete a box, unsert a shape, insert a shape — applied to the server's DAG and sent as the same three operations to each witness holding that cell. A damaged tree's remaining geometry crosses the wire once; the two copies stay byte-identical. |
| Scripts | Actors, projectile types, skills and objects are Wren scripts with hot reload. A type declares its light, its emissions, its flammability; the engine stamps those into facts. The client never reads a script. |
Drawing. It raymarches the world and every actor straight from voxel DAGs, replays facts at the pace of their ticks, applies the same three world-edit operations, evaluates motion curves in closed form, and infers nothing from a model or a name. It has no simulation of its own to disagree with the server about.
The demo is an isometric fantasy town and its surroundings: players, guards, a forge, monsters that see and hear you, and trees that come apart when a fireball hits them. It exists to exercise the engine end to end — login and character select, streaming chunks, chat over IRC, skills and cooldowns, combat, destruction — with real assets and a real server.
Everything is in development. The engine is not packaged for players yet; what you can run today is the client against your own server.
Each of these replaced something that looked reasonable and wasn't.
| Rule | Why |
|---|---|
| Time is the tick | The world advances only on actions, at most once per 200 ms no matter how many players act. Physics, fire and cooldowns count ticks, never seconds — so a paused world is exactly paused, a busy one doesn't run faster, and a projectile that lingers in the air for an hour while nobody plays is the engine being honest, not a bug. |
| The client infers nothing | Whether a thing smokes, burns or glows arrives as data on its own facts. Keying the fireball's trail on "is a projectile" once put a trail on arrows and none on a burning knight. |
| Never expire on a clock | A projectile or piece is dropped only when the server says so, or when the tick has run past its maximum flight — and that case is logged as a server bug, because it is one. |
| Overflow degrades, never fails | Sixty-four actor slots and thirty-two lights. When a town square exceeds them, the lowest-ranked go unrendered and the log says so; the 65th actor used to exit the process, which handed every crowd a kill switch. |
| Edits are the same ops on both sides | Delete a box, unsert a shape, insert a shape. A damaged tree's remaining geometry is sent once; the client applies the same three operations the server did, and the two copies stay byte-identical. |
| Extents are content, not containers | A row's bounding box is its voxels' tight extent. Recording the element's power-of-two cube instead once deleted a 512³ block of ground on a tree's second hit. |
| Motion is described, not streamed | A falling limb gets one closed-form curve in tick units — linear, ballistic, or a topple about a hinge — evaluated identically everywhere. Facts that arrive before the curve ends queue behind it, so a flight resolved in one server pass still plays out. |
| Presentation is the server's to project | Emission (what a thing does to what it strikes) and burning (what is happening to it) are separate axes; both project onto one fx field and a glow. The client draws the projection and can't tell the axes apart — by design. |
Precompiled builds of the client, each with a working configuration that points at the demo server. Unpack, run, log in.
# Linux
tar xzf stratum-linux-x86_64.tar.gz
cd stratum && ./run.sh
# Windows
unzip stratum-windows-x86_64.zip, then run stratum.exe
On first login the client fetches the world's asset bundle — models, sounds, skills, the default action map — into .cache/<world>/ beside the binary, and streams the world from there on. Keyboard and mouse or a gamepad; rebind anything in .cache/<world>/bindings.json.