feat: add bounded npc memory retention
This commit is contained in:
@@ -17,6 +17,7 @@ signal npc_decision_recorded(npc: SimNPC, decision: ActionSelectionResult)
|
||||
signal relationship_changed(relationship: RelationshipStateRecord, cause_event: EconomicEventRecord)
|
||||
signal event_knowledge_changed(knower_id: int, event: EconomicEventRecord)
|
||||
signal event_knowledge_transferred(speaker_id: int, listener_id: int, event: EconomicEventRecord)
|
||||
signal event_knowledge_forgotten(knower_id: int, event: EconomicEventRecord)
|
||||
|
||||
var village := SimVillage.new()
|
||||
|
||||
@@ -158,6 +159,8 @@ func simulate_tick() -> void:
|
||||
|
||||
if village_was_changed:
|
||||
village_changed.emit(village)
|
||||
if tick_count % get_knowledge_review_interval() == 0:
|
||||
_maintain_event_knowledge(true)
|
||||
|
||||
if debug_logs:
|
||||
print(village.get_summary())
|
||||
@@ -600,13 +603,15 @@ func try_communicate_at_shared_activity(speaker_id: int, listener_id: int) -> bo
|
||||
var listener := _find_npc_by_id(listener_id)
|
||||
if not _can_communicate_at_shared_activity(speaker, listener):
|
||||
return false
|
||||
var known_event_ids := event_knowledge_system.get_known_event_ids(speaker.id, 0)
|
||||
for index in range(known_event_ids.size() - 1, -1, -1):
|
||||
var event := event_log.get_by_id(known_event_ids[index])
|
||||
var known_event_ids := event_knowledge_system.get_communicable_event_ids(
|
||||
speaker.id, _get_lasting_event_ids(speaker.id)
|
||||
)
|
||||
for event_id in known_event_ids:
|
||||
var event := event_log.get_by_id(event_id)
|
||||
if event == null:
|
||||
continue
|
||||
var learned_record := event_knowledge_system.communicate_event(
|
||||
event, speaker.id, listener.id
|
||||
event, speaker.id, listener.id, tick_count
|
||||
)
|
||||
if learned_record == null:
|
||||
continue
|
||||
@@ -648,13 +653,51 @@ func _can_communicate_at_shared_activity(speaker: SimNPC, listener: SimNPC) -> b
|
||||
func _apply_new_event_knowledge(
|
||||
known_event: KnownEventStateRecord, event: EconomicEventRecord
|
||||
) -> void:
|
||||
event_knowledge_changed.emit(known_event.get_knower_id(), event)
|
||||
var newly_informed_ids: Array[int] = [known_event.get_knower_id()]
|
||||
var changed_relationships: Array[RelationshipStateRecord] = relationship_system.apply_event(
|
||||
event, npcs, newly_informed_ids
|
||||
)
|
||||
event_knowledge_changed.emit(known_event.get_knower_id(), event)
|
||||
for relationship in changed_relationships:
|
||||
relationship_changed.emit(relationship, event)
|
||||
_maintain_event_knowledge(false)
|
||||
|
||||
|
||||
func _maintain_event_knowledge(expire_by_age: bool) -> void:
|
||||
var forgotten := event_knowledge_system.maintain_retention(
|
||||
tick_count, get_knowledge_review_interval(), _get_lasting_knowledge_records(), expire_by_age
|
||||
)
|
||||
for known_event in forgotten:
|
||||
var event := event_log.get_by_id(known_event.get_event_id())
|
||||
if event != null:
|
||||
event_knowledge_forgotten.emit(known_event.get_knower_id(), event)
|
||||
|
||||
|
||||
func _get_lasting_knowledge_records() -> Array[KnownEventStateRecord]:
|
||||
var lasting: Array[KnownEventStateRecord] = []
|
||||
var seen := {}
|
||||
for relationship in relationship_system.get_all_sorted():
|
||||
var event_id := relationship.get_last_trust_cause_event_id()
|
||||
if event_id == RelationshipStateRecord.NO_CAUSE_EVENT:
|
||||
continue
|
||||
var record := event_knowledge_system.get_record(relationship.get_observer_id(), event_id)
|
||||
if record == null:
|
||||
continue
|
||||
var key := "%d:%d" % [record.get_knower_id(), record.get_event_id()]
|
||||
if seen.has(key):
|
||||
continue
|
||||
seen[key] = true
|
||||
lasting.append(record)
|
||||
return lasting
|
||||
|
||||
|
||||
func _get_lasting_event_ids(npc_id: int) -> Array[int]:
|
||||
var event_ids: Array[int] = []
|
||||
for record in _get_lasting_knowledge_records():
|
||||
if record.get_knower_id() == npc_id:
|
||||
event_ids.append(record.get_event_id())
|
||||
event_ids.sort()
|
||||
return event_ids
|
||||
|
||||
|
||||
func _find_npc_by_id(npc_id: int) -> SimNPC:
|
||||
@@ -695,6 +738,24 @@ func get_known_event_record(npc_id: int, event_id: int) -> KnownEventStateRecord
|
||||
return event_knowledge_system.get_record(npc_id, event_id)
|
||||
|
||||
|
||||
func is_known_event_lasting(npc_id: int, event_id: int) -> bool:
|
||||
return event_id in _get_lasting_event_ids(npc_id)
|
||||
|
||||
|
||||
func get_memory_summary(npc_id: int) -> Dictionary:
|
||||
var total := event_knowledge_system.get_known_event_ids(npc_id, 0).size()
|
||||
var lasting := _get_lasting_event_ids(npc_id).size()
|
||||
return {"lasting": lasting, "recent": maxi(total - lasting, 0)}
|
||||
|
||||
|
||||
func get_knowledge_review_interval() -> int:
|
||||
var active_cycle_duration := (
|
||||
clock.cycle_duration_seconds if clock != null else cycle_duration_seconds
|
||||
)
|
||||
var active_tick_interval := clock.tick_interval if clock != null else tick_interval
|
||||
return maxi(roundi(active_cycle_duration / maxf(active_tick_interval, 0.0001)), 1)
|
||||
|
||||
|
||||
func get_current_speed() -> String:
|
||||
return "%.2fx" % SPEED_LEVELS[speed_index]
|
||||
|
||||
@@ -1018,6 +1079,7 @@ func restore_state(record: SimulationStateRecord) -> bool:
|
||||
npcs.append(npc_record.restore(debug_logs))
|
||||
relationship_system.restore(record.relationships)
|
||||
event_knowledge_system.restore(record.event_knowledge)
|
||||
_maintain_event_knowledge(tick_count % get_knowledge_review_interval() == 0)
|
||||
|
||||
wander_random_sources.clear()
|
||||
var wander_streams: Array = record.simulation["wander_random_streams"]
|
||||
|
||||
@@ -2,6 +2,7 @@ class_name EventKnowledgeSystem
|
||||
extends RefCounted
|
||||
|
||||
const WITNESS_RADIUS := 10.0
|
||||
const MAX_RECENT_FACTS_PER_NPC := 3
|
||||
|
||||
var known_events: Dictionary = {}
|
||||
|
||||
@@ -14,7 +15,10 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
||||
if actor == null:
|
||||
return learned
|
||||
var event_id := int(event.data["event_id"])
|
||||
var actor_record := _remember(actor.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED)
|
||||
var acquired_tick := int(event.data["tick"])
|
||||
var actor_record := _remember(
|
||||
actor.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED, acquired_tick
|
||||
)
|
||||
if actor_record != null:
|
||||
learned.append(actor_record)
|
||||
var witness_radius_squared := WITNESS_RADIUS * WITNESS_RADIUS
|
||||
@@ -25,7 +29,7 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
||||
if npc.position.distance_squared_to(event_position) > witness_radius_squared:
|
||||
continue
|
||||
var witness_record := _remember(
|
||||
npc.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED
|
||||
npc.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, acquired_tick
|
||||
)
|
||||
if witness_record != null:
|
||||
learned.append(witness_record)
|
||||
@@ -34,7 +38,7 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
||||
|
||||
|
||||
func communicate_event(
|
||||
event: EconomicEventRecord, speaker_id: int, listener_id: int
|
||||
event: EconomicEventRecord, speaker_id: int, listener_id: int, acquired_tick: int
|
||||
) -> KnownEventStateRecord:
|
||||
if (
|
||||
event == null
|
||||
@@ -56,7 +60,12 @@ func communicate_event(
|
||||
):
|
||||
return null
|
||||
return _remember(
|
||||
listener_id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED, speaker_id
|
||||
listener_id,
|
||||
event_id,
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED,
|
||||
acquired_tick,
|
||||
speaker_id,
|
||||
speaker_record.get_acquisition_method()
|
||||
)
|
||||
|
||||
|
||||
@@ -78,11 +87,14 @@ func get_knowers(event_id: int) -> Array[int]:
|
||||
|
||||
|
||||
func get_known_event_ids(knower_id: int, max_count: int = 3) -> Array[int]:
|
||||
var event_ids: Array[int] = []
|
||||
var records: Array[KnownEventStateRecord] = []
|
||||
for record in known_events.values():
|
||||
if record.get_knower_id() == knower_id:
|
||||
event_ids.append(record.get_event_id())
|
||||
event_ids.sort()
|
||||
records.append(record)
|
||||
records.sort_custom(_sort_by_acquisition)
|
||||
var event_ids: Array[int] = []
|
||||
for record in records:
|
||||
event_ids.append(record.get_event_id())
|
||||
if max_count <= 0 or event_ids.size() <= max_count:
|
||||
return event_ids
|
||||
var recent_ids: Array[int] = []
|
||||
@@ -91,6 +103,67 @@ func get_known_event_ids(knower_id: int, max_count: int = 3) -> Array[int]:
|
||||
return recent_ids
|
||||
|
||||
|
||||
func get_communicable_event_ids(knower_id: int, lasting_event_ids: Array[int]) -> Array[int]:
|
||||
var ranked: Array[Dictionary] = []
|
||||
for record in known_events.values():
|
||||
if record.get_knower_id() != knower_id:
|
||||
continue
|
||||
if record.get_acquisition_method() not in KnownEventStateRecord.DIRECT_ACQUISITION_METHODS:
|
||||
continue
|
||||
(
|
||||
ranked
|
||||
. append(
|
||||
{
|
||||
"event_id": record.get_event_id(),
|
||||
"acquired_tick": record.get_acquired_tick(),
|
||||
"lasting": record.get_event_id() in lasting_event_ids,
|
||||
}
|
||||
)
|
||||
)
|
||||
ranked.sort_custom(_sort_communicable)
|
||||
var event_ids: Array[int] = []
|
||||
for item in ranked:
|
||||
event_ids.append(int(item["event_id"]))
|
||||
return event_ids
|
||||
|
||||
|
||||
func maintain_retention(
|
||||
current_tick: int,
|
||||
max_recent_age: int,
|
||||
lasting_records: Array[KnownEventStateRecord],
|
||||
expire_by_age: bool
|
||||
) -> Array[KnownEventStateRecord]:
|
||||
var lasting_keys := {}
|
||||
for record in lasting_records:
|
||||
lasting_keys[_key(record.get_knower_id(), record.get_event_id())] = true
|
||||
var removal_keys := {}
|
||||
var recent_by_knower := {}
|
||||
for record in known_events.values():
|
||||
var key := _key(record.get_knower_id(), record.get_event_id())
|
||||
if lasting_keys.has(key):
|
||||
continue
|
||||
if expire_by_age and current_tick - record.get_acquired_tick() >= max_recent_age:
|
||||
removal_keys[key] = true
|
||||
continue
|
||||
var recent: Array = recent_by_knower.get(record.get_knower_id(), [])
|
||||
recent.append(record)
|
||||
recent_by_knower[record.get_knower_id()] = recent
|
||||
for recent in recent_by_knower.values():
|
||||
recent.sort_custom(_sort_by_acquisition)
|
||||
var excess: int = recent.size() - MAX_RECENT_FACTS_PER_NPC
|
||||
for index in range(maxi(excess, 0)):
|
||||
var record := recent[index] as KnownEventStateRecord
|
||||
removal_keys[_key(record.get_knower_id(), record.get_event_id())] = true
|
||||
var forgotten: Array[KnownEventStateRecord] = []
|
||||
for record in get_all_sorted():
|
||||
var key := _key(record.get_knower_id(), record.get_event_id())
|
||||
if not removal_keys.has(key):
|
||||
continue
|
||||
known_events.erase(key)
|
||||
forgotten.append(record)
|
||||
return forgotten
|
||||
|
||||
|
||||
func get_all_sorted() -> Array[KnownEventStateRecord]:
|
||||
var records: Array[KnownEventStateRecord] = []
|
||||
for record in known_events.values():
|
||||
@@ -109,13 +182,20 @@ func _remember(
|
||||
knower_id: int,
|
||||
event_id: int,
|
||||
acquisition_method: StringName,
|
||||
source_npc_id: int = KnownEventStateRecord.NO_SOURCE_NPC_ID
|
||||
acquired_tick: int,
|
||||
source_npc_id: int = KnownEventStateRecord.NO_SOURCE_NPC_ID,
|
||||
source_acquisition_method: StringName = &""
|
||||
) -> KnownEventStateRecord:
|
||||
var key := _key(knower_id, event_id)
|
||||
if known_events.has(key):
|
||||
return null
|
||||
var record := KnownEventStateRecord.create(
|
||||
knower_id, event_id, acquisition_method, source_npc_id
|
||||
knower_id,
|
||||
event_id,
|
||||
acquisition_method,
|
||||
source_npc_id,
|
||||
acquired_tick,
|
||||
source_acquisition_method
|
||||
)
|
||||
known_events[key] = record
|
||||
return record
|
||||
@@ -144,3 +224,19 @@ static func _sort_records(first: KnownEventStateRecord, second: KnownEventStateR
|
||||
if first.get_knower_id() != second.get_knower_id():
|
||||
return first.get_knower_id() < second.get_knower_id()
|
||||
return first.get_event_id() < second.get_event_id()
|
||||
|
||||
|
||||
static func _sort_by_acquisition(
|
||||
first: KnownEventStateRecord, second: KnownEventStateRecord
|
||||
) -> bool:
|
||||
if first.get_acquired_tick() != second.get_acquired_tick():
|
||||
return first.get_acquired_tick() < second.get_acquired_tick()
|
||||
return first.get_event_id() < second.get_event_id()
|
||||
|
||||
|
||||
static func _sort_communicable(first: Dictionary, second: Dictionary) -> bool:
|
||||
if bool(first["lasting"]) != bool(second["lasting"]):
|
||||
return bool(first["lasting"])
|
||||
if int(first["acquired_tick"]) != int(second["acquired_tick"]):
|
||||
return int(first["acquired_tick"]) > int(second["acquired_tick"])
|
||||
return int(first["event_id"]) > int(second["event_id"])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class_name KnownEventStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 2
|
||||
const SCHEMA_VERSION := 3
|
||||
const NO_SOURCE_NPC_ID := -1
|
||||
|
||||
const VALID_ACQUISITION_METHODS := [
|
||||
@@ -10,6 +10,10 @@ const VALID_ACQUISITION_METHODS := [
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED,
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY,
|
||||
]
|
||||
const DIRECT_ACQUISITION_METHODS := [
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED,
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED,
|
||||
]
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
@@ -22,7 +26,9 @@ static func create(
|
||||
knower_id: int,
|
||||
event_id: int,
|
||||
acquisition_method: StringName = SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY,
|
||||
source_npc_id: int = NO_SOURCE_NPC_ID
|
||||
source_npc_id: int = NO_SOURCE_NPC_ID,
|
||||
acquired_tick: int = 0,
|
||||
source_acquisition_method: StringName = &""
|
||||
) -> KnownEventStateRecord:
|
||||
return (
|
||||
KnownEventStateRecord
|
||||
@@ -33,6 +39,8 @@ static func create(
|
||||
"event_id": event_id,
|
||||
"acquisition_method": String(acquisition_method),
|
||||
"source_npc_id": source_npc_id,
|
||||
"acquired_tick": acquired_tick,
|
||||
"source_acquisition_method": String(source_acquisition_method),
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -41,22 +49,45 @@ static func create(
|
||||
static func from_dictionary(record_data: Dictionary) -> KnownEventStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not record_data.has_all(["knower_id", "event_id", "acquisition_method", "source_npc_id"]):
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"knower_id",
|
||||
"event_id",
|
||||
"acquisition_method",
|
||||
"source_npc_id",
|
||||
"acquired_tick",
|
||||
"source_acquisition_method",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
var knower_id := int(record_data["knower_id"])
|
||||
var event_id := int(record_data["event_id"])
|
||||
var acquisition_method := StringName(record_data["acquisition_method"])
|
||||
var source_npc_id := int(record_data["source_npc_id"])
|
||||
if knower_id < 0 or event_id < 0:
|
||||
var acquired_tick := int(record_data["acquired_tick"])
|
||||
var source_acquisition_method := StringName(record_data["source_acquisition_method"])
|
||||
if knower_id < 0 or event_id < 0 or acquired_tick < 0:
|
||||
return null
|
||||
if acquisition_method not in VALID_ACQUISITION_METHODS:
|
||||
return null
|
||||
if acquisition_method == SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED:
|
||||
if source_npc_id < 0 or source_npc_id == knower_id:
|
||||
return null
|
||||
elif source_npc_id != NO_SOURCE_NPC_ID:
|
||||
if source_acquisition_method not in DIRECT_ACQUISITION_METHODS:
|
||||
return null
|
||||
elif source_npc_id != NO_SOURCE_NPC_ID or not source_acquisition_method.is_empty():
|
||||
return null
|
||||
return create(knower_id, event_id, acquisition_method, source_npc_id)
|
||||
return create(
|
||||
knower_id,
|
||||
event_id,
|
||||
acquisition_method,
|
||||
source_npc_id,
|
||||
acquired_tick,
|
||||
source_acquisition_method
|
||||
)
|
||||
|
||||
|
||||
func get_knower_id() -> int:
|
||||
@@ -75,5 +106,13 @@ func get_source_npc_id() -> int:
|
||||
return int(data["source_npc_id"])
|
||||
|
||||
|
||||
func get_acquired_tick() -> int:
|
||||
return int(data["acquired_tick"])
|
||||
|
||||
|
||||
func get_source_acquisition_method() -> StringName:
|
||||
return StringName(data["source_acquisition_method"])
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
|
||||
@@ -2,12 +2,13 @@ class_name SimulationStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_NAME := "the_steward.simulation"
|
||||
const SCHEMA_VERSION := 6
|
||||
const SCHEMA_VERSION := 7
|
||||
const LEGACY_SCHEMA_VERSION := 1
|
||||
const EVENT_LEGACY_SCHEMA_VERSION := 2
|
||||
const RELATIONSHIP_LEGACY_SCHEMA_VERSION := 3
|
||||
const KNOWLEDGE_LEGACY_SCHEMA_VERSION := 4
|
||||
const PREVIOUS_SCHEMA_VERSION := 5
|
||||
const PROVENANCE_LEGACY_SCHEMA_VERSION := 5
|
||||
const PREVIOUS_SCHEMA_VERSION := 6
|
||||
|
||||
var simulation: Dictionary
|
||||
var village: VillageStateRecord
|
||||
@@ -76,6 +77,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
EVENT_LEGACY_SCHEMA_VERSION,
|
||||
RELATIONSHIP_LEGACY_SCHEMA_VERSION,
|
||||
KNOWLEDGE_LEGACY_SCHEMA_VERSION,
|
||||
PROVENANCE_LEGACY_SCHEMA_VERSION,
|
||||
PREVIOUS_SCHEMA_VERSION,
|
||||
]
|
||||
):
|
||||
@@ -114,6 +116,23 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
]
|
||||
):
|
||||
return null
|
||||
var saved_tick_interval := float(simulation_data["tick_interval"])
|
||||
var saved_tick_count := int(simulation_data["tick_count"])
|
||||
var saved_clock_accumulator := float(simulation_data["clock_accumulator"])
|
||||
var saved_clock_elapsed_ticks := int(simulation_data["clock_elapsed_ticks"])
|
||||
if (
|
||||
not is_finite(saved_tick_interval)
|
||||
or saved_tick_interval <= 0.0
|
||||
or saved_tick_count < 0
|
||||
or not is_finite(saved_clock_accumulator)
|
||||
or saved_clock_accumulator < 0.0
|
||||
or saved_clock_elapsed_ticks < 0
|
||||
):
|
||||
return null
|
||||
if simulation_data.has("cycle_duration_seconds"):
|
||||
var saved_cycle_duration := float(simulation_data["cycle_duration_seconds"])
|
||||
if not is_finite(saved_cycle_duration) or saved_cycle_duration <= 0.0:
|
||||
return null
|
||||
var wander_streams = simulation_data["wander_random_streams"]
|
||||
if not wander_streams is Array:
|
||||
return null
|
||||
@@ -209,7 +228,6 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
return null
|
||||
|
||||
var knowledge_keys := {}
|
||||
var knowledge_records_by_key := {}
|
||||
for item in knowledge_data:
|
||||
if not item is Dictionary:
|
||||
return null
|
||||
@@ -224,11 +242,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
if knowledge_keys.has(knowledge_key):
|
||||
return null
|
||||
knowledge_keys[knowledge_key] = true
|
||||
knowledge_records_by_key[knowledge_key] = known_event_record
|
||||
record.event_knowledge.append(known_event_record)
|
||||
for known_event_record in record.event_knowledge:
|
||||
if not _is_valid_knowledge_provenance(
|
||||
known_event_record, npc_ids, event_records_by_id, knowledge_records_by_key
|
||||
known_event_record, npc_ids, event_records_by_id, int(record.simulation["tick_count"])
|
||||
):
|
||||
return null
|
||||
|
||||
@@ -279,7 +296,7 @@ static func _is_valid_knowledge_provenance(
|
||||
known_event: KnownEventStateRecord,
|
||||
npc_ids: Dictionary,
|
||||
event_records_by_id: Dictionary,
|
||||
knowledge_records_by_key: Dictionary
|
||||
current_tick: int
|
||||
) -> bool:
|
||||
var event := event_records_by_id.get(known_event.get_event_id()) as EconomicEventRecord
|
||||
if event == null:
|
||||
@@ -287,6 +304,10 @@ static func _is_valid_knowledge_provenance(
|
||||
var method := known_event.get_acquisition_method()
|
||||
var knower_id := known_event.get_knower_id()
|
||||
var actor_id := int(event.data["actor_id"])
|
||||
var event_tick := int(event.data["tick"])
|
||||
var acquired_tick := known_event.get_acquired_tick()
|
||||
if acquired_tick < event_tick or acquired_tick > current_tick:
|
||||
return false
|
||||
if method == SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY:
|
||||
return true
|
||||
if (
|
||||
@@ -297,29 +318,22 @@ static func _is_valid_knowledge_provenance(
|
||||
return false
|
||||
match method:
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED:
|
||||
return knower_id == actor_id
|
||||
return knower_id == actor_id and acquired_tick == event_tick
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED:
|
||||
return knower_id != actor_id
|
||||
return knower_id != actor_id and acquired_tick == event_tick
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED:
|
||||
if knower_id == actor_id:
|
||||
return false
|
||||
var source_npc_id := known_event.get_source_npc_id()
|
||||
if not npc_ids.has(source_npc_id):
|
||||
return false
|
||||
var source_record := (
|
||||
knowledge_records_by_key.get("%d:%d" % [source_npc_id, known_event.get_event_id()])
|
||||
as KnownEventStateRecord
|
||||
)
|
||||
return (
|
||||
source_record != null
|
||||
and (
|
||||
source_record.get_acquisition_method()
|
||||
in [
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED,
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED,
|
||||
]
|
||||
)
|
||||
)
|
||||
var source_method := known_event.get_source_acquisition_method()
|
||||
if (
|
||||
(source_method == SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED)
|
||||
!= (source_npc_id == actor_id)
|
||||
):
|
||||
return false
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
@@ -368,10 +382,13 @@ static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary
|
||||
]
|
||||
):
|
||||
migrated["event_knowledge"] = _migrate_relationship_causes(
|
||||
migrated.get("relationships", [])
|
||||
migrated.get("relationships", []),
|
||||
int((migrated.get("simulation", {}) as Dictionary).get("tick_count", 0))
|
||||
)
|
||||
migrated["event_knowledge"] = _migrate_known_event_provenance(
|
||||
migrated.get("event_knowledge", []), migrated.get("economic_events", [])
|
||||
migrated["event_knowledge"] = _migrate_known_event_retention(
|
||||
migrated.get("event_knowledge", []),
|
||||
migrated.get("economic_events", []),
|
||||
int((migrated.get("simulation", {}) as Dictionary).get("tick_count", 0))
|
||||
)
|
||||
return migrated
|
||||
|
||||
@@ -417,7 +434,9 @@ static func _relationship_dictionary_sort(first: Dictionary, second: Dictionary)
|
||||
return int(first["subject_id"]) < int(second["subject_id"])
|
||||
|
||||
|
||||
static func _migrate_relationship_causes(relationship_data: Variant) -> Array[Dictionary]:
|
||||
static func _migrate_relationship_causes(
|
||||
relationship_data: Variant, migration_tick: int
|
||||
) -> Array[Dictionary]:
|
||||
var migrated_knowledge: Array[Dictionary] = []
|
||||
var known_keys := {}
|
||||
if not relationship_data is Array:
|
||||
@@ -438,7 +457,17 @@ static func _migrate_relationship_causes(relationship_data: Variant) -> Array[Di
|
||||
continue
|
||||
known_keys[knowledge_key] = true
|
||||
migrated_knowledge.append(
|
||||
KnownEventStateRecord.create(observer_id, cause_event_id).to_dictionary()
|
||||
(
|
||||
KnownEventStateRecord
|
||||
. create(
|
||||
observer_id,
|
||||
cause_event_id,
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY,
|
||||
KnownEventStateRecord.NO_SOURCE_NPC_ID,
|
||||
migration_tick
|
||||
)
|
||||
. to_dictionary()
|
||||
)
|
||||
)
|
||||
migrated_knowledge.sort_custom(_knowledge_dictionary_sort)
|
||||
return migrated_knowledge
|
||||
@@ -450,17 +479,21 @@ static func _knowledge_dictionary_sort(first: Dictionary, second: Dictionary) ->
|
||||
return int(first["event_id"]) < int(second["event_id"])
|
||||
|
||||
|
||||
static func _migrate_known_event_provenance(knowledge_data: Variant, event_data: Variant) -> Array:
|
||||
static func _migrate_known_event_retention(
|
||||
knowledge_data: Variant, event_data: Variant, migration_tick: int
|
||||
) -> Array:
|
||||
if not knowledge_data is Array:
|
||||
return []
|
||||
var actor_by_event_id := {}
|
||||
var event_by_id := {}
|
||||
if event_data is Array:
|
||||
for event_item in event_data:
|
||||
if not event_item is Dictionary:
|
||||
continue
|
||||
actor_by_event_id[int(event_item.get("event_id", -1))] = int(
|
||||
event_item.get("actor_id", -1)
|
||||
)
|
||||
event_by_id[int(event_item.get("event_id", -1))] = event_item
|
||||
var old_knowledge_by_key := {}
|
||||
for item in knowledge_data:
|
||||
if item is Dictionary and item.has_all(["knower_id", "event_id"]):
|
||||
old_knowledge_by_key["%d:%d" % [int(item["knower_id"]), int(item["event_id"])]] = item
|
||||
var migrated_knowledge: Array = []
|
||||
var can_sort := true
|
||||
for item in knowledge_data:
|
||||
@@ -471,17 +504,48 @@ static func _migrate_known_event_provenance(knowledge_data: Variant, event_data:
|
||||
if int(item.get("schema_version", -1)) == KnownEventStateRecord.SCHEMA_VERSION:
|
||||
migrated_knowledge.append(item.duplicate(true))
|
||||
continue
|
||||
if int(item.get("schema_version", -1)) != 1 or not item.has_all(["knower_id", "event_id"]):
|
||||
var nested_version := int(item.get("schema_version", -1))
|
||||
if nested_version not in [1, 2] or not item.has_all(["knower_id", "event_id"]):
|
||||
migrated_knowledge.append(item.duplicate(true))
|
||||
can_sort = false
|
||||
continue
|
||||
var knower_id := int(item["knower_id"])
|
||||
var event_id := int(item["event_id"])
|
||||
var acquisition_method := SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY
|
||||
if int(actor_by_event_id.get(event_id, -2)) == knower_id:
|
||||
acquisition_method = SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED
|
||||
var event_item := event_by_id.get(event_id, {}) as Dictionary
|
||||
var event_tick := int(event_item.get("tick", migration_tick))
|
||||
var event_actor_id := int(event_item.get("actor_id", -2))
|
||||
var acquisition_method := StringName(
|
||||
item.get("acquisition_method", SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY)
|
||||
)
|
||||
var source_npc_id := int(item.get("source_npc_id", KnownEventStateRecord.NO_SOURCE_NPC_ID))
|
||||
if nested_version == 1:
|
||||
acquisition_method = SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY
|
||||
source_npc_id = KnownEventStateRecord.NO_SOURCE_NPC_ID
|
||||
if event_actor_id == knower_id:
|
||||
acquisition_method = SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED
|
||||
var acquired_tick := migration_tick
|
||||
if acquisition_method in KnownEventStateRecord.DIRECT_ACQUISITION_METHODS:
|
||||
acquired_tick = event_tick
|
||||
var source_acquisition_method := &""
|
||||
if acquisition_method == SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED:
|
||||
var source_item: Variant = old_knowledge_by_key.get(
|
||||
"%d:%d" % [source_npc_id, event_id], {}
|
||||
)
|
||||
if source_item is Dictionary:
|
||||
source_acquisition_method = StringName(source_item.get("acquisition_method", &""))
|
||||
migrated_knowledge.append(
|
||||
KnownEventStateRecord.create(knower_id, event_id, acquisition_method).to_dictionary()
|
||||
(
|
||||
KnownEventStateRecord
|
||||
. create(
|
||||
knower_id,
|
||||
event_id,
|
||||
acquisition_method,
|
||||
source_npc_id,
|
||||
acquired_tick,
|
||||
source_acquisition_method
|
||||
)
|
||||
. to_dictionary()
|
||||
)
|
||||
)
|
||||
if can_sort:
|
||||
migrated_knowledge.sort_custom(_knowledge_dictionary_sort)
|
||||
|
||||
Reference in New Issue
Block a user