class_name WorldEventDailyRollupRecord extends RefCounted const SCHEMA_VERSION := 1 const RECORD_FIELDS := [ "schema_version", "day_index", "world_id", "location_id", "event_type", "item_id", "event_count", "total_amount", "minimum_tick", "maximum_tick", "minimum_event_id", "maximum_event_id", "source_event_ids_checksum", ] var _day_index := -1 var _world_id: StringName var _location_id: StringName var _event_type: StringName var _item_id: StringName var _event_count := 0 var _total_amount := 0.0 var _minimum_tick := -1 var _maximum_tick := -1 var _minimum_event_id := -1 var _maximum_event_id := -1 var _source_event_ids_checksum := "" static func create( day_index: int, world_id: StringName, location_id: StringName, event_type: StringName, item_id: StringName, source_event_ids: Array[int], total_amount: float, minimum_tick: int, maximum_tick: int ) -> WorldEventDailyRollupRecord: var normalized_ids: Variant = _normalize_event_ids(source_event_ids) if normalized_ids == null or normalized_ids.is_empty(): return null var record := WorldEventDailyRollupRecord.new() record._day_index = day_index record._world_id = world_id record._location_id = location_id record._event_type = event_type record._item_id = item_id record._event_count = normalized_ids.size() record._total_amount = total_amount record._minimum_tick = minimum_tick record._maximum_tick = maximum_tick record._minimum_event_id = int(normalized_ids.front()) record._maximum_event_id = int(normalized_ids.back()) record._source_event_ids_checksum = JSON.stringify(normalized_ids).sha256_text() return record if record.is_valid() else null static func from_dictionary(record_data: Dictionary) -> WorldEventDailyRollupRecord: if not _has_exact_fields(record_data, RECORD_FIELDS): return null if ( not record_data["schema_version"] is int or int(record_data["schema_version"]) != SCHEMA_VERSION or not record_data["day_index"] is int or not record_data["world_id"] is String or not record_data["location_id"] is String or not record_data["event_type"] is String or not record_data["item_id"] is String or not record_data["event_count"] is int or (not record_data["total_amount"] is int and not record_data["total_amount"] is float) or not record_data["minimum_tick"] is int or not record_data["maximum_tick"] is int or not record_data["minimum_event_id"] is int or not record_data["maximum_event_id"] is int or not record_data["source_event_ids_checksum"] is String ): return null var record := WorldEventDailyRollupRecord.new() record._day_index = int(record_data["day_index"]) record._world_id = StringName(record_data["world_id"]) record._location_id = StringName(record_data["location_id"]) record._event_type = StringName(record_data["event_type"]) record._item_id = StringName(record_data["item_id"]) record._event_count = int(record_data["event_count"]) record._total_amount = float(record_data["total_amount"]) record._minimum_tick = int(record_data["minimum_tick"]) record._maximum_tick = int(record_data["maximum_tick"]) record._minimum_event_id = int(record_data["minimum_event_id"]) record._maximum_event_id = int(record_data["maximum_event_id"]) record._source_event_ids_checksum = String(record_data["source_event_ids_checksum"]) return record if record.is_valid() else null func is_valid() -> bool: return ( _day_index >= 0 and not _world_id.is_empty() and not _location_id.is_empty() and not _event_type.is_empty() and not _item_id.is_empty() and _event_count > 0 and is_finite(_total_amount) and _total_amount > 0.0 and _minimum_tick >= 0 and _maximum_tick >= _minimum_tick and _minimum_event_id >= 0 and _maximum_event_id >= _minimum_event_id and _is_sha256(_source_event_ids_checksum) ) func get_day_index() -> int: return _day_index func get_world_id() -> StringName: return _world_id func get_location_id() -> StringName: return _location_id func get_event_type() -> StringName: return _event_type func get_item_id() -> StringName: return _item_id func get_event_count() -> int: return _event_count func get_total_amount() -> float: return _total_amount func get_minimum_tick() -> int: return _minimum_tick func get_maximum_tick() -> int: return _maximum_tick func get_minimum_event_id() -> int: return _minimum_event_id func get_maximum_event_id() -> int: return _maximum_event_id func get_source_event_ids_checksum() -> String: return _source_event_ids_checksum func index_key() -> String: return index_key_for(_day_index, _world_id, _location_id, _event_type, _item_id) func location_index_key() -> String: return location_key_for(_world_id, _location_id) func get_reason_trace() -> Array[StringName]: return [ &"daily_transaction_rollup", StringName("source_event_count_%d" % _event_count), StringName("source_event_ids_sha256_%s" % _source_event_ids_checksum), ] func to_dictionary() -> Dictionary: return { "schema_version": SCHEMA_VERSION, "day_index": _day_index, "world_id": String(_world_id), "location_id": String(_location_id), "event_type": String(_event_type), "item_id": String(_item_id), "event_count": _event_count, "total_amount": _total_amount, "minimum_tick": _minimum_tick, "maximum_tick": _maximum_tick, "minimum_event_id": _minimum_event_id, "maximum_event_id": _maximum_event_id, "source_event_ids_checksum": _source_event_ids_checksum, } static func index_key_for( day_index: int, world_id: StringName, location_id: StringName, event_type: StringName, item_id: StringName ) -> String: return JSON.stringify( [day_index, String(world_id), String(location_id), String(event_type), String(item_id)] ) static func location_key_for(world_id: StringName, location_id: StringName) -> String: return JSON.stringify([String(world_id), String(location_id)]) static func _normalize_event_ids(values: Array) -> Variant: var seen: Dictionary = {} var normalized: Array[int] = [] for value: Variant in values: if not value is int or int(value) < 0 or seen.has(int(value)): return null seen[int(value)] = true normalized.append(int(value)) normalized.sort() return normalized static func _has_exact_fields(record_data: Dictionary, fields: Array) -> bool: if record_data.size() != fields.size(): return false for field: String in fields: if not record_data.has(field): return false return true static func _is_sha256(value: String) -> bool: if value.length() != 64: return false for character_index in range(value.length()): if value[character_index] not in "0123456789abcdef": return false return true