Files
Grovewright/game/tools/guest_bot.gd
Steven Wroblewski 84e786e531 Zwei-Instanzen-Test unabhängig von der Startzeit, Bot endet immer
Die Uhr des Hosts wartet jetzt auf den Gast. So kann ein langsamer Start
des zweiten Prozesses die Takte bis zum Vergleich nicht mehr verbrauchen.
Der Bot endet auch dann, wenn er seinen Bericht nicht schreiben kann,
und überlebt den Test nie. Ein leeres --storage= führt ihn nicht mehr in
den Ordner des Spielers. Ein neuer Test prüft, dass der Bot ohne
schreibbaren Bericht mit Exit-Code 1 endet.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-10-01 23:24:10 +02:00

68 lines
2.5 KiB
GDScript

extends SceneTree
## A second instance of the game for the test of two instances. It starts the
## real main scene as a guest, hits the rock beside the spawn clearing until
## it has a stone, follows the host until the host stops at a tick, and
## writes what its world looks like then as JSON.
##
## The test of two instances starts it; by hand:
## godot --headless --path game -s res://tools/guest_bot.gd -- --join=ADDRESS
## --storage=FOLDER --until=TICK --out=FILE
## Milliseconds the bot waits for everything before it gives up.
const PATIENCE_MSEC := 20000
func _initialize() -> void:
_run.call_deferred()
func _run() -> void:
var settings := {}
for argument in OS.get_cmdline_user_args():
settings[argument.get_slice("=", 0)] = argument.get_slice("=", 1)
var until := int(settings.get("--until", "0"))
var report := {"error": "the host did not answer"}
var main: Node = load("res://main.tscn").instantiate()
# Never the player's own folder, even for an empty --storage=.
var storage: String = settings.get("--storage", "")
main.storage_dir = storage if not storage.is_empty() else "user://bot"
main.command_line = PackedStringArray(["--join=" + settings.get("--join", ""), "--name=Bot"])
root.add_child(main)
var started := Time.get_ticks_msec()
while Time.get_ticks_msec() - started < PATIENCE_MSEC:
await process_frame
var session: GwSession = main.session
if session == null:
report = {"error": "the session ended: %s" % main.menu.status()}
break
if session.local_player() < 0:
continue
var rock := session.spawn_point() + Vector2i(7, -2)
if session.item_count(GwSession.ITEM_STONE) == 0:
main.dig_at(rock)
if session.tick() == until and session.pending_ticks() == 0:
report = {
"tick": session.tick(),
"hash": session.state_hash_hex(),
"player": session.local_player(),
"stones": session.item_count(GwSession.ITEM_STONE),
"resyncs": main.resyncs,
}
break
# The bot ends in every case, even if the report cannot be written, so that
# it never outlives the test that started it.
var path: String = settings.get("--out", "user://bot.json")
var file := FileAccess.open(path, FileAccess.WRITE)
if file == null:
push_error("the report cannot be written to %s: %s" % [path, error_string(FileAccess.get_open_error())])
else:
file.store_string(JSON.stringify(report))
file.close()
main.notification(Node.NOTIFICATION_WM_CLOSE_REQUEST)
root.remove_child(main)
main.free()
quit(0 if file != null and not report.has("error") else 1)