The simulation and multiplayer
Rules live in one place
The game's rules, movement, fights, pathing, fog of war, teams, training and building, live in a single Rust crate, aqua4-sim, that knows nothing about graphics, sound or the interface. It runs at a fixed 30 steps a second. Everything else, rendering, the interface, audio, only reads its state and sends commands to change it: a click on the command card does not move a unit, it sends a Move command.
Online play depends on this split. If the same commands in the same order give the same game on every machine, players can send each other commands instead of state. A dedicated server can run the same simulation as the authority.
What is in it today
- Movement and a path search over eight neighbours where cliffs and buildings block,
- melee reach and wind-up before a hit lands,
- fog of war per player, drawn in tiers,
- teams: allies never fight,
- training units from buildings and building with a worker,
- heroes with a level and a mana bar (no experience or spending yet),
- a script host (the Go sandbox) that runs beside it,
- hold, patrol, repair, upgrade, load and unload orders.
Multiplayer is a rule
Every feature has to work over a network later. A client never reads state it should not see. Chat and interface effects are messages on their own channel. Scripts run in the simulation, so every machine agrees. A feature that cannot be played over a network is not finished. The networking itself (a lobby, joining, dedicated servers) is not built yet.
Versus Warcraft III
Warcraft III uses the same lockstep idea, and it is proven. W4 keeps it and runs scripts in a sandbox. The simulation is separate from the renderer, so a server without graphics can be built from the same code.
What a server needs from it
The simulation has the pieces a dedicated server and a client need, all deterministic:
- What changed:
Sim::changed_since(step)lists the units that changed in the last 30 steps, so a server sends deltas instead of scanning every unit (0.088 ms a step at 30,000 idle units on a Ryzen 7 7700). Asking from an older step gives every unit, a full resync. - Presentation events for shots, stat changes, upgrades, loading and unloading, tagged with where and for whom (
Sim::event_info). They are not part of the game state. - Orders: Hold, Patrol, Repair, Upgrade, Load and Unload are commands like Move, with the rules inside the simulation.
- A state hash:
Sim::state_hash()is a short number for everything that decides what happens next. Two machines that were given the same commands have the same hash on every step, which is how a desync is found. - A bounded path search: a step may spend 30,000 tiles of search; units past that stand and wait their turn, and units of one group order near each other share a search. One order for 1,000 units dropped from 3.3 s to 6.5 ms (Ryzen 7 7700, fast build, empty 1024 x 1024 map).
- A build for browsers: with
--no-default-featuresthe simulation compiles forwasm32-unknown-unknown, without the script host. It has not been run in a browser yet.