Crowd Simulator
What it is and why you’d want it
Section titled “What it is and why you’d want it”Firing one test event tells you a handler works. It doesn’t tell you what your Pack feels like when thirty people find the gravity button at the same time. The Crowd Simulator plays a fake audience against your local bridge: fake viewers with plausible names redeem your events at a steady rate, type viewer text where an event asks for it, and pile up in queues exactly like a real crowd would. You watch the game, the overlay, and the Dashboard audit react.
You get the same loop as a live stream, minus the stream:
mobrule dev crowdThat’s the whole zero-config run. It reads your active manifest, picks events with even weights, invents parameter values that pass your schemas, and fires at 10 redeems a minute until you press Ctrl-C. At the end it prints what it fired and how each invocation settled.
You need a running bridge with your Pack active, and your adapter connected.
The sim is a loopback client like the rest of mobrule dev: it talks to the
bridge’s local API and nothing else, so no Twitch account or cloud setup is
involved anywhere.
What it simulates, and what it skips
Section titled “What it simulates, and what it skips”Every fire is a real invocation. It rides the normal queues, dispatches to
your adapter over MPP, credits a fake viewer by name (your adapter’s
“X used Y” attribution works), and shows up in the Dashboard audit marked
with the sim source, so you can tell a rehearsal from an operator
test-fire.
The sim paces itself like a crowd, too. It honors each Reward’s cooldown on its own clock, and it caps itself at 120 fires a minute no matter what rate you ask for.
It skips the platform’s Twitch half: nothing is charged, matched, paused, or
refunded, because no Redemption exists. It also can’t vote in
Chat-Plays windows. Votes are chat messages,
and the sim has no chat. If your Pack declares a vote-window event, give it a
weight of 0 in your scenario so rehearsals don’t open windows nobody can
vote in.
Viewer text
Section titled “Viewer text”Events that take viewer input get generated text. Where your schema constrains the value (an enum, a number with a range), the sim derives something valid. For free text it draws from your scenario’s corpus, or falls back to a small built-in word list.
The --chaos flag mixes in hostile input on purpose: a ratio of 0.2 makes
roughly one fire in five carry text designed to fail your schema or stress
your handler, the way real chat eventually will. Bounded fields get values
that fail validation (and you get to watch the failure path work). Unbounded
text fields can’t fail validation, so chaos sends adversarial strings
instead: injection attempts, control characters, right-to-left marks. Your
adapter should survive all of them.
Scenario files
Section titled “Scenario files”The zero-config run is uniform and generic. A scenario file makes it feel
like your crowd. It’s a small TOML file, and a Pack can ship one next to
its pack.toml:
[weights]say = 4.0add_bot = 2.0gravity = 1.0fast_monsters = 1.0
[cadence]rate_per_min = 12
[corpus]"say.message" = [ "get behind the barrel lol", "MOON GRAVITY MOON GRAVITY", "imagine dodging",]Run it with:
mobrule dev crowd --scenario crowd.tomlEverything in a scenario is optional, and it only reshapes the run. Weights
skew the event mix (a weight of 0 removes an event, unnamed events keep
1.0). rate_per_min overrides --rate. Corpus entries feed viewer text,
keyed by event name or by event.param when one field needs its own pool.
A usernames list adds extra names to the built-in pool. A scenario never
invents events: a weight naming something absent from the manifest is
ignored.
Deterministic runs and replay
Section titled “Deterministic runs and replay”Pass --seed and the whole run is reproducible: same seed, same scenario,
same sequence of fires.
mobrule dev crowd --seed 42 --duration 60 --record run.jsonlmobrule dev crowd --replay run.jsonl--record writes the run as a timeline: one JSON line per fire, with a
version header up front. --replay executes a timeline exactly as written.
That pair turns a good rehearsal into a regression fixture. Record a run
that exercises the paths you care about, replay it in CI, and assert on the
invocation audit or your pack state afterward. The timeline format is
versioned, and a replay refuses a version it doesn’t know rather than
guessing.
| Flag | What it does |
|---|---|
--rate <n> | Mean fires per minute (default 10, hard cap 120) |
--seed <n> | Fixes the random sequence for a reproducible run |
--chaos <0..1> | Ratio of fires carrying hostile viewer text |
--duration <secs> | Stop after this long instead of on Ctrl-C |
--scenario <path> | Load a scenario TOML |
--record <path> | Write the run as a replayable timeline |
--replay <path> | Execute a recorded timeline |
See also
Section titled “See also”- Defining Events: params schemas and viewer input.
- Pack tutorial: Doom, stage 10: shipping a scenario with a Pack.
- Chat-Plays: the one thing the sim can’t do.
- Pack doctor: catches a
crowd.tomlthat references an event your manifest doesn’t declare.