The harness is everything agents need to play and be measured: bots, the interface, the perception layer, headless matches, the recorder, ratings and the virtual clock. What is built so far is in the changelog.
Bots created per match#
When a match forms, the server creates bot characters with the right loadouts and seats an agent on each. The bots have no network connection; they are sessions inside the server, driven by the project's module. Everything bots need is reachable from a module, without changing AzerothCore itself.
Movement. A bot moves as a player's client does: it holds keys and reports its positions, and the server agrees with them. Walls, pillars, closed doors and steep ground stop it.
One interface, two modes#
| In-process | Out-of-process | |
|---|---|---|
| Where the agent runs | Inside the server | A separate program on the same machine |
| How messages travel | An in-memory queue | A local socket |
| Parser, perception, validation, delays | The same code | The same code |
Only the pipe differs, so the two modes can't drift apart. Out-of-process agents never get a game connection: a real client connection would carry exact positions and everything else the perception layer exists to hide. The interface is designed for both modes from the start, but only in-process is built so far: out-of-process play is built when it serves play with humans or a stronger agent, not as a gate on training.
Each observation is a full snapshot of what the agent knows plus the events since the last one, a few kilobytes in 2v2, so any message can be read on its own. Actions are validated and sent as ordinary client packets, so the server treats a bot exactly like a player.
The recorder#
Every match is kept, in a versioned format that future methods can read:
- each seat's full packet stream in both directions, stamped with game time: the ground truth;
- the observations and actions as sent;
- match events: connects, timeouts, faults, phase changes;
- a sealed log of the true game state, written only after the match ends and unreadable by any agent, used by the parity tests and by monitors that look for bugs.
The server's own dice aren't seeded, so a match can't be re-simulated. What can be replayed is every decision: feed a seat's recorded observations to the same agent version and it must choose the same actions. And every observation can be rebuilt from the packets, so a future perception layer can be run over old matches.
The virtual clock#
The virtual clock runs the real server faster than real time. Almost all of the server's game logic advances by each tick's elapsed time rather than by reading a clock, so the server can run on a lockstep clock: one fixed step of game time per tick, as fast as the CPU allows.
- Speed, the server's side measured: a tick costs the server about 37 microseconds on average during a small arena match, so a 3 to 4 ms step could run about 12 to 100 times real time, most likely 35 to 90 times, before the agents' own compute and the coordination with them (how fast the server can run). Several arenas can run at once, up to the machine's cores.
- Bots only. A human's client moves in its own time, so virtual-clock matches are bot-only; humans play on a real-time server.
- A small patch. The loop and a handful of clock reads in combat code can't be reached from a module, so the clock is a small AzerothCore patch carried by the project, behind a switch that is off on the server humans play on; a check fails if it is ever on there. A barrier holds the world at each decision point until the agents have answered, so a slow agent can't fall behind a fast clock.