From 17cd2893ce441d534526ca317b698ac92a3f78f2 Mon Sep 17 00:00:00 2001 From: Harold Serrano Date: Wed, 21 Jan 2026 21:42:13 -0700 Subject: [PATCH] [Patch] Fix find entity name --- .../Systems/RegistrationSystem.swift | 47 +++++++++++++++++-- Sources/UntoldEngine/Utils/Globals.swift | 2 +- Tests/UntoldEngineTests/ECSTests.swift | 23 +++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/Sources/UntoldEngine/Systems/RegistrationSystem.swift b/Sources/UntoldEngine/Systems/RegistrationSystem.swift index a6411e66..721b2cc0 100644 --- a/Sources/UntoldEngine/Systems/RegistrationSystem.swift +++ b/Sources/UntoldEngine/Systems/RegistrationSystem.swift @@ -855,8 +855,28 @@ func getMeshesForEntity(entityId: EntityID) -> [Mesh]? { } public func setEntityName(entityId: EntityID, name: String) { + if let previousName = entityNameMap[entityId], !previousName.isEmpty { + if var list = reverseEntityNameMap[previousName] { + list.removeAll { $0 == entityId } + if list.isEmpty { + reverseEntityNameMap.removeValue(forKey: previousName) + } else { + reverseEntityNameMap[previousName] = list + } + } + } + + if name.isEmpty { + entityNameMap.removeValue(forKey: entityId) + return + } + entityNameMap[entityId] = name - reverseEntityNameMap[name] = entityId + var list = reverseEntityNameMap[name] ?? [] + if list.contains(entityId) == false { + list.append(entityId) + } + reverseEntityNameMap[name] = list } public func getEntityName(entityId: EntityID) -> String { @@ -870,13 +890,34 @@ func removeEntityName(entityId: EntityID) { if let stored = entityNameMap[entityId], stored.isEmpty == false { - reverseEntityNameMap.removeValue(forKey: stored) + if var list = reverseEntityNameMap[stored] { + list.removeAll { $0 == entityId } + if list.isEmpty { + reverseEntityNameMap.removeValue(forKey: stored) + } else { + reverseEntityNameMap[stored] = list + } + } } entityNameMap.removeValue(forKey: entityId) } public func findEntity(name: String) -> EntityID? { - reverseEntityNameMap[name] + guard var list = reverseEntityNameMap[name], !list.isEmpty else { + return nil + } + + list = list.filter { id in + scene.exists(id) && entityNameMap[id] == name + } + + if list.isEmpty { + reverseEntityNameMap.removeValue(forKey: name) + return nil + } + + reverseEntityNameMap[name] = list + return list.first } /* diff --git a/Sources/UntoldEngine/Utils/Globals.swift b/Sources/UntoldEngine/Utils/Globals.swift index 76005849..f3a8c196 100644 --- a/Sources/UntoldEngine/Utils/Globals.swift +++ b/Sources/UntoldEngine/Utils/Globals.swift @@ -43,7 +43,7 @@ var entityMeshMap: [EntityID: [Mesh]] = [:] // Holds all meshes loaded var entityNameMap: [EntityID: String] = [:] // links entity Id to names -var reverseEntityNameMap: [String: EntityID] = [:] +var reverseEntityNameMap: [String: [EntityID]] = [:] // Timing properties var timeSinceLastUpdatePreviousTime: TimeInterval! diff --git a/Tests/UntoldEngineTests/ECSTests.swift b/Tests/UntoldEngineTests/ECSTests.swift index e729199c..e253fb99 100644 --- a/Tests/UntoldEngineTests/ECSTests.swift +++ b/Tests/UntoldEngineTests/ECSTests.swift @@ -118,6 +118,29 @@ final class ECSTests: XCTestCase { XCTAssertNotEqual(entityId, wrongId, "entities should not match") } + func testFindEntityWithDuplicateNamesReturnsFirstValidMatch() { + entityNameMap.removeAll() + reverseEntityNameMap.removeAll() + + let firstEntity = createEntity() + let secondEntity = createEntity() + + setEntityName(entityId: firstEntity, name: "dup") + setEntityName(entityId: secondEntity, name: "dup") + + XCTAssertEqual(findEntity(name: "dup"), firstEntity, "Should return first entity assigned with the name") + + destroyEntity(entityId: firstEntity) + + XCTAssertEqual(findEntity(name: "dup"), secondEntity, "Should skip stale entity and return next valid match") + + if let list = reverseEntityNameMap["dup"] { + XCTAssertEqual(list, [secondEntity], "Stale entries should be pruned from the reverse name map") + } else { + XCTFail("Expected reverse name map to contain remaining entity") + } + } + func testGetAllEntityComponentTypes() { // Arrange let entityId = createEntity()