extends SceneTree var _checks := 0 var _failures := 0 var _clock: Variant var _state: Variant var _runtime: Variant func _initialize() -> void: call_deferred("_run") func _run() -> void: print("S0: runtime integration") _test_tick_command_execution() _test_starter_world_layers() _test_paused_command_execution() _test_runtime_snapshot_restore() _test_runtime_save_round_trip() _test_twenty_four_hour_determinism() _cleanup_runtime() if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) func _reset_runtime(seed := 20260827) -> void: _cleanup_runtime() _clock = load("res://autoload/game_clock.gd").new() _state = load("res://autoload/game_state.gd").new() _runtime = load("res://autoload/simulation_runtime.gd").new() _clock.reset(1, 6, 0) _state.reset() _runtime.initialize(_clock, _state, seed) func _cleanup_runtime() -> void: if _runtime != null: _runtime.free() if _clock != null: _clock.free() if _state != null: _state.free() _runtime = null _clock = null _state = null func _test_tick_command_execution() -> void: _reset_runtime() var initial_gold: int = _state.get_resource(&"gold") var result = _runtime.submit_command( &"spend_resources", {&"cost": {&"gold": 100}}, &"player", &"", 50, _clock.absolute_minute + 5, &"integration.spend.future" ) _expect(result.is_success(), "future resource command enters the queue") _clock.advance_minutes(4) _expect(_state.get_resource(&"gold") == initial_gold, "future command does not execute early") _clock.advance_one_minute() _expect(_state.get_resource(&"gold") == initial_gold - 100, "command executes on its scheduled minute") var processed = _runtime.command_queue.get_result_by_idempotency_key(&"integration.spend.future") _expect(processed != null and processed.is_success(), "processed result remains available by idempotency key") func _test_starter_world_layers() -> void: _reset_runtime() _expect(_runtime.definition_registry.facility_count() == 23, "Runtime loads baseline sixteen plus stairs, the infirmary and premium room/dormitory and chapel") _expect(_runtime.definition_registry.has_facility(&"chapel"), "Runtime exposes the original chapel definition") var front_start := Vector3i(-2, 0, WorldService.LAYER_FRONT) var back_goal := Vector3i(2, 0, WorldService.LAYER_BACK) var guest_path: Array[Vector3i] = _runtime.path_service.find_path( front_start, back_goal, [WorldService.LAYER_FRONT, WorldService.LAYER_BACK] ) _expect(guest_path.is_empty(), "guest access cannot cross the FRONT/BACK staff door") var staff_path: Array[Vector3i] = _runtime.path_service.find_path( front_start, back_goal, [WorldService.LAYER_FRONT, WorldService.LAYER_BACK], [&"staff"] ) _expect(not staff_path.is_empty(), "staff access crosses the explicit FRONT/BACK connector") func _test_paused_command_execution() -> void: _reset_runtime() _clock.set_paused(true) var minute_before: int = _clock.absolute_minute var result = _runtime.submit_command( &"change_resources", {&"deltas": {&"faith": 7}}, &"player", &"", 0, -1, &"integration.paused.faith" ) _expect(result.is_success() and _state.get_resource(&"faith") == 7, "current-minute command can complete while paused") _expect(_clock.absolute_minute == minute_before, "paused command does not advance simulation time") var duplicate = _runtime.submit_command( &"change_resources", {&"deltas": {&"faith": 7}}, &"player", &"", 0, -1, &"integration.paused.faith" ) _expect(duplicate.code == CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY, "duplicate idempotency key cannot apply twice") func _test_runtime_snapshot_restore() -> void: _reset_runtime(77) var authority_rng = _runtime.get_rng_stream(&"authority") for _index in range(9): authority_rng.next_u32() _runtime.submit_command( &"spend_resources", {&"cost": {&"materials": 3}}, &"player", &"", 0, _clock.absolute_minute + 30, &"integration.pending.materials" ) var snapshot: Dictionary = _runtime.create_snapshot() var expected_digest := SimulationDigest.compute(snapshot) var expected_rng_tail: int = authority_rng.next_u32() _clock.advance_minutes(12) _state.add_resource(&"gold", 500) _expect(_runtime.restore_snapshot(snapshot), "runtime restores clock, state, queue and RNG streams") _expect(_runtime.latest_digest == expected_digest, "restored runtime recreates the exact digest") _expect(_runtime.get_rng_stream(&"authority").next_u32() == expected_rng_tail, "restored RNG resumes at the exact next draw") _clock.advance_minutes(30) _expect(_state.get_resource(&"materials") == 47, "restored pending command executes once") func _test_twenty_four_hour_determinism() -> void: var first_snapshot := _run_day_scenario(99117) var second_snapshot := _run_day_scenario(99117) var first_digest := SimulationDigest.compute(first_snapshot) var second_digest := SimulationDigest.compute(second_snapshot) if first_digest != second_digest: _report_digest_difference(first_snapshot, second_snapshot) _expect(first_digest.length() == 64, "24-hour scenario produces a simulation digest") _expect(first_digest == second_digest, "same seed and command sequence match after 24 game hours") func _test_runtime_save_round_trip() -> void: var save_path := "user://saves/s0_runtime_round_trip" SaveService.delete_save(save_path) _reset_runtime(54321) _runtime.get_rng_stream(&"event").next_u32() _runtime.submit_command( &"spend_resources", {&"cost": {&"gold": 333}}, &"player", &"", 0, _clock.absolute_minute + 20, &"runtime.save.pending" ) var expected_digest := SimulationDigest.compute(_runtime.create_snapshot()) var save_result: Dictionary = _runtime.save_to_path(save_path, {&"display_name": "Runtime Round Trip"}) _expect(save_result[&"ok"], "runtime snapshot writes command queue and RNG state to disk") _clock.advance_minutes(25) _state.add_resource(&"gold", 999) var load_result: Dictionary = _runtime.load_from_path(save_path) _expect(load_result[&"ok"], "runtime snapshot loads from verified world data") _expect(_runtime.latest_digest == expected_digest, "disk round trip restores the exact runtime digest") _clock.advance_minutes(20) _expect(_state.get_resource(&"gold") == 4667, "disk-restored pending command executes exactly once") SaveService.delete_save(save_path) func _run_day_scenario(seed: int) -> Dictionary: _reset_runtime(seed) var guest_rng = _runtime.get_rng_stream(&"guest_spawn") for _index in range(16): guest_rng.randi_range(1, 100) _runtime.submit_command( &"spend_resources", {&"cost": {&"gold": 250, &"materials": 2}}, &"player", &"", 20, 6 * 60 + 60, &"scenario.build" ) _runtime.submit_command( &"change_resources", {&"deltas": {&"mana": 15, &"faith": 3}}, &"event", &"", 10, 6 * 60 + 12 * 60, &"scenario.reward" ) _clock.advance_minutes(24 * 60) return _runtime.latest_snapshot.duplicate(true) func _report_digest_difference(first: Dictionary, second: Dictionary) -> void: var first_runtime: Dictionary = first[&"systems"][&"runtime"] var second_runtime: Dictionary = second[&"systems"][&"runtime"] var keys: Array = first_runtime.keys() keys.sort() for key: Variant in keys: var first_part := SimulationDigest.compute(first_runtime[key]) var second_part := SimulationDigest.compute(second_runtime.get(key)) if first_part != second_part: print(" digest mismatch in runtime.%s: %s != %s" % [key, first_part, second_part]) func _expect(condition: bool, label: String) -> void: _checks += 1 if condition: print(" ✓ %s" % label) return _failures += 1 push_error(" ✗ %s" % label)