Move an army: reference integration starter
In this tutorial a general orders an army to march to a neighboring territory. You will run the example, see which boundary moves the army, and check that a saved, forked, and replayed copy of the run all end in the same state. It is the shortest program that uses a simulation plugin, a domain command, and exact replay together.
Open starter.rs
Browse the reference integration crate
Run it
Section titled “Run it”Install Git and Rust 1.88 or newer, then run:
git clone https://github.com/PeiyuanQi/canwu.gitcd canwucargo run -p canwu-reference-world --example starterThe first build takes a few minutes. The program prints one line:
army_location=3 checkpoint=dd9796a606983d02eafd3495df11b20bb8423cec33f96c0eaddc24e44e4e2de1Territory 3 is Eastwatch, the army’s destination. The checkpoint is a hash of the whole simulation state. The run is deterministic, so running it again prints the same hash.
How it works
Section titled “How it works”View diagram source
flowchart LR
World["demo_scenario()"] --> New["Canwu::new_with_plugins"]
New --> Queue["enqueue_command()"]
Queue --> Advance["advance_canonical(19 h)"]
Advance --> Save["snapshot_json()"]
Save --> Load["load"]
Load --> Fork["fork()"]
Advance --> Replay["replay_from_journal()"]
Load --> Compare["Same checkpoint hash"]
Fork --> Compare
Replay --> Compare
1. Build the world
Section titled “1. Build the world”let (scenario, ids) = demo_scenario()?;let plugin = ReferenceWorldPlugin;let mut canwu = Canwu::new_with_plugins(35, scenario, &[&plugin])?;demo_scenario() returns a Scenario, the starting world, together with the IDs you need later. General Shen commands the First Field Army at Yun Capital (territory 2). The Eastern River Road takes 18 hours from Yun Capital to Eastwatch. 35 is the random seed.
The world model comes from ReferenceWorldPlugin in the canwu-reference-world crate. The plugin stores the world as a domain record and registers the order_movement_v1 command and the boundary system that moves armies. It uses only the public canwu-api crate, so you can replace it with your own world model.
2. Queue the order
Section titled “2. Queue the order”let envelope = order_movement( Issuer::Actor(ids.commander), &MovementCommand { subject: EntityRef::Army(ids.army), destination: ids.eastern_territory, cargo: Vec::new(), },)?.at_time(canwu.time());canwu.enqueue_command( canwu.time(), 0, CommandRequest::new(CommandRequestId::new(1), canwu.revision(), envelope),)?;order_movement wraps the MovementCommand in a CommandEnvelope as a domain command (Command::Plugin) issued by General Shen. at_time(canwu.time()) and canwu.revision() guard against stale input: the request must match the simulation time and state revision when Canwu admits it.
enqueue_command takes a due time, a priority, and the request, and puts the request on canonical ingress: the recorded, ordered input path that replay reads later.
3. Advance time
Section titled “3. Advance time”canwu.advance_canonical(SimDuration::hours(19))?;advance_canonical settles every boundary due in the next 19 hours. A boundary is one atomic settlement step; see Settlement system. This run settles three:
| Boundary | Time | What happens |
|---|---|---|
| 1 | Day 0, 00:00 | The command handler checks the order, reserves the army, and schedules two movement packets: departure now and arrival 18 hours later. |
| 2 | Day 0, 00:00 | The departure packet puts the army in transit. |
| 3 | Day 0, 18:00 | The arrival packet moves the army to Eastwatch. |
4. Save, load, fork, and replay
Section titled “4. Save, load, fork, and replay”let saved = canwu.snapshot_json()?;let loaded = Canwu::from_snapshot_json_with_plugins(&saved, &[&plugin])?;let fork = loaded.fork();let journal = canwu.replay_journal();let replayed = Canwu::replay_from_journal(&[&plugin], &journal)?;snapshot_jsonserializes the state. Loading it requires the same plugins.forkcopies a simulation so the copy can continue with different inputs.replay_from_journalrebuilds the run from its initial scenario and recorded inputs.
The example asserts that all three copies have the original checkpoint hash, then prints the army’s location from the replayed copy.
Try next
Section titled “Try next”Print the event log after advance_canonical:
for event in canwu.events() { println!("{} {} {}", event.timestamp, event.kind.qualified_event_type(), event.summary);}Day 0, 00:00 canwu.reference-world.pending_changed Reserve a reference-world movement subjectDay 0, 00:00 canwu.reference-world.domain_record_updated Apply reference-world movement transitionsDay 0, 00:00 canwu.reference-world.canwu.reference-world.movement_started.v1 Reference-world movement startedDay 0, 18:00 canwu.reference-world.domain_record_updated Apply reference-world movement transitionsDay 0, 18:00 canwu.reference-world.canwu.reference-world.movement_arrived.v1 Reference-world movement arrivedChange SimDuration::hours(19) to SimDuration::hours(17). The example now prints army_location=2: the army is still on the road, and its location stays at Yun Capital until the arrival boundary.
If your game runs in real time, continue with the continuous-time game loop.