extends SceneTree ## A06 reserve across a real process boundary. Run "-- writer" first, then ## "-- reader" in a new Godot process; the reader must reproduce the writer's ## continuation exactly from the disk save, and migrate a legacy save. const Cycle = preload("res://scripts/mana/mana_reserve_cycle.gd") const CURRENT_SAVE := "user://saves/a06_mana_new_pid_current" const LEGACY_SAVE := "user://saves/a06_mana_new_pid_legacy" const EXPECTED_PATH := "user://saves/a06_mana_new_pid_expected.json" const CONTINUE_MINUTES := 75 var _checks := 0 var _failures := 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: var mode := "writer" if not "reader" in OS.get_cmdline_user_args() else "reader" print("S2: A06 Mana reserve new-PID %s (pid %d)" % [mode, OS.get_process_id()]) if mode == "writer": _write() else: _read() print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks]) quit(_failures) func _new_runtime() -> Array: var clock: Variant = load("res://autoload/game_clock.gd").new() var state: Variant = load("res://autoload/game_state.gd").new() var runtime: Variant = load("res://autoload/simulation_runtime.gd").new() clock.reset(1, 6, 0) state.reset() runtime.initialize(clock, state, 4404) clock.set_paused(true) return [clock, state, runtime] func _free(nodes: Array) -> void: for node: Variant in nodes: node.free() func _write() -> void: var nodes := _new_runtime() var clock: Variant = nodes[0] var state: Variant = nodes[1] var runtime: Variant = nodes[2] # Stop mid-interval so the reader must not settle the pending boundary early. clock.advance_minutes(43) var snapshot: Dictionary = runtime.create_snapshot() _expect(SaveService.save_snapshot(CURRENT_SAVE, snapshot)[&"ok"], "writer saves current A06 reserve") var legacy: Dictionary = snapshot.duplicate(true) legacy[&"systems"][&"runtime"].erase(&"mana_reserve") legacy[&"game_state"][&"resources"][&"mana"] = 0 _expect(SaveService.save_snapshot(LEGACY_SAVE, legacy)[&"ok"], "writer saves legacy zero-reserve save") var saved_mana: int = state.get_resource(&"mana") clock.advance_minutes(CONTINUE_MINUTES) var expected := { "writer_pid": OS.get_process_id(), "saved_minute": int(snapshot[&"clock"][&"absolute_minute"]), "saved_mana": saved_mana, "mana": state.get_resource(&"mana"), "reserve": var_to_str(runtime.mana_reserve.create_snapshot()), "digest": SimulationDigest.compute(runtime.create_snapshot()), } var file := FileAccess.open(EXPECTED_PATH, FileAccess.WRITE) _expect(file != null, "writer records continuation") if file != null: file.store_string(JSON.stringify(expected)) file.close() _free(nodes) func _read() -> void: var expected: Variant = JSON.parse_string(FileAccess.get_file_as_string(EXPECTED_PATH)) _expect(expected is Dictionary, "reader finds writer record") if not expected is Dictionary: return _expect(int(expected["writer_pid"]) != OS.get_process_id(), "reader runs in a different process") var nodes := _new_runtime() var clock: Variant = nodes[0] var state: Variant = nodes[1] var runtime: Variant = nodes[2] var loaded := SaveService.load_snapshot(CURRENT_SAVE) _expect(loaded[&"ok"] and runtime.restore_snapshot(loaded[&"snapshot"]), "reader restores current save from disk") _expect(clock.absolute_minute == int(expected["saved_minute"]) and state.get_resource(&"mana") == int(expected["saved_mana"]), "restored minute and reserve match the save") clock.advance_minutes(CONTINUE_MINUTES) _expect(state.get_resource(&"mana") == int(expected["mana"]), "continued reserve matches the writer") _expect(var_to_str(runtime.mana_reserve.create_snapshot()) == String(expected["reserve"]), "continued reserve receipt matches the writer") _expect(SimulationDigest.compute(runtime.create_snapshot()) == String(expected["digest"]), "whole continued state digest matches the writer") var legacy := SaveService.load_snapshot(LEGACY_SAVE) _expect(legacy[&"ok"] and runtime.restore_snapshot(legacy[&"snapshot"]), "reader migrates legacy save from disk") _expect(state.get_resource(&"mana") == 0 and runtime.mana_reserve.get_last_flow().is_empty(), "legacy explicit 0 stays stopped") _expect(runtime.create_snapshot()[&"systems"][&"runtime"].has(&"mana_reserve"), "migrated state saves with the A06 reserve") for path: String in [CURRENT_SAVE, LEGACY_SAVE]: SaveService.delete_save(path) DirAccess.remove_absolute(ProjectSettings.globalize_path(EXPECTED_PATH)) _free(nodes) func _expect(condition: bool, label: String) -> void: _checks += 1 if condition: print(" ✓ %s" % label) else: _failures += 1 push_error(" ✗ %s" % label)