feat: add versioned simulation state records
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user