101 lines
4.4 KiB
GDScript
101 lines
4.4 KiB
GDScript
extends SceneTree
|
|
|
|
## Passive observer: no selection, save/load, command or engine input injection.
|
|
## Runner launches separate owned processes with private user-data directories.
|
|
var _main: Control
|
|
var _elapsed := 0.0
|
|
var _last := ""
|
|
var _sequence := 0
|
|
var _output := ""
|
|
var _source_map: Dictionary = {}
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_start")
|
|
|
|
func _start() -> void:
|
|
_output = OS.get_environment("P24_OBSERVER_OUTPUT")
|
|
var pins := OS.get_environment("P24_OBSERVER_SOURCE_MAP")
|
|
if not _output.is_absolute_path() or not DirAccess.dir_exists_absolute(_output) or not pins.is_absolute_path():
|
|
push_error("P24 observer requires existing absolute output directory and source-map JSON")
|
|
quit(1)
|
|
return
|
|
var parsed: Variant = JSON.parse_string(FileAccess.get_file_as_string(pins))
|
|
if not parsed is Dictionary or parsed.is_empty():
|
|
push_error("P24 observer source map missing")
|
|
quit(1)
|
|
return
|
|
_source_map = parsed
|
|
root.size = Vector2i(1440, 810)
|
|
_main = load("res://scenes/main/main.tscn").instantiate()
|
|
root.add_child(_main)
|
|
print("P24_PROCESS pid=%d scene=res://scenes/main/main.tscn" % OS.get_process_id())
|
|
# Use the ordinary Main pause control through OS input when a frozen state
|
|
# is needed. The observer itself never changes Runtime or Clock.
|
|
|
|
func _process(delta: float) -> bool:
|
|
_elapsed += delta
|
|
if _elapsed >= 1.0 and is_instance_valid(_main):
|
|
_elapsed = 0.0
|
|
_capture()
|
|
return false
|
|
|
|
func _capture() -> void:
|
|
var runtime: Variant = root.get_node("SimulationRuntime")
|
|
var selection: Dictionary = _main.call("_capture_interface_selection")
|
|
var facility_id := StringName(selection.get("id", ""))
|
|
var facility: Variant = runtime.facility_service.get_instance(facility_id)
|
|
var guest_panel: GuestObservationPanel
|
|
var dialog: Variant = _main.get("guest_observer")
|
|
if dialog != null:
|
|
guest_panel = dialog.find_child("GuestObservationPanel", true, false) as GuestObservationPanel
|
|
var staff_panel: StaffManagementPanel = _main.get("staff_management_panel")
|
|
var staff_model: Dictionary = {}
|
|
for row: Dictionary in runtime.get_staff_management_report().get(&"staff", []):
|
|
if String(row.get(&"staff_id", "")) == String(selection.get("staff_id", "")):
|
|
staff_model = row.duplicate(true)
|
|
var needs: Dictionary = {}
|
|
if guest_panel != null:
|
|
for need: StringName in guest_panel.bars:
|
|
needs[need] = guest_panel.bars[need].value
|
|
var observation := {
|
|
"interface_selection": selection,
|
|
"layer": root.get_node("GameState").get("active_layer"),
|
|
"facility": {"id": String(facility_id), "model_snapshot": facility.create_snapshot() if facility != null else {},
|
|
"heading_text": _main.get("selected_heading_label").text,
|
|
"detail_text": _main.get("selected_detail_label").text,
|
|
"highlight_id": String(_main.get("world_preview").get("selected_entity_id"))},
|
|
"guest": {"id": selection.get("guest_id", ""),
|
|
"model_report": runtime.guest_service.get_observation(StringName(selection.get("guest_id", ""))),
|
|
"detail_text": guest_panel.details.text if guest_panel != null else "",
|
|
"priority_text": guest_panel.priority_label.text if guest_panel != null else "",
|
|
"decision_text": guest_panel.decision_label.text if guest_panel != null else "", "need_bars": needs},
|
|
"staff": {"id": selection.get("staff_id", ""), "model_report": staff_model,
|
|
"detail_text": staff_panel.staff_detail_label.text,
|
|
"editor_visible": staff_panel.editor_panel.is_visible_in_tree()},
|
|
}
|
|
var encoded := JSON.stringify(observation, "", true)
|
|
if encoded == _last:
|
|
return
|
|
_sequence += 1
|
|
var stem := "process-%d-observation-%d" % [OS.get_process_id(), _sequence]
|
|
var report_path := _output.path_join(stem + ".json")
|
|
var frame_path := _output.path_join(stem + ".png")
|
|
if FileAccess.file_exists(report_path) or FileAccess.file_exists(frame_path):
|
|
push_error("P24 observer refuses artifact overwrite")
|
|
return
|
|
var image := root.get_texture().get_image()
|
|
if image.save_png(frame_path) != OK:
|
|
push_error("P24 observer frame capture failed")
|
|
return
|
|
var file := FileAccess.open(report_path, FileAccess.WRITE)
|
|
if file == null:
|
|
push_error("P24 observer report write failed")
|
|
return
|
|
file.store_string(JSON.stringify({"pid": OS.get_process_id(), "source_map": _source_map,
|
|
"scene": "res://scenes/main/main.tscn", "ticks_usec": Time.get_ticks_usec(),
|
|
"raw_observation": observation, "screenshot": stem + ".png",
|
|
"formal_acceptance": false}, "\t", true) + "\n")
|
|
file.close()
|
|
_last = encoded
|
|
print("P24_RAW_OBSERVATION " + encoded)
|