Run a bot load test

botclient is a program that plays a match without people. Each bot says hello, loads, gives orders, pings the server and measures how long every answer takes. Use it to find out what your server and your computer can carry before real players find out for you. It lives with the server's code (server/dedicated/examples/botclient.rs) and is built with the server.

Build

cd server\dedicated
cargo build --profile fast --bins --examples

You get w4-dedicated.exe and examples\botclient.exe in your target folder.

The quickest test

Let the bot client start a private server of its own on this computer, play for a minute and stop it:

botclient --spawn w4-dedicated.exe --map "battle" --bots 8 --secs 60

--spawn copies the map with 12 seats, so up to 12 bots get a seat. More bots than seats are refused with "the game is full", which tests the refusal path but not the game.

Against a server you started yourself

w4-dedicated --map "w4 net 5k" --listen 127.0.0.1:7777 --start-when 4
botclient --addr 127.0.0.1:7777 --bots 4 --secs 60 --delta --orders-per-sec 2

Bots use guest names (Bot001, Bot002, ...). If the server runs with --online, start it with --allow-guests or the bots are turned away.

The flags you will use

Flag What it does
--addr host:port The server to test (default 127.0.0.1:7777).
--bots n, --secs n How many bots and for how long (default 10 and 60).
--orders-per-sec x Orders each bot sends a second: a move order for one random unit of its own.
--delta Ask for delta snapshots. Do this on big maps: full JSON snapshots of 5,000 units were about 24 MB a second for four bots in our test, and bots that could not read that fast were dropped, as a slow player would be.
--bin Use the compact binary codec, as the game does.
--view n With --bin: only ask for units inside a view that holds about n units.
--ramp Add ten bots a second instead of all at once.
--cycle s Each bot drops its connection and comes back about every s seconds (somewhere between half and one and a half times s): a rejoin test.
--tls name, --ca file, --insecure Test a server with TLS (--insecure only for this computer).
--quic addr, --datagrams Test the QUIC transport.
--spawn exe, --map path, --raw-map Start a private server (see above); --raw-map uses the map as it is.
--metrics-port p With --spawn: also read the server's /metrics and print its numbers.

Reading the output

Every ten seconds you get one line, for example

t=  30s clients 4/4 | 0.10 MB/s | 20 snaps/s | steps/s 30.0 | rtt p50/p95/p99 17.8/22.1/24.5 ms | refused 0 errors 0 disc 0
  • clients 4/4: connected and seated out of started.
  • steps/s 30.0: the simulation's speed. Below 30 the server cannot keep up: this is the number to watch.
  • rtt: round trip of a ping. The server answers pings between steps, so a value up to 33 ms is the step, not the network.
  • refused, errors, disc: players turned away, read errors and disconnects. A bot that fell behind and was dropped counts here.

At the end you get a table per bot (kilobytes received, snapshots, decode time, round trips, errors) and totals.

Numbers we measured

AMD Ryzen 7 7700, Windows 11, fast build, bots and server on the same computer over loopback:

  • 8 bots on a 36 unit map: 0.33 MB/s in all, 30.0 steps a second, round trips 12 ms median.
  • Four guest bots with delta snapshots on the 5,000 unit map plus one spectator, a full game to its timeout: all seated, 30 steps a second, and the replay of the finished game plays back to the same result. (scripts/stack-smoke.ps1 runs this whole check.)
  • A server killed and started again 20 times under two bots (scripts/restart-stress.ps1): every restart resumed the match from its checkpoint and both bots were seated again.

Your numbers depend on your computer; the bots use some of its time too, so read them as a floor for the server's headroom, not as a limit.

When something looks wrong

  • steps/s under 30 with few bots: the map or its scripts are heavy; compare with an empty map.
  • Bots dropped one after the other: use --delta or --bin; the snapshots are too big for the client.
  • Everything refused with "the game is full": the map has fewer seats than bots.