84e786e531
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>
68 lines
2.5 KiB
GDScript
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)
|