---
title: "Crowds in a rollback game"
url: https://recursiveai.org/onepath/cookbook/crowd-rollback/
description: "Run a OnePath crowd with a fixed time step, save its state, roll back and resimulate, and check with a state hash that the second run reaches the same bits."
version: "1.1.0"
---

# Crowds in a rollback game

Run a OnePath crowd with a fixed time step, save its state, roll back and resimulate, and check with a state hash that the second run reaches the same bits.

Eight agents cross a 40 × 40 floor in two rows, through each other:

```csharp
using RecursiveAI.OneMath;
using RecursiveAI.OnePath;

var floor = new[]
{
    new Triangle3D(new Vector3D(0, 0, 0), new Vector3D(0, 0, 40), new Vector3D(40, 0, 0)),
    new Triangle3D(new Vector3D(40, 0, 0), new Vector3D(0, 0, 40), new Vector3D(40, 0, 40)),
};
NavMesh level = NavMesh.Build(floor, NavSettings.Humanoid);
Crowd crowd = level.CreateCrowd();

// Eight agents in two rows, each walking to the other side.
for (int i = 0; i < 4; i++)
{
    crowd.Add(new Vector3D(5 + 8 * i, 0, 5), Agent.Humanoid).SetDestination(new Vector3D(35 - 8 * i, 0, 35));
    crowd.Add(new Vector3D(5 + 8 * i, 0, 35), Agent.Humanoid).SetDestination(new Vector3D(35 - 8 * i, 0, 5));
}

for (int i = 0; i < 100; i++) crowd.Step(1.0 / 30);
byte[] saved = crowd.SaveState();                 // frame 100
Console.WriteLine($"frame {crowd.StepCount}: saved {saved.Length} bytes, hash {crowd.StateHash():X16}");

for (int i = 0; i < 200; i++) crowd.Step(1.0 / 30);
ulong ahead = crowd.StateHash();
Console.WriteLine($"frame {crowd.StepCount}: hash {ahead:X16}");

crowd.RestoreState(saved);                        // roll back to frame 100 and simulate again
Console.WriteLine($"restored: frame {crowd.StepCount}, hash {crowd.StateHash():X16}");
for (int i = 0; i < 200; i++) crowd.Step(1.0 / 30);
Console.WriteLine($"frame {crowd.StepCount}: hash {crowd.StateHash():X16}, same as before: {crowd.StateHash() == ahead}");

Console.WriteLine($"{crowd.Count(a => a.ReachedTarget)} of {crowd.Count} agents have arrived");
```

```text
frame 100: saved 3320 bytes, hash E7D9E5CD5E787CD2
frame 300: hash 75F1194801CC93C2
restored: frame 100, hash E7D9E5CD5E787CD2
frame 300: hash 75F1194801CC93C2, same as before: True
4 of 8 agents have arrived
```

## The pieces

- **`crowd.Step(dt)`** takes the time step from you. The crowd has no clock, and nothing in it depends on threads or hash order, so the same steps give the same state on every machine.
- **`SaveState()`** writes the crowd’s state to bytes and **`RestoreState(bytes)`** puts it back, so the next step continues exactly where the saved one left off. The same state writes the same bytes on every platform.
- **`StateHash()`** is a cheap fingerprint to exchange with peers every few frames: a mismatch is a desync.
- `SaveStateWithMesh` also saves a dynamic mesh (rebuilt tiles, obstacles) with the crowd.

ORCA and the corridor follower are in [ORCA solver](https://recursiveai.org/onepath/docs/orca/) and [corridors and following](https://recursiveai.org/onepath/docs/crowds/).
