Zone tick on a big continent: idle creatures far from players sleep (2026-10-11)

Issue #93: with one player in Alizar (world 51, 24,873 creature spawns) the zone tick's p95 was several hundred milliseconds on the live server, because every creature of the continent ran its aggro scan and idle AI (wandering, patrols) on each idle tick, wherever it was. Creatures now sleep unless a player is within 200 units (the players' view range); see Zone.CreatureActivity.

Method

scripts/bench_zone_tick.exs starts a real zone instance with all of the world's spawns, suspends its own ticking, and calls its tick handler directly, in order, sleeping 250 ms between calls as the zone does (wander and patrol timers are clock based). It times the whole tick handler (creature AI, pets, scripts, auras, regeneration). The "all awake" and "range 200" cases run in the same process, alternating order (all awake, 200, 200, all awake), so machine load hits both alike.

cd apps/bezgelor_world
MIX_ENV=test BENCH_JSON=out.json ../../scripts/limited.sh mix run ../../scripts/bench_zone_tick.exs

240 ticks (one minute) per case. Idle ticks are every fourth tick (the 1 s idle step). Machine: shared 20-core x86_64 host, 4 Erlang schedulers. Times in milliseconds. The raw numbers are in zone-tick-alizar-2026-10-11.json.

Results

Case all ticks p50 all ticks p95 idle ticks p50 idle ticks p95 max
no player 4.7 9.2 4.6 10.7 93.1
one player, all awake (before) 6.1 / 5.7 49.1 / 42.3 43.6 / 38.3 78.7 / 80.4 453.3 / 195.8
one player, range 200 (after) 4.9 / 4.3 10.7 / 9.0 8.8 / 8.0 13.3 / 10.3 40.5 / 22.0

(Two runs per case, separated by "/".)

The tick's p95 with a player went from about 42-49 ms to about 9-11 ms, the idle tick from about 40 ms to about 8 ms, and the worst tick from 196-453 ms to 22-41 ms. What remains is walking the creature table once per tick (the no-player cost). The live server's tick also carries packet and broadcast work that the benchmark does not, which is why the issue saw about 400 ms there.

Profile before

eprof over 8 idle ticks with one player: the time was spent computing each wandering creature's current position (Movement.path_length, Vector3.distance) and its aggro scan, for about 25,000 creatures per idle tick.