From 031db2c1e3bd66d4b3add362e28a316aa768c493 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Sat, 5 Oct 2019 10:03:08 +0200 Subject: [PATCH] Conform Component + Entity Identifier to Identifiable protocol --- .../FirebladeECS/ComponentIdentifier.swift | 6 ++-- Sources/FirebladeECS/EntityIdentifier.swift | 13 +++---- Sources/FirebladeECS/Hashing.swift | 4 +-- Sources/FirebladeECS/Identifiable.swift | 34 +++++++++++++++++++ Sources/FirebladeECS/Nexus+Component.swift | 12 +++---- Sources/FirebladeECS/Nexus+Entity.swift | 10 +++--- Sources/FirebladeECS/Nexus+Family.swift | 2 +- Sources/FirebladeECS/Nexus+FamilyUpdate.swift | 4 +-- Tests/FirebladeECSTests/EntityTests.swift | 6 ++-- Tests/FirebladeECSTests/NexusTests.swift | 6 ++-- 10 files changed, 66 insertions(+), 31 deletions(-) create mode 100644 Sources/FirebladeECS/Identifiable.swift diff --git a/Sources/FirebladeECS/ComponentIdentifier.swift b/Sources/FirebladeECS/ComponentIdentifier.swift index 2ed9d57..d3ae5d7 100644 --- a/Sources/FirebladeECS/ComponentIdentifier.swift +++ b/Sources/FirebladeECS/ComponentIdentifier.swift @@ -6,11 +6,11 @@ // /// Identifies a component by it's meta type -public struct ComponentIdentifier { - @usableFromInline let objectIdentifier: ObjectIdentifier +public struct ComponentIdentifier: Identifiable { + public let id: ObjectIdentifier init(_ type: T.Type) where T: Component { - self.objectIdentifier = ObjectIdentifier(type) + self.id = ObjectIdentifier(type) } } diff --git a/Sources/FirebladeECS/EntityIdentifier.swift b/Sources/FirebladeECS/EntityIdentifier.swift index 6a1dc64..3bf35d1 100644 --- a/Sources/FirebladeECS/EntityIdentifier.swift +++ b/Sources/FirebladeECS/EntityIdentifier.swift @@ -5,16 +5,17 @@ // Created by Christian Treffs on 08.10.17. // -public struct EntityIdentifier { - public static let invalid = EntityIdentifier(.max) - +public struct EntityIdentifier: Identifiable { /// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid. - public let index: Int + public let id: Int public init(_ uint32: UInt32) { - self.index = Int(uint32) + self.id = Int(uint32) } } +extension EntityIdentifier { + public static let invalid = EntityIdentifier(.max) +} extension EntityIdentifier: Equatable { } extension EntityIdentifier: Hashable { } @@ -22,6 +23,6 @@ extension EntityIdentifier: Codable { } extension EntityIdentifier: Comparable { @inlinable public static func < (lhs: EntityIdentifier, rhs: EntityIdentifier) -> Bool { - return lhs.index < rhs.index + return lhs.id < rhs.id } } diff --git a/Sources/FirebladeECS/Hashing.swift b/Sources/FirebladeECS/Hashing.swift index 3e13954..104e530 100644 --- a/Sources/FirebladeECS/Hashing.swift +++ b/Sources/FirebladeECS/Hashing.swift @@ -66,14 +66,14 @@ public func hash(combine hashables: H) -> Int where H.Element: Hash // MARK: - entity component hash extension EntityComponentHash { internal static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash { - let entityIdSwapped = UInt(entityId.index).byteSwapped // needs to be 64 bit + let entityIdSwapped = UInt(entityId.id).byteSwapped // needs to be 64 bit let componentTypeHashUInt = UInt(bitPattern: componentTypeHash) let hashUInt: UInt = componentTypeHashUInt ^ entityIdSwapped return Int(bitPattern: hashUInt) } internal static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash { - let entityIdSwapped = UInt(entityId.index).byteSwapped + let entityIdSwapped = UInt(entityId.id).byteSwapped let entityIdSwappedInt = Int(bitPattern: entityIdSwapped) return hash ^ entityIdSwappedInt } diff --git a/Sources/FirebladeECS/Identifiable.swift b/Sources/FirebladeECS/Identifiable.swift new file mode 100644 index 0000000..bd0f924 --- /dev/null +++ b/Sources/FirebladeECS/Identifiable.swift @@ -0,0 +1,34 @@ +// +// Identifiable.swift +// +// +// Created by Christian Treffs on 05.10.19. +// + +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +#if swift(<5.1) +/// A class of types whose instances hold the value of an entity with stable identity. +public protocol Identifiable { + /// A type representing the stable identity of the entity associated with `self`. + associatedtype ID: Hashable + + /// The stable identity of the entity associated with `self`. + var id: ID { get } +} + +extension Identifiable where Self: AnyObject { + public var id: ObjectIdentifier { + return ObjectIdentifier(self) + } +} +#endif diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index 6195df4..4a0ec73 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -14,7 +14,7 @@ extension Nexus { guard let uniforms = componentsByType[componentId] else { return false } - return uniforms.contains(entityId.index) + return uniforms.contains(entityId.id) } public final func count(components entityId: EntityIdentifier) -> Int { @@ -36,7 +36,7 @@ extension Nexus { if componentsByType[componentId] == nil { componentsByType[componentId] = ManagedContiguousArray() } - componentsByType[componentId]?.insert(component, at: entityId.index) + componentsByType[componentId]?.insert(component, at: entityId.id) // assigns the component id to the entity id if componentIdsByEntity[entityId] == nil { @@ -58,13 +58,13 @@ extension Nexus { guard let uniformComponents = componentsByType[componentId] else { return nil } - return uniformComponents.get(at: entityId.index) + return uniformComponents.get(at: entityId.id) } @inlinable public final func get(unsafeComponent componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component { let uniformComponents = componentsByType[componentId].unsafelyUnwrapped - return uniformComponents.get(unsafeAt: entityId.index) + return uniformComponents.get(unsafeAt: entityId.id) } @inlinable @@ -88,7 +88,7 @@ extension Nexus { @discardableResult public final func remove(component componentId: ComponentIdentifier, from entityId: EntityIdentifier) -> Bool { // delete component instance - componentsByType[componentId]?.remove(at: entityId.index) + componentsByType[componentId]?.remove(at: entityId.id) // unasign component from entity componentIdsByEntity[entityId]?.remove(componentId) @@ -117,6 +117,6 @@ extension Nexus { guard let uniformComponents = componentsByType[componentId] else { return nil } - return uniformComponents.get(at: entityId.index) as? C + return uniformComponents.get(at: entityId.id) as? C } } diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index d8ce86a..b40308b 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -18,7 +18,7 @@ extension Nexus { public func createEntity() -> Entity { let newEntityIdentifier: EntityIdentifier = nextEntityId() let newEntity = Entity(nexus: self, id: newEntityIdentifier) - entityStorage.insert(newEntity, at: newEntityIdentifier.index) + entityStorage.insert(newEntity, at: newEntityIdentifier.id) delegate?.nexusEvent(EntityCreated(entityId: newEntityIdentifier)) return newEntity } @@ -36,22 +36,22 @@ extension Nexus { } public func exists(entity entityId: EntityIdentifier) -> Bool { - return entityStorage.contains(entityId.index) + return entityStorage.contains(entityId.id) } public func get(entity entityId: EntityIdentifier) -> Entity? { - return entityStorage.get(at: entityId.index) + return entityStorage.get(at: entityId.id) } public func get(unsafeEntity entityId: EntityIdentifier) -> Entity { - return entityStorage.get(unsafeAt: entityId.index) + return entityStorage.get(unsafeAt: entityId.id) } @discardableResult public func destroy(entity: Entity) -> Bool { let entityId: EntityIdentifier = entity.identifier - guard entityStorage.remove(at: entityId.index) != nil else { + guard entityStorage.remove(at: entityId.id) != nil else { delegate?.nexusNonFatalError("EntityRemove failure: no entity \(entityId) to remove") return false } diff --git a/Sources/FirebladeECS/Nexus+Family.swift b/Sources/FirebladeECS/Nexus+Family.swift index f7622a3..3895b4d 100644 --- a/Sources/FirebladeECS/Nexus+Family.swift +++ b/Sources/FirebladeECS/Nexus+Family.swift @@ -31,6 +31,6 @@ extension Nexus { } public func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool { - return members(withFamilyTraits: traits).contains(entityId.index) + return members(withFamilyTraits: traits).contains(entityId.id) } } diff --git a/Sources/FirebladeECS/Nexus+FamilyUpdate.swift b/Sources/FirebladeECS/Nexus+FamilyUpdate.swift index 944e706..2c1ab51 100644 --- a/Sources/FirebladeECS/Nexus+FamilyUpdate.swift +++ b/Sources/FirebladeECS/Nexus+FamilyUpdate.swift @@ -64,11 +64,11 @@ extension Nexus { final func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) { precondition(familyMembersByTraits[traits] != nil) - familyMembersByTraits[traits].unsafelyUnwrapped.insert(entityId, at: entityId.index) + familyMembersByTraits[traits].unsafelyUnwrapped.insert(entityId, at: entityId.id) } final func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) { precondition(familyMembersByTraits[traits] != nil) - familyMembersByTraits[traits].unsafelyUnwrapped.remove(at: entityId.index) + familyMembersByTraits[traits].unsafelyUnwrapped.remove(at: entityId.id) } } diff --git a/Tests/FirebladeECSTests/EntityTests.swift b/Tests/FirebladeECSTests/EntityTests.swift index d15452d..9a6803d 100644 --- a/Tests/FirebladeECSTests/EntityTests.swift +++ b/Tests/FirebladeECSTests/EntityTests.swift @@ -11,15 +11,15 @@ import XCTest class EntityTests: XCTestCase { func testEntityIdentifierAndIndex() { let min = EntityIdentifier(.min) - XCTAssertEqual(min.index, Int(UInt32.min)) + XCTAssertEqual(min.id, Int(UInt32.min)) let uRand = UInt32.random(in: UInt32.min...UInt32.max) let rand = EntityIdentifier(uRand) - XCTAssertEqual(rand.index, Int(uRand)) + XCTAssertEqual(rand.id, Int(uRand)) let max = EntityIdentifier(.max) XCTAssertEqual(max, EntityIdentifier.invalid) - XCTAssertEqual(max.index, Int(UInt32.max)) + XCTAssertEqual(max.id, Int(UInt32.max)) } func testEntityIdentifierComparison() { diff --git a/Tests/FirebladeECSTests/NexusTests.swift b/Tests/FirebladeECSTests/NexusTests.swift index 9d296c5..f334879 100644 --- a/Tests/FirebladeECSTests/NexusTests.swift +++ b/Tests/FirebladeECSTests/NexusTests.swift @@ -26,12 +26,12 @@ class NexusTests: XCTestCase { let e0 = nexus.createEntity() - XCTAssertEqual(e0.identifier.index, 0) + XCTAssertEqual(e0.identifier.id, 0) XCTAssertEqual(nexus.numEntities, 1) let e1 = nexus.createEntity(with: Name(name: "Entity 1")) - XCTAssert(e1.identifier.index == 1) + XCTAssert(e1.identifier.id == 1) XCTAssert(nexus.numEntities == 2) //FIXME: XCTAssertNil(e0.name) @@ -43,7 +43,7 @@ class NexusTests: XCTestCase { XCTAssertEqual(nexus.numEntities, 2) let e1: Entity = nexus.get(entity: EntityIdentifier(1))! - XCTAssertEqual(e1.identifier.index, 1) + XCTAssertEqual(e1.identifier.id, 1) XCTAssertTrue(nexus.destroy(entity: e1)) XCTAssertFalse(nexus.destroy(entity: e1))