feat: add versioned simulation state records

This commit is contained in:
2026-07-05 12:52:07 +02:00
parent c690937afa
commit 30cac28fc0
12 changed files with 514 additions and 1 deletions
+88 -1
View File
@@ -309,4 +309,91 @@ func get_state_snapshot() -> Dictionary:
}
func get_state_checksum() -> String:
return JSON.stringify(get_state_snapshot()).sha256_text()
return create_state_record().to_json().sha256_text()
func create_state_record() -> SimulationStateRecord:
var record := SimulationStateRecord.new()
var wander_streams: Array[Dictionary] = []
var sorted_npc_ids: Array = wander_random_sources.keys()
sorted_npc_ids.sort()
for npc_id in sorted_npc_ids:
var source: RandomNumberGenerator = wander_random_sources[npc_id]
wander_streams.append({
"npc_id": int(npc_id),
"seed": str(source.seed),
"state": str(source.state)
})
record.simulation = {
"seed": simulation_seed,
"tick_interval": tick_interval,
"tick_count": tick_count,
"clock_accumulator": clock.accumulator,
"clock_elapsed_ticks": clock.elapsed_ticks,
"wander_random_streams": wander_streams
}
record.village = VillageStateRecord.capture(village)
for npc in npcs:
record.npcs.append(NPCStateRecord.capture(npc))
var resource_nodes := ResourceNode.get_all()
resource_nodes.sort_custom(
func(a: ResourceNode, b: ResourceNode) -> bool:
return String(a.node_id) < String(b.node_id)
)
var captured_resource_ids := {}
for node in resource_nodes:
if captured_resource_ids.has(node.node_id):
continue
captured_resource_ids[node.node_id] = true
record.resources.append(ResourceStateRecord.capture(node))
return record
func serialize_state() -> String:
return create_state_record().to_json()
func restore_state_from_json(json_text: String) -> bool:
var record := SimulationStateRecord.from_json(json_text)
if record == null:
push_error("SimulationManager: refused invalid or unsupported simulation state")
return false
return restore_state(record)
func restore_state(record: SimulationStateRecord) -> bool:
if record == null:
return false
simulation_seed = int(record.simulation["seed"])
tick_interval = float(record.simulation["tick_interval"])
tick_count = int(record.simulation["tick_count"])
clock = SimulationClock.new(tick_interval)
clock.accumulator = float(record.simulation["clock_accumulator"])
clock.elapsed_ticks = int(record.simulation["clock_elapsed_ticks"])
village = record.village.restore(debug_logs)
npcs.clear()
for npc_record in record.npcs:
npcs.append(npc_record.restore(debug_logs))
wander_random_sources.clear()
var wander_streams: Array = record.simulation["wander_random_streams"]
for stream_data in wander_streams:
var source := RandomNumberGenerator.new()
source.seed = String(stream_data["seed"]).to_int()
source.state = String(stream_data["state"]).to_int()
wander_random_sources[int(stream_data["npc_id"])] = source
for resource_record in record.resources:
var node_id := StringName(resource_record.data["node_id"])
var node := ResourceNode.get_by_id(node_id)
if node == null:
push_warning(
"SimulationManager: resource state '%s' has no loaded presentation node"
% node_id
)
continue
if not resource_record.restore(node):
return false
village_changed.emit(village)
return true