extends SceneTree const E = preload("res://world/owniverse/math/exact.gd") const Clock = preload("res://world/owniverse/simulation_time.gd") const Fixture = preload("res://tests/fixtures/civilization_benchmark.gd") var checks: int = 0 var failures: int = 0 func _initialize(): call_deferred("run") func check(condition: bool, message: String): checks += 1 if not condition: failures += 1 push_error(message) func fixture(): var world = load("res://world/owniverse/owniverse.gd").new() world.catalog.load_definitions(root.get_node("Global").OwniverseEntities) Fixture.seed(world, root.get_node("Global")) return world func dispose(world): for entity in world.catalog.entities.values(): entity.free() world.free() func run(): Engine.max_fps = 0 var direct = fixture() var sliced = fixture() for delta in [1.0 / 60.0, 0.133333333333]: direct.evolute(delta) var clock: Dictionary = Clock.advance(sliced.state.time, delta, sliced.speed) var budget = load("res://world/owniverse/frame_budget.gd").new(self, 1) await sliced.evolution.evolute_to_datetime(clock.datetime, clock, budget) check(E.eq(direct.state, sliced.state), "Sliced transaction exactly matches uninterrupted evolution") dispose(direct) dispose(sliced) direct = fixture() sliced = fixture() var initial_h: Variant = sliced.state.amount.H sliced.evolute_frame(1.0 / 60.0, self) check(sliced.frame_busy, "Heavy transaction yields before publishing") check(E.eq(sliced.state.time.year, 0), "Published clock stays at completed transaction") sliced.request_production("H", 1, true) check(E.eq(sliced.state.amount.H, initial_h), "Player action waits for transaction commit") sliced.evolute_frame(0.01, self) var requested: Dictionary = sliced.frame_clock.duplicate() sliced.speed = 3 check(sliced.speed == 3 and sliced.state.speed == 6, "Speed change is visible and cannot mutate an unfinished transaction") while sliced.frame_busy: await process_frame direct.evolute(1.0 / 60.0) direct.production.produce_hs("H", 1) direct.speed = 3 check(E.eq(direct.state, sliced.state), "Queued action and speed change survive publication") sliced.evolute_frame(0.001, self) var second_clock: Dictionary = Clock.advance(requested, 0.001, 3) var active: Dictionary = sliced.frame_active_clock.duplicate() while sliced.frame_busy: await process_frame direct.evolution.evolute_to_datetime(active.datetime, active) check(E.eq(direct.state, sliced.state), "Bounded frame transaction matches its exact target clock") check(E.eq(sliced.frame_clock, second_clock), "All elapsed time during a busy frame remains in the requested clock") check(sliced.frame_commits == 2, "Only complete simulation states are committed") dispose(direct) dispose(sliced) var original = fixture() original.evolute_frame(1.0 / 60.0, self) original.request_production("H", 123, true) original.evolute_frame(0.01, self) original.speed = 3 var checkpoint: Dictionary = E.decode(E.encode(original.frame_checkpoint())) var published: Dictionary = E.decode(E.encode(original.state)) var expected_clock: Dictionary = original.frame_active_clock.duplicate() dispose(original) for frame in 3: await process_frame var resumed = fixture() var expected = fixture() resumed.state = published.duplicate(true) expected.state = published.duplicate(true) resumed.restore_frame_checkpoint(checkpoint) resumed.evolute_frame(0.0, self) while resumed.frame_busy: await process_frame expected.evolution.evolute_to_datetime(expected_clock.datetime, expected_clock) expected.production.produce_hs("H", 123) expected.speed = 3 check(E.eq(expected.state, resumed.state), "Checkpoint recomputes interrupted transaction and preserves queued production") check(E.eq(resumed.frame_clock, checkpoint.requested_clock), "Checkpoint preserves all requested elapsed time") dispose(resumed) dispose(expected) var cancelled = fixture() cancelled.evolute_frame(0.133333333333, self) check(cancelled.frame_busy, "Cancellation fixture has suspended work") var budget_ref: RefCounted = cancelled.frame_budget dispose(cancelled) for frame in 3: await process_frame check(budget_ref.cancelled, "Resetting the world cancels suspended work without publishing") var invalid = fixture() var before: Dictionary = invalid.state.duplicate(true) invalid.evolute_frame(1e308, self) invalid.evolute(1e308) check(not invalid.frame_busy and invalid.frame_clock.is_empty() and E.eq(before, invalid.state), "Overflowing elapsed float cannot enter either clock API") dispose(invalid) var adaptive = fixture() adaptive.frame_clock = Clock.advance(adaptive.state.time, 10.0, 6) var debt_clock: Dictionary = adaptive.frame_clock.duplicate(true) check(adaptive.update_frame_step() == 0.2, "Growing debt increases step to 0.2 seconds") check(adaptive.update_frame_step() == 0.5, "Large debt increases step to bounded 0.5 seconds") check(adaptive.update_frame_step() == 0.5 and E.eq(adaptive.frame_clock, debt_clock), "Maximum step preserves requested time") adaptive.frame_clock = Clock.advance(adaptive.state.time, 0.3, 6) check(adaptive.update_frame_step() == 0.5, "Hysteresis keeps step between thresholds") adaptive.frame_clock = Clock.advance(adaptive.state.time, 0.05, 6) check(adaptive.update_frame_step() == 0.2, "Small debt reduces step") check(adaptive.update_frame_step() == 0.1, "Caught-up simulation returns to base step") adaptive.frame_clock = Clock.advance(adaptive.state.time, 0.15, 6) adaptive._frame_cost_ratio = 1.1 check(adaptive.update_frame_step() == 0.2, "Measured slow transactions increase step before two-step debt") adaptive.frame_clock = Clock.advance(adaptive.state.time, 0.05, 6) adaptive.speed = 3 check(adaptive.update_frame_step() == 1.0 / 60.0, "Low speeds retain 60 Hz scheduling") adaptive.speed = 6 check(adaptive.update_frame_step() == 0.1, "Speed switch resets adaptive step") dispose(adaptive) print("Frame evolution: %d checks, %d failures" % [checks, failures]) quit(1 if failures else 0)