205 lines
6.0 KiB
GDScript
205 lines
6.0 KiB
GDScript
class_name WorldEventStore
|
|
extends RefCounted
|
|
|
|
signal event_recorded(event: WorldEventRecord)
|
|
|
|
const SCHEMA_VERSION := 1
|
|
|
|
var _events_by_id: Dictionary = {}
|
|
var _ordered_event_ids: Array[int] = []
|
|
var _event_type_index: Dictionary = {}
|
|
var _participant_index: Dictionary = {}
|
|
var _location_index: Dictionary = {}
|
|
var _tick_index: Dictionary = {}
|
|
var _ordered_ticks: Array[int] = []
|
|
|
|
|
|
static func from_dictionary(store_data: Dictionary) -> WorldEventStore:
|
|
var store := WorldEventStore.new()
|
|
return store if store.restore_from_dictionary(store_data) else null
|
|
|
|
|
|
func size() -> int:
|
|
return _events_by_id.size()
|
|
|
|
|
|
func is_empty() -> bool:
|
|
return _events_by_id.is_empty()
|
|
|
|
|
|
func has_event(event_id: int) -> bool:
|
|
return _events_by_id.has(event_id)
|
|
|
|
|
|
func append(event: WorldEventRecord) -> bool:
|
|
return _append(event, true)
|
|
|
|
|
|
func append_economic(
|
|
economic_event: EconomicEventRecord, world_id: StringName = &"", location_id: StringName = &""
|
|
) -> WorldEventRecord:
|
|
var world_event := WorldEventRecord.from_economic_event(economic_event, world_id, location_id)
|
|
if world_event == null or not append(world_event):
|
|
return null
|
|
return get_by_id(world_event.get_event_id())
|
|
|
|
|
|
func get_by_id(event_id: int) -> WorldEventRecord:
|
|
return _events_by_id.get(event_id) as WorldEventRecord
|
|
|
|
|
|
func get_by_ids(event_ids: Array[int]) -> Array[WorldEventRecord]:
|
|
var results: Array[WorldEventRecord] = []
|
|
for event_id in event_ids:
|
|
var event := get_by_id(event_id)
|
|
if event != null:
|
|
results.append(event)
|
|
return results
|
|
|
|
|
|
func get_all() -> Array[WorldEventRecord]:
|
|
return _records_for_ids(_ordered_event_ids)
|
|
|
|
|
|
func get_for_type(event_type: StringName) -> Array[WorldEventRecord]:
|
|
return _records_for_ids(_event_type_index.get(event_type, []))
|
|
|
|
|
|
func get_for_participant(participant: WorldEntityRef) -> Array[WorldEventRecord]:
|
|
if participant == null or not participant.is_valid():
|
|
return []
|
|
return _records_for_ids(_participant_index.get(participant.index_key(), []))
|
|
|
|
|
|
func get_for_location(location: SpatialAddress) -> Array[WorldEventRecord]:
|
|
if location == null or not location.is_valid():
|
|
return []
|
|
return _records_for_ids(_location_index.get(location.index_key(), []))
|
|
|
|
|
|
func get_for_tick(tick: int) -> Array[WorldEventRecord]:
|
|
return _records_for_ids(_tick_index.get(tick, []))
|
|
|
|
|
|
func get_between_ticks(first_tick: int, last_tick: int) -> Array[WorldEventRecord]:
|
|
var results: Array[WorldEventRecord] = []
|
|
if first_tick < 0 or last_tick < first_tick:
|
|
return results
|
|
for tick in _ordered_ticks:
|
|
if tick < first_tick:
|
|
continue
|
|
if tick > last_tick:
|
|
break
|
|
results.append_array(_records_for_ids(_tick_index[tick]))
|
|
return results
|
|
|
|
|
|
func restore(restored_events: Array[WorldEventRecord]) -> bool:
|
|
var candidate := WorldEventStore.new()
|
|
for event in restored_events:
|
|
if not candidate._append(event, false):
|
|
return false
|
|
_adopt_indexes(candidate)
|
|
return true
|
|
|
|
|
|
func restore_from_dictionary(store_data: Dictionary) -> bool:
|
|
if int(store_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
|
return false
|
|
var saved_events = store_data.get("events")
|
|
if not saved_events is Array:
|
|
return false
|
|
var restored_events: Array[WorldEventRecord] = []
|
|
for saved_event in saved_events:
|
|
if not saved_event is Dictionary:
|
|
return false
|
|
var event := WorldEventRecord.from_dictionary(saved_event)
|
|
if event == null:
|
|
return false
|
|
restored_events.append(event)
|
|
return restore(restored_events)
|
|
|
|
|
|
func to_dictionary() -> Dictionary:
|
|
var serialized_events: Array[Dictionary] = []
|
|
for event in get_all():
|
|
serialized_events.append(event.to_dictionary())
|
|
return {
|
|
"schema_version": SCHEMA_VERSION,
|
|
"events": serialized_events,
|
|
}
|
|
|
|
|
|
func _append(event: WorldEventRecord, emit_signal: bool) -> bool:
|
|
if event == null or not event.is_valid() or _events_by_id.has(event.get_event_id()):
|
|
return false
|
|
var stored_event := WorldEventRecord.from_dictionary(event.to_dictionary())
|
|
if stored_event == null:
|
|
return false
|
|
var event_id := stored_event.get_event_id()
|
|
_events_by_id[event_id] = stored_event
|
|
_insert_event_id(_ordered_event_ids, event_id)
|
|
_index_event_id(_event_type_index, stored_event.get_event_type(), event_id)
|
|
for participant_key in stored_event.get_participant_index_keys():
|
|
_index_event_id(_participant_index, participant_key, event_id)
|
|
if stored_event.has_location():
|
|
_index_event_id(_location_index, stored_event.get_location().index_key(), event_id)
|
|
_index_event_id(_tick_index, stored_event.get_tick(), event_id)
|
|
_insert_tick(stored_event.get_tick())
|
|
if emit_signal:
|
|
event_recorded.emit(stored_event)
|
|
return true
|
|
|
|
|
|
func _index_event_id(index: Dictionary, key: Variant, event_id: int) -> void:
|
|
if not index.has(key):
|
|
index[key] = []
|
|
var indexed_ids: Array = index[key]
|
|
_insert_event_id(indexed_ids, event_id)
|
|
|
|
|
|
func _insert_event_id(event_ids: Array, event_id: int) -> void:
|
|
var low := 0
|
|
var high := event_ids.size()
|
|
while low < high:
|
|
var middle := (low + high) / 2
|
|
if _event_precedes(event_id, int(event_ids[middle])):
|
|
high = middle
|
|
else:
|
|
low = middle + 1
|
|
event_ids.insert(low, event_id)
|
|
|
|
|
|
func _event_precedes(left_id: int, right_id: int) -> bool:
|
|
var left := get_by_id(left_id)
|
|
var right := get_by_id(right_id)
|
|
if left.get_tick() != right.get_tick():
|
|
return left.get_tick() < right.get_tick()
|
|
return left_id < right_id
|
|
|
|
|
|
func _insert_tick(tick: int) -> void:
|
|
var insert_at := _ordered_ticks.bsearch(tick)
|
|
if insert_at < _ordered_ticks.size() and _ordered_ticks[insert_at] == tick:
|
|
return
|
|
_ordered_ticks.insert(insert_at, tick)
|
|
|
|
|
|
func _records_for_ids(event_ids: Array) -> Array[WorldEventRecord]:
|
|
var results: Array[WorldEventRecord] = []
|
|
for event_id in event_ids:
|
|
var event := get_by_id(int(event_id))
|
|
if event != null:
|
|
results.append(event)
|
|
return results
|
|
|
|
|
|
func _adopt_indexes(source: WorldEventStore) -> void:
|
|
_events_by_id = source._events_by_id
|
|
_ordered_event_ids = source._ordered_event_ids
|
|
_event_type_index = source._event_type_index
|
|
_participant_index = source._participant_index
|
|
_location_index = source._location_index
|
|
_tick_index = source._tick_index
|
|
_ordered_ticks = source._ordered_ticks
|