Files
Grovewright/game/tests/test_two_instances.gd
T
Steven Wroblewski acce1ed435 Zwei Instanzen im Gleichschritt
Ein Test startet einen zweiten Godot-Prozess als Gast. Der Gast tritt
über das Netzwerk bei und schlägt Stein, beide Welten enden im selben
Zustand, ohne Abgleich unterwegs. Damit ist die Prüffrage von
Meilenstein 1 Teil der automatischen Prüfungen.

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

66 lines
2.8 KiB
GDScript

extends TestCase
## What milestone 1 promises: two instances of the game, each in a process of
## its own, stay in lockstep. This process is the host; a second Godot
## process plays a guest with tools/guest_bot.gd.
## The tick at which the host stops and both compare their worlds.
const UNTIL := 150
## Milliseconds the test waits for the second instance.
const PATIENCE_MSEC := 30000
func test_a_guest_in_another_process_stays_in_lockstep() -> void:
var directory := TestFiles.temporary_dir("two-instances")
var host: Node = load("res://main.tscn").instantiate()
host.storage_dir = directory.path_join("host")
host.command_line = PackedStringArray()
tree.root.add_child(host)
host.options.port = 0
host.listen_address = "127.0.0.1"
check(host.play(true), "the world opens")
var report_path := directory.path_join("guest.json")
var arguments := PackedStringArray([
"--headless",
"--path", ProjectSettings.globalize_path("res://"),
"--log-file", ProjectSettings.globalize_path(directory.path_join("guest.log")),
"-s", "res://tools/guest_bot.gd",
"--",
"--join=127.0.0.1:%d" % host.link.link.local_port(),
"--storage=" + directory.path_join("guest"),
"--until=%d" % UNTIL,
"--out=" + report_path,
])
var process := OS.create_process(OS.get_executable_path(), arguments)
check(process > 0, "the second instance starts")
var started := Time.get_ticks_msec()
while OS.is_process_running(process) and Time.get_ticks_msec() - started < PATIENCE_MSEC:
if host.session.guest_count() > 0:
# Once the guest is there, the test runs the host's ticks itself,
# one per frame, instead of twenty a second, and stops at UNTIL.
# A guest's arrival ends a pause, so the clock is stopped here.
host.clock.paused = true
if host.session.tick() < UNTIL:
host._run_tick()
await tree.process_frame
if OS.is_process_running(process):
OS.kill(process)
check(false, "the second instance did not finish in time")
var report: Variant = JSON.parse_string(FileAccess.get_file_as_string(report_path))
check(report is Dictionary and not report.has("error"), "the guest reports: %s" % [report])
if report is Dictionary and not report.has("error"):
check_eq(int(report["tick"]), UNTIL, "the guest stopped where the host stopped")
check_eq(report["hash"], host.session.state_hash_hex(), "both worlds are the same")
check_eq(int(report["player"]), 1, "the guest is player 1")
check_eq(int(report["stones"]), 1, "the guest broke the rock")
check_eq(int(report["resyncs"]), 0, "without a divergence on the way")
var rock: Vector2i = host.session.spawn_point() + Vector2i(7, -2)
check_eq(host.session.terrain_at(rock.x, rock.y), GwSession.TERRAIN_GROUND, "the rock is gone in the host's world too")
check_eq(host.session.player_name(1), "Bot", "the host knows the guest")
tree.root.remove_child(host)
host.link.link.close()
host.free()
TestFiles.remove_dir(directory)