extends SceneTree ## Headless performance probe for the simulation alone -- no transport, no ## rendering. Answers the only question that decides whether the design holds: ## how many ticks per second can one dedicated server sustain per instance, and ## how big does the bullet field get? ## ## godot --headless --path . --script tools/bench.gd ## godot --headless --path . --script tools/bench.gd -- --players 4 --ticks 5400 var players: int = 4 var ticks: int = 3600 func _init() -> void: _parse() print("bench: %d players, %d ticks (%.1f s of play)" % [players, ticks, float(ticks) / float(SimConfig.TICK_RATE)]) _run("boss fight", _make_boss_world()) _run("trash wave", _make_wave_world()) quit(0) func _parse() -> void: var argv := OS.get_cmdline_user_args() var i := 0 while i < argv.size(): match argv[i]: "--players": i += 1 players = int(argv[i]) "--ticks": i += 1 ticks = int(argv[i]) i += 1 func _add_players(world: SimWorld) -> void: for n in players: var p := world.add_player(n + 2, "bench%d" % n) p.pos = Vector2(-200.0 + 100.0 * float(n), 180.0) func _make_boss_world() -> SimWorld: var world := SimWorld.new(1234) _add_players(world) world.spawn_boss(Content.warden()) return world func _make_wave_world() -> SimWorld: var world := SimWorld.new(4321) _add_players(world) for i in 6: world.spawn_enemy(Content.turret(), Vector2(-400.0 + 160.0 * float(i), -220.0), i * 20) for i in 4: world.spawn_enemy(Content.drifter(), Vector2(-240.0 + 160.0 * float(i), -80.0), i * 25) return world func _run(label: String, world: SimWorld) -> void: var peak := 0 var total := 0 var start := Time.get_ticks_usec() for t in ticks: for peer in world.players: var frames: Array[InputFrame] = [InputFrame.make(world.tick + 1, Vector2(cos(float(t) * 0.02), sin(float(t) * 0.031)), float(t) * 0.05, InputFrame.BTN_FIRE)] world.queue_input(peer, frames) world.step() world.drain_events() if world.boss != null: # Sweep hp downwards instead of letting the boss die, so the run # covers every phase including the heaviest one. world.boss.hp = int(float(world.boss.def.max_hp) * (0.95 - 0.9 * float(t) / float(ticks))) peak = maxi(peak, world.pool.live_count) total += world.pool.live_count var elapsed := float(Time.get_ticks_usec() - start) / 1_000_000.0 var per_tick_ms := elapsed / float(ticks) * 1000.0 print(" %-12s %7.3f ms/tick peak %4d bullets avg %4d headroom x%.1f" % [label, per_tick_ms, peak, total / ticks, (1000.0 / float(SimConfig.TICK_RATE)) / maxf(per_tick_ms, 0.0001)])