extends RefCounted ## All seeds, actor IDs, positions, food quantities and schedules are diagnostic inputs. var checks := 0 var failures := 0 var runtime: Variant var clock: Variant var state: Variant var directory: String var mode: String var slot: String var serial := 0 var events: Array = [] var identity: Dictionary = {} const MEMBERS := [SaveService.WORLD_FILE, SaveService.MANIFEST_FILE, SaveService.THUMBNAIL_FILE, SaveService.JOURNAL_FILE] func expect(ok: bool, label: String) -> bool: checks += 1 if not ok: failures += 1 push_error("P08 mixed: " + label) return ok func command(kind: StringName, payload: Dictionary) -> bool: serial += 1 var result: CommandResult = runtime.submit_command(kind, payload, &"player", &"", 0, -1, StringName("p08.%d" % serial)) var label := String(kind) + ": " + result.message if not result.is_success(): label += " payload=" + var_to_str(payload) + " details=" + var_to_str(result.details) return expect(result.is_success(), label) func registry(old: bool) -> void: var paths: Array[String] = FacilityCatalog.FACILITY_PATHS + FacilityCatalog.INFRASTRUCTURE_FACILITY_PATHS + FacilityCatalog.RESCUE_FACILITY_PATHS paths.append_array(["res://tests/fixtures/legacy_premium_room.tres", "res://tests/fixtures/legacy_staff_dormitory.tres"] if old else ["res://data/facilities/expansion/premium_room.tres", "res://data/facilities/expansion/staff_dormitory.tres"]) expect(runtime.definition_registry.rebuild_from_paths(paths)[&"valid"], "definition registry") func build(id: StringName, cell: Vector3i, complete := true) -> bool: if not command(&"build_facility", {&"definition_id":id, &"origin":cell}): return false if complete: clock.advance_minutes(runtime.definition_registry.get_facility(id).build_minutes) return true func instance(id: StringName) -> FacilityInstance: for room: FacilityInstance in runtime.facility_service.get_instances(): if room.definition_id == id: return room return null ## Scalar text avoids loss of integer precision; raw Variant bytes accompany JSON. func typed(value: Variant) -> Dictionary: var result := {"type":type_string(typeof(value))} if value is Dictionary: result["entries"] = [] for key: Variant in value: result["entries"].append([typed(key),typed(value[key])]) elif value is Array: result["element_type"] = value.get_typed_builtin() result["element_class"] = String(value.get_typed_class_name()) result["items"] = [] for item: Variant in value: result["items"].append(typed(item)) else: result["text"] = var_to_str(value) return result func put(name: String, value: Variant) -> void: var path := directory.path_join(name) if not expect(not FileAccess.file_exists(path+".var") and not FileAccess.file_exists(path+".json"), "fresh proof "+name): return var file := FileAccess.open(path+".var",FileAccess.WRITE) if not expect(file != null,"Variant output"): return file.store_buffer(var_to_bytes(value)); file.close() file = FileAccess.open(path+".json",FileAccess.WRITE) if not expect(file != null,"typed output"): return file.store_string(JSON.stringify(typed(value))); file.close() func get_proof(name: String) -> Variant: return bytes_to_var(FileAccess.get_file_as_bytes(directory.path_join(name+".var"))) func capture(name: String, value: Variant) -> void: put(mode+"-"+name,value) if mode == "reader": expect(var_to_bytes(value) == var_to_bytes(get_proof("writer-"+name)),"whole cross-PID "+name) func copy_package(source: String, destination: String) -> void: expect(DirAccess.make_dir_recursive_absolute(destination) == OK,"package directory") for member: String in MEMBERS: var data := FileAccess.get_file_as_bytes(source.path_join(member)) if not expect(not data.is_empty() and not FileAccess.file_exists(destination.path_join(member)),"fresh complete member "+member): continue var file := FileAccess.open(destination.path_join(member),FileAccess.WRITE) if not expect(file != null,"member writable"): continue file.store_buffer(data); file.close() expect(data == FileAccess.get_file_as_bytes(destination.path_join(member)),"whole member copy verified") func packages() -> Dictionary: var hashes := {} for suffix: String in ["",SaveService.MIGRATION_BACKUP_SUFFIX]: for member: String in MEMBERS: hashes[suffix+"/"+member] = FileAccess.get_sha256(ProjectSettings.globalize_path(slot+suffix).path_join(member)) return hashes func observe(event: SimulationDomainEvent) -> void: events.append(event.to_dictionary()) ## Controlled diagnostic input; costs come only from registered domain definitions. func prepare_diagnostic_budget() -> bool: var planned: Array[StringName] = [&"warehouse",&"kitchen",&"infirmary",&"staff_door",&"reception",&"reception",&"guest_elevator",&"staff_dormitory",&"premium_room",&"premium_room",&"service_stairs",&"service_stairs",&"service_stairs",&"guest_stairs",&"guest_stairs",&"guest_stairs"] var needed := {&"gold":0,&"materials":0} var costs: Array = [] for id: StringName in planned: var definition: FacilityDefinition = runtime.definition_registry.get_facility(id) if not expect(definition != null,"budget definition "+String(id)): return false costs.append({&"definition_id":id,&"cost":definition.construction_cost.duplicate(true)}) for resource: StringName in needed: needed[resource] += int(definition.construction_cost.get(resource,0)) var signing := int(runtime.mvp_content_catalog.staff_hire_payload(&"staff_gina").get(&"signing_cost",0)) var food := 8 * int(SupplyCatalog.get_item(SupplyCatalog.RAW_FOOD)[&"unit_price"]) needed[&"gold"] += signing + food var deltas := {} for resource: StringName in needed: var deficit: int = maxi(0,int(needed[resource])-state.get_resource(resource)) if deficit > 0: deltas[resource] = deficit identity[&"diagnostic_budget"] = {&"seed":42588,&"costs":costs,&"signing":signing,&"food_quantity":8,&"food_cost":food,&"needed":needed,&"initial":state.resources.duplicate(true),&"previous_change_resources":{},&"deltas":deltas} return deltas.is_empty() or command(&"change_resources",{&"deltas":deltas}) func setup(operations: bool) -> bool: registry(true) if not prepare_diagnostic_budget(): return false if operations and not expect(runtime.enable_production_operations().get(&"ok",false),"Operations enabled at Day 1 06:00"): return false for row: Array in [[&"warehouse",Vector3i(-8,0,1)],[&"kitchen",Vector3i(2,0,1)],[&"infirmary",Vector3i(6,0,1)],[&"staff_door",Vector3i(0,0,1)],[&"reception",Vector3i(-6,0,0)],[&"reception",Vector3i(5,0,0)],[&"guest_elevator",Vector3i.ZERO]]: if not build(row[0],row[1],false): return false clock.advance_minutes(180) # Completed stairs supply the next floor's landing through the domain projector. # Ground-floor landings come from the initialized world; never inject path cells. var stair_minutes: int = maxi(runtime.definition_registry.get_facility(&"service_stairs").build_minutes,runtime.definition_registry.get_facility(&"guest_stairs").build_minutes) for y: int in [1,2,3]: if not expect(runtime.path_service.has_cell(Vector3i(-9,y-1,1)) and runtime.path_service.has_cell(Vector3i(4,y-1,0)),"completed lower stair landings for floor %d" % y): return false if not build(&"service_stairs",Vector3i(-9,y,1),false) or not build(&"guest_stairs",Vector3i(4,y,0),false): return false clock.advance_minutes(stair_minutes) if not build(&"staff_dormitory",Vector3i(-8,2,1)): return false identity[&"dorm"] = instance(&"staff_dormitory").instance_id if not build(&"premium_room",Vector3i(-6,2,0),false): return false clock.advance_minutes(7) identity[&"jobs"] = [] for job: ConstructionJob in runtime.construction_service.get_jobs(): if not job.is_terminal(): identity[&"jobs"].append(job.job_id) if not expect(identity[&"jobs"].size() == 1,"one paid progressing job"): return false var warehouse := instance(&"warehouse") var kitchen := instance(&"kitchen") if not expect(warehouse != null and kitchen != null,"completed logistics endpoints"): return false if not command(&"hire_staff_candidate",{&"candidate_id":&"staff_gina"}): return false if not command(&"set_staff_work_assignment",{&"staff_id":&"staff_gina",&"facility_id":warehouse.instance_id,&"template_id":&"DAY"}): return false if not command(&"purchase_supplies",{&"warehouse_id":warehouse.instance_id,&"item_id":SupplyCatalog.RAW_FOOD,&"quantity":8}): return false if not command(&"set_kitchen_production",{&"facility_id":kitchen.instance_id,&"enabled":true,&"target_stock":4}): return false var cargo_ready := false for minute: int in range(60): clock.advance_minutes(1) if not runtime.staff_service.get_staff(&"staff_gina").carried_inventory_id.is_empty(): cargo_ready = true; break if not expect(cargo_ready,"real purchased cargo picked up"): return false for id: StringName in [&"patient",&"carrier"]: if not command(&"register_staff",{&"staff_id":id,&"role_id":&"porter",&"options":{&"current_cell":Vector3i(-3,0,1) if id == &"patient" else Vector3i(-1,0,1),&"health":0 if id == &"patient" else 100,&"state":StaffRuntime.State.AVAILABLE,&"role_tags":[&"medic"],&"permitted_task_tags":[&"rescue"]}}): return false var carrying := false for minute: int in range(6): clock.advance_minutes(1) for task: StaffTask in runtime.staff_service.get_tasks(): if task.task_type == &"rescue_transport" and task.state == StaffTask.State.MOVING: carrying = true; identity[&"rescue_task"] = task.task_id if carrying: break if not expect(carrying,"actual rescue transport"): return false for index: int in range(5): if not command(&"spawn_guest",{&"guest_id":StringName("p08.passenger.%d" % index),&"guest_type_id":&"traveler",&"gold":100,&"options":{&"current_cell":Vector3i.ZERO,&"state":GuestRuntime.State.MOVING,&"state_entered_at":clock.absolute_minute-1,&"planned_exit_at":clock.absolute_minute+2000,&"path_handle":{&"target_cell":Vector3i(0,1,0)}}}): return false clock.advance_minutes(1) if not command(&"spawn_guest",{&"guest_id":&"p08.walker",&"guest_type_id":&"traveler",&"gold":100,&"options":{&"current_cell":Vector3i(4,0,0),&"state":GuestRuntime.State.MOVING,&"state_entered_at":clock.absolute_minute-1,&"planned_exit_at":clock.absolute_minute+2000,&"path_handle":{&"target_cell":Vector3i(4,3,0)}}}): return false clock.advance_minutes(1) identity[&"ev_requests"] = [] for trip: Dictionary in runtime.elevators.get_report()[&"trips"]: identity[&"ev_requests"].append(trip[&"request_id"]) identity[&"logistics_requests"] = [] for request: LogisticsRequest in runtime.logistics_requests.values(): if not request.is_terminal(): identity[&"logistics_requests"].append(request.request_id) expect(not runtime.staff_service.get_staff(&"staff_gina").carried_inventory_id.is_empty(),"cargo in SAME save as patient and EV") expect(runtime.staff_service.get_staff(&"patient").health == 0,"patient remains untreated") var car: Dictionary = runtime.elevators.get_report()[&"shafts"][0] expect(car[&"passenger_count"] == 4 and car[&"waiting_count"] == 1,"passengers and queue coexist") expect(runtime.create_snapshot()[&"systems"][&"runtime"][&"movement_credits"].size() > 0,"fractional movement credit") if not build(&"premium_room",Vector3i(5,2,0),false): return false for candidate: ConstructionJob in runtime.construction_service.get_jobs(): if not candidate.is_terminal() and not identity[&"jobs"].has(candidate.job_id): identity[&"jobs"].append(candidate.job_id) if not expect(identity[&"jobs"].size() == 2,"two paid active jobs"): return false identity[&"starting_job"] = identity[&"jobs"][1] for id: StringName in identity[&"jobs"]: var job: ConstructionJob = runtime.construction_service.get_job(id) expect(not job.is_terminal() and (job.progress == 0 if id == identity[&"starting_job"] else job.progress > 0) and not job.construction_cost.is_empty() and job.reserved_cells.size() == 6,"old-size paid starting/progressing jobs remain active") return failures == 0 func invariant() -> void: expect(runtime.staff_service.validate_invariants()[&"ok"],"staff ownership") expect(SupplyLogisticsBridge.validate_crosslinks(runtime.logistics_requests,runtime.staff_service,runtime.inventory_service,runtime.facility_service),"cargo crosslinks") var snap: Dictionary = runtime.create_snapshot() expect(runtime.restore_snapshot(snap),"whole Runtime ownership/support/transport validation") expect(var_to_bytes(snap) == var_to_bytes(runtime.create_snapshot()),"whole canonical typed state") func run(tree: SceneTree, output: String, role: String, operations: bool, refusal: bool) -> Dictionary: directory = output; mode = role slot = "user://p08-%d/formal" % Time.get_ticks_usec() tree.root.get_node("GameClock").set_paused(true); tree.root.get_node("GameClock").set_process(false) tree.root.get_node("SimulationRuntime").configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false}) runtime = load("res://autoload/simulation_runtime.gd").new() clock = load("res://autoload/game_clock.gd").new(); state = load("res://autoload/game_state.gd").new() clock.reset(1,6,0); state.reset(); expect(runtime.initialize(clock,state,42588)[&"valid"],"current initialization") clock.set_paused(true); runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false,&"auto_migrate":false}) if mode == "writer": expect(DirAccess.make_dir_recursive_absolute(directory) == OK,"proof directory") if not setup(operations): return finish() registry(false) var baseline: Dictionary = runtime.create_snapshot() expect(SaveService.save_snapshot(slot,baseline)[&"ok"],"formal original saved") if refusal: var initial: Dictionary = runtime.migrate_facility_dimensions_at_path(slot) expect(initial.get(&"ok",false) and initial.get(&"backup_created",false),"real prior migration backup") expect(runtime.staff_service.create_task(&"maintenance",10,clock.absolute_minute,{&"target_entity_id":identity[&"dorm"],&"target_cell":Vector3i(-9,3,1),&"estimated_minutes":30},&"p08.late").is_success(),"later old entrance task") if not command(&"register_staff",{&"staff_id":&"maintainer",&"role_id":&"porter",&"options":{&"current_cell":Vector3i(-9,1,1),&"state":StaffRuntime.State.AVAILABLE}}): return finish() expect(runtime.staff_service.assign_task_to_staff(&"p08.late",&"maintainer",clock.absolute_minute).is_success(),"real task ownership") expect(SaveService.save_snapshot(slot,runtime.create_snapshot())[&"ok"],"obstructed source saved") put("identity",identity); put("original",runtime.create_snapshot()) copy_package(ProjectSettings.globalize_path(slot),directory.path_join("original/formal")) var live: Dictionary = runtime.create_snapshot() var before := packages(); put("before-members",before) var first: Dictionary = runtime.prepare_facility_dimension_snapshot(identity[&"jobs"][0]) expect(first.get(&"ok",false),"earlier candidate independently migratable") put("first-candidate",first) var preview: Dictionary = runtime.preview_facility_dimensions_at_path(slot); put("preview",preview) var result: Dictionary = runtime.migrate_facility_dimensions_at_path(slot,&"",String(preview.get(&"source_digest",""))); put("migration",result) expect(var_to_bytes(live) == var_to_bytes(runtime.create_snapshot()),"live unchanged") put("after-live",runtime.create_snapshot()) if refusal: expect(not result.get(&"ok",true) and result.get(&"details",{}).get(&"rolled_back",false) and result.get(&"details",{}).get(&"facility_id",&"") == identity[&"dorm"],"later refusal rolls back earlier candidates") expect(before == packages(),"original and existing backup unchanged") else: expect(preview.get(&"ok",false) and result.get(&"ok",false) and result.get(&"backup_created",false),"positive whole batch") put("after-members",packages()) copy_package(ProjectSettings.globalize_path(slot),directory.path_join("result/formal")) copy_package(ProjectSettings.globalize_path(slot+SaveService.MIGRATION_BACKUP_SUFFIX),directory.path_join("backup/formal")) else: registry(false); identity = get_proof("identity") copy_package(directory.path_join("result/formal"),ProjectSettings.globalize_path(slot)) copy_package(directory.path_join("backup/formal"),ProjectSettings.globalize_path(slot+SaveService.MIGRATION_BACKUP_SUFFIX)) var loaded := SaveService.load_snapshot(slot) expect(loaded.get(&"ok",false) and runtime.restore_snapshot(loaded.get(&"snapshot",{})),"formal continuation restore") expect(runtime.get_operations_report().get(&"enabled",false) == operations,"actual Operations mode") invariant(); capture("checkpoint",runtime.create_snapshot()) expect(runtime.domain_events.subscribe(&"*",&"p08.uncapped",Callable(self,"observe")),"uncapped events") for minute: int in range(160): clock.advance_minutes(1); capture("minute-%03d" % (minute+1),runtime.create_snapshot()) capture("events",events) for id: StringName in identity[&"jobs"]: var job: ConstructionJob = runtime.construction_service.get_job(id) expect(job.state == ConstructionJob.State.COMPLETED and runtime.facility_service.get_instance(job.facility_instance_id).occupied_cells.size() == (6 if refusal else 4),"paid job completes once with expected shape") expect(runtime.staff_service.get_staff(&"patient").health == 100,"patient treated") for id: StringName in identity[&"logistics_requests"]: expect(runtime.logistics_requests[id].state == LogisticsRequest.State.COMPLETED,"original cargo delivered") for id: StringName in identity[&"ev_requests"]: var count := 0 for event: Dictionary in events: if event["event_type"] == "ELEVATOR_ARRIVED" and event["payload"].get(&"request_id",&"") == id: count += 1 expect(count == 1,"original EV request completes exactly once") invariant() put(mode+"-process",{&"pid":OS.get_process_id(),&"operations":operations,&"refusal":refusal,&"checks":checks,&"failures":failures}) return finish() func finish() -> Dictionary: runtime.free(); clock.free(); state.free() return {"checks":checks,"failures":failures}