feat(M01): add typed domain identities

Work-Package: M01-FND-DOMAIN-IDENTITIES-001

Agent: sindo-main-codex
This commit is contained in:
2026-07-13 16:11:00 +04:00
parent d68786355c
commit c1dc07c705
14 changed files with 621 additions and 1 deletions
+43
View File
@@ -0,0 +1,43 @@
class_name ContentId
extends RefCounted
## Stable authored-content identity serialized as canonical lowercase UUID text.
## It is independent of runtime entities, server database entries and wire GUIDs.
var uuid_text: String:
get:
return _uuid_text
var _uuid_text: String
## Normalizes surrounding whitespace and hexadecimal letter case.
func _init(uuid_text_value: String) -> void:
_uuid_text = uuid_text_value.strip_edges().to_lower()
## Reports whether the value has the canonical 8-4-4-4-12 UUID text shape.
func is_valid() -> bool:
if _uuid_text.length() != 36:
return false
for character_index in range(_uuid_text.length()):
if character_index in [8, 13, 18, 23]:
if _uuid_text[character_index] != "-":
return false
elif not _is_lowercase_hexadecimal_character(_uuid_text[character_index]):
return false
return true
## Compares stable content identity by normalized UUID text.
func equals(other_content_id: ContentId) -> bool:
return other_content_id != null and _uuid_text == other_content_id.uuid_text
## Returns the canonical dictionary/serialization key.
func to_key() -> String:
return _uuid_text
func _is_lowercase_hexadecimal_character(character: String) -> bool:
return (character >= "0" and character <= "9") or (character >= "a" and character <= "f")
+1
View File
@@ -0,0 +1 @@
uid://dk6splyui2an1
+31
View File
@@ -0,0 +1,31 @@
class_name EntityId
extends RefCounted
## Session-local runtime entity identity.
## This sequence is never a stable content ID, server entry or WoW wire GUID.
var session_sequence: int:
get:
return _session_sequence
var _session_sequence: int
## Stores an allocator-issued session sequence without persistence semantics.
func _init(session_sequence_value: int) -> void:
_session_sequence = session_sequence_value
## Reports whether this is a positive allocator-issued sequence.
func is_valid() -> bool:
return _session_sequence > 0
## Compares identity inside the same explicitly owned runtime session.
func equals(other_entity_id: EntityId) -> bool:
return other_entity_id != null and _session_sequence == other_entity_id.session_sequence
## Returns a diagnostic key; it must not be persisted across sessions.
func to_debug_key() -> String:
return "entity:%d" % _session_sequence
+1
View File
@@ -0,0 +1 @@
uid://bqdd8el4me5ng
+41
View File
@@ -0,0 +1,41 @@
class_name ServerEntryId
extends RefCounted
## Adapter-namespaced numeric entry from a server database or canonical snapshot.
## Equal numbers in different template tables are intentionally different IDs.
var entry_namespace: String:
get:
return _entry_namespace
var entry_value: int:
get:
return _entry_value
var _entry_namespace: String
var _entry_value: int
## Normalizes a table/adapter namespace and preserves its positive entry value.
func _init(entry_namespace_value: String, entry_value_value: int) -> void:
_entry_namespace = entry_namespace_value.strip_edges().to_lower()
_entry_value = entry_value_value
## Reports whether both collision-prevention fields are present.
func is_valid() -> bool:
return not _entry_namespace.is_empty() and _entry_value > 0
## Compares both namespace and numeric entry.
func equals(other_server_entry_id: ServerEntryId) -> bool:
return (
other_server_entry_id != null
and _entry_namespace == other_server_entry_id.entry_namespace
and _entry_value == other_server_entry_id.entry_value
)
## Returns the stable adapter key without implying a ContentId mapping.
func to_key() -> String:
return "%s:%d" % [_entry_namespace, _entry_value]
@@ -0,0 +1 @@
uid://cg3mht5bhgsya
+53
View File
@@ -0,0 +1,53 @@
class_name WowGuid
extends RefCounted
## Opaque 64-bit WoW wire GUID stored as two lossless unsigned 32-bit words.
## Build-specific high-type decoding belongs to the protocol adapter and must be
## fixture-backed before becoming a domain contract.
const MAXIMUM_UNSIGNED_32_BIT_VALUE := 0xffffffff
var high_32_bits: int:
get:
return _high_32_bits
var low_32_bits: int:
get:
return _low_32_bits
var _high_32_bits: int
var _low_32_bits: int
## Preserves both wire words without signed 64-bit conversion.
func _init(high_32_bits_value: int, low_32_bits_value: int) -> void:
_high_32_bits = high_32_bits_value
_low_32_bits = low_32_bits_value
## Reports whether both words fit their unsigned wire ranges.
func has_valid_word_ranges() -> bool:
return _is_unsigned_32_bit_word(_high_32_bits) and _is_unsigned_32_bit_word(_low_32_bits)
## Reports the all-zero protocol sentinel without assigning gameplay meaning.
func is_empty() -> bool:
return _high_32_bits == 0 and _low_32_bits == 0
## Compares all 64 opaque wire bits.
func equals(other_wow_guid: WowGuid) -> bool:
return (
other_wow_guid != null
and _high_32_bits == other_wow_guid.high_32_bits
and _low_32_bits == other_wow_guid.low_32_bits
)
## Returns exactly sixteen lowercase hexadecimal digits for diagnostics/replays.
func to_hex_string() -> String:
return "%08x%08x" % [_high_32_bits, _low_32_bits]
func _is_unsigned_32_bit_word(word_value: int) -> bool:
return word_value >= 0 and word_value <= MAXIMUM_UNSIGNED_32_BIT_VALUE
+1
View File
@@ -0,0 +1 @@
uid://bmwlyacooq2lc
+87
View File
@@ -0,0 +1,87 @@
extends SceneTree
## Headless M01 contract verification for non-interchangeable identity values.
const ContentIdScript = preload("res://src/domain/identity/content_id.gd")
const EntityIdScript = preload("res://src/domain/identity/entity_id.gd")
const WowGuidScript = preload("res://src/domain/identity/wow_guid.gd")
const ServerEntryIdScript = preload("res://src/domain/identity/server_entry_id.gd")
func _initialize() -> void:
var failures: Array[String] = []
_verify_content_id(failures)
_verify_entity_id(failures)
_verify_wow_guid(failures)
_verify_server_entry_id(failures)
_verify_scene_free_sources(failures)
if not failures.is_empty():
for failure in failures:
push_error("DOMAIN_IDENTITIES: %s" % failure)
quit(1)
return
print("DOMAIN_IDENTITIES PASS content=5 entity=5 wow_guid=6 server_entry=5")
quit(0)
func _verify_content_id(failures: Array[String]) -> void:
var normalized = ContentIdScript.new(" 123E4567-E89B-42D3-A456-426614174000 ")
_expect_true(normalized.is_valid(), "normalized ContentId is valid", failures)
_expect_equal(normalized.to_key(), "123e4567-e89b-42d3-a456-426614174000", "ContentId canonical key", failures)
_expect_true(normalized.equals(ContentIdScript.new("123e4567-e89b-42d3-a456-426614174000")), "equal ContentIds", failures)
_expect_true(not normalized.equals(ContentIdScript.new("123e4567-e89b-42d3-a456-426614174001")), "different ContentIds", failures)
_expect_true(not ContentIdScript.new("123e4567e89b42d3a456426614174000").is_valid(), "malformed ContentId", failures)
func _verify_entity_id(failures: Array[String]) -> void:
var entity_id = EntityIdScript.new(42)
_expect_true(entity_id.is_valid(), "positive EntityId", failures)
_expect_true(not EntityIdScript.new(0).is_valid(), "zero EntityId", failures)
_expect_true(not EntityIdScript.new(-1).is_valid(), "negative EntityId", failures)
_expect_true(entity_id.equals(EntityIdScript.new(42)), "equal EntityIds", failures)
_expect_equal(entity_id.to_debug_key(), "entity:42", "EntityId diagnostic key", failures)
func _verify_wow_guid(failures: Array[String]) -> void:
var guid = WowGuidScript.new(0xf1300000, 0xffffffff)
_expect_true(guid.has_valid_word_ranges(), "maximum GUID low word", failures)
_expect_equal(guid.to_hex_string(), "f1300000ffffffff", "GUID hexadecimal form", failures)
_expect_true(guid.equals(WowGuidScript.new(0xf1300000, 0xffffffff)), "equal GUID words", failures)
_expect_true(WowGuidScript.new(0, 0).is_empty(), "zero GUID sentinel", failures)
_expect_true(not WowGuidScript.new(-1, 0).has_valid_word_ranges(), "negative GUID word", failures)
_expect_true(not WowGuidScript.new(0x100000000, 0).has_valid_word_ranges(), "overflow GUID word", failures)
func _verify_server_entry_id(failures: Array[String]) -> void:
var creature_entry = ServerEntryIdScript.new(" Creature_Template ", 123)
var gameobject_entry = ServerEntryIdScript.new("gameobject_template", 123)
_expect_true(creature_entry.is_valid(), "namespaced server entry", failures)
_expect_equal(creature_entry.to_key(), "creature_template:123", "server entry key", failures)
_expect_true(not creature_entry.equals(gameobject_entry), "cross-table entry collision prevented", failures)
_expect_true(not ServerEntryIdScript.new("", 123).is_valid(), "missing entry namespace", failures)
_expect_true(not ServerEntryIdScript.new("creature_template", 0).is_valid(), "zero server entry", failures)
func _verify_scene_free_sources(failures: Array[String]) -> void:
for file_name in ["content_id.gd", "entity_id.gd", "wow_guid.gd", "server_entry_id.gd"]:
var path := "res://src/domain/identity/%s" % file_name
var file := FileAccess.open(path, FileAccess.READ)
if file == null:
failures.append("cannot open %s" % path)
continue
var source := file.get_as_text()
_expect_true(not source.contains("extends Node"), "%s does not inherit Node" % file_name, failures)
_expect_true(not source.contains("extends Resource"), "%s does not inherit Resource" % file_name, failures)
_expect_true(not source.contains("Vector3"), "%s does not expose Vector3" % file_name, failures)
func _expect_equal(actual_value, expected_value, label: String, failures: Array[String]) -> void:
if actual_value != expected_value:
failures.append("%s expected %s, got %s" % [label, expected_value, actual_value])
func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
if not actual_value:
failures.append("%s expected true" % label)
@@ -0,0 +1 @@
uid://bmig1rwaur44r