Merge branch 'feature/codable' into 'develop'
v0.11 | Codable See merge request fireblade/ecs!5
This commit is contained in:
commit
0af7cc0bfe
|
|
@ -46,7 +46,6 @@ fastlane/test_output
|
||||||
Gemfile*
|
Gemfile*
|
||||||
Icon
|
Icon
|
||||||
Network Trash Folder
|
Network Trash Folder
|
||||||
Package.resolved
|
|
||||||
Packages
|
Packages
|
||||||
playground.xcworkspace
|
playground.xcworkspace
|
||||||
Temporary Items
|
Temporary Items
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
image: swift:5.0
|
image: swift:5.2.2
|
||||||
|
|
||||||
#before_script:
|
#before_script:
|
||||||
#- eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
|
#- eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
5.0.3
|
5.2.2
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ opt_in_rules:
|
||||||
- multiline_arguments
|
- multiline_arguments
|
||||||
- multiline_function_chains
|
- multiline_function_chains
|
||||||
- multiline_parameters
|
- multiline_parameters
|
||||||
- multiline_parameters_brackets
|
#- multiline_parameters_brackets
|
||||||
- nimble_operator
|
- nimble_operator
|
||||||
- no_extension_access_modifier
|
- no_extension_access_modifier
|
||||||
- number_separator
|
- number_separator
|
||||||
|
|
|
||||||
12
.travis.yml
12
.travis.yml
|
|
@ -11,14 +11,4 @@ script:
|
||||||
- swift package reset
|
- swift package reset
|
||||||
- swift build
|
- swift build
|
||||||
- swift test
|
- swift test
|
||||||
env:
|
|
||||||
- BADGE=linux
|
|
||||||
- BADGE=osx
|
|
||||||
# hack to get some OS-specific badges
|
|
||||||
# see: https://github.com/travis-ci/travis-ci/issues/9579
|
|
||||||
matrix:
|
|
||||||
exclude:
|
|
||||||
- os: linux
|
|
||||||
env: BADGE=osx
|
|
||||||
- os: osx
|
|
||||||
env: BADGE=linux
|
|
||||||
49
Makefile
49
Makefile
|
|
@ -1,7 +1,23 @@
|
||||||
|
# Version 1.0.0
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
|
||||||
|
# Lint
|
||||||
lint:
|
lint:
|
||||||
swiftlint autocorrect --format
|
swiftlint autocorrect --format
|
||||||
swiftlint lint --quiet
|
swiftlint lint --quiet
|
||||||
|
|
||||||
|
lintErrorOnly:
|
||||||
|
@swiftlint autocorrect --format --quiet
|
||||||
|
@swiftlint lint --quiet | grep error
|
||||||
|
|
||||||
|
# Git
|
||||||
|
precommit: lint genLinuxTests
|
||||||
|
|
||||||
|
submodule:
|
||||||
|
git submodule init
|
||||||
|
git submodule update --recursive
|
||||||
|
|
||||||
|
# Tests
|
||||||
genLinuxTests:
|
genLinuxTests:
|
||||||
swift test --generate-linuxmain
|
swift test --generate-linuxmain
|
||||||
swiftlint autocorrect --format --path Tests/
|
swiftlint autocorrect --format --path Tests/
|
||||||
|
|
@ -9,6 +25,21 @@ genLinuxTests:
|
||||||
test: genLinuxTests
|
test: genLinuxTests
|
||||||
swift test
|
swift test
|
||||||
|
|
||||||
|
# Package
|
||||||
|
latest:
|
||||||
|
swift package update
|
||||||
|
|
||||||
|
resolve:
|
||||||
|
swift package resolve
|
||||||
|
|
||||||
|
# Xcode
|
||||||
|
genXcode:
|
||||||
|
swift package generate-xcodeproj --enable-code-coverage --skip-extra-files
|
||||||
|
|
||||||
|
genXcodeOpen: genXcode
|
||||||
|
open *.xcodeproj
|
||||||
|
|
||||||
|
# Clean
|
||||||
clean:
|
clean:
|
||||||
swift package reset
|
swift package reset
|
||||||
rm -rdf .swiftpm/xcode
|
rm -rdf .swiftpm/xcode
|
||||||
|
|
@ -19,19 +50,7 @@ clean:
|
||||||
cleanArtifacts:
|
cleanArtifacts:
|
||||||
swift package clean
|
swift package clean
|
||||||
|
|
||||||
genXcode:
|
# Test links in README
|
||||||
swift package generate-xcodeproj --enable-code-coverage --skip-extra-files
|
# requires <https://github.com/tcort/markdown-link-check>
|
||||||
|
|
||||||
latest:
|
|
||||||
swift package update
|
|
||||||
|
|
||||||
resolve:
|
|
||||||
swift package resolve
|
|
||||||
|
|
||||||
genXcodeOpen: genXcode
|
|
||||||
open *.xcodeproj
|
|
||||||
|
|
||||||
precommit: lint genLinuxTests
|
|
||||||
|
|
||||||
testReadme:
|
testReadme:
|
||||||
markdown-link-check -p -v ./README.md
|
markdown-link-check -p -v ./README.md
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Fireblade ECS (Entity-Component System)
|
# Fireblade ECS (Entity-Component System)
|
||||||
[](https://travis-ci.com/fireblade-engine/ecs)
|
[](https://travis-ci.com/fireblade-engine/ecs)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://swift.org/download)
|
[](https://swift.org/download)
|
||||||
[](#)
|
[](#)
|
||||||
[](#)
|
[](#)
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ These instructions will get you a copy of the project up and running on your loc
|
||||||
|
|
||||||
### 💻 Installing
|
### 💻 Installing
|
||||||
|
|
||||||
Fireblade ECS is available for all platforms that support [Swift 5.0](https://swift.org/) and higher and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager).
|
Fireblade ECS is available for all platforms that support [Swift 5](https://swift.org/) and higher and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager).
|
||||||
|
|
||||||
Extend the following lines in your `Package.swift` file or use it to create a new project.
|
Extend the following lines in your `Package.swift` file or use it to create a new project.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
//
|
|
||||||
// Component+Access.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Christian Treffs on 25.06.19.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.1)
|
|
||||||
@dynamicMemberLookup
|
|
||||||
public struct ReadableOnly<Comp> where Comp: Component {
|
|
||||||
@usableFromInline let component: Comp
|
|
||||||
|
|
||||||
public init(_ component: Comp) {
|
|
||||||
self.component = component
|
|
||||||
}
|
|
||||||
|
|
||||||
@inlinable
|
|
||||||
public subscript<C>(dynamicMember keyPath: KeyPath<Comp, C>) -> C {
|
|
||||||
return component[keyPath: keyPath]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@dynamicMemberLookup
|
|
||||||
public struct Writable<Comp> where Comp: Component {
|
|
||||||
@usableFromInline let component: Comp
|
|
||||||
|
|
||||||
public init(_ component: Comp) {
|
|
||||||
self.component = component
|
|
||||||
}
|
|
||||||
|
|
||||||
@inlinable
|
|
||||||
public subscript<C>(dynamicMember keyPath: ReferenceWritableKeyPath<Comp, C>) -> C {
|
|
||||||
nonmutating get { return component[keyPath: keyPath] }
|
|
||||||
nonmutating set { component[keyPath: keyPath] = newValue }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -7,14 +7,17 @@
|
||||||
|
|
||||||
/// **Component**
|
/// **Component**
|
||||||
///
|
///
|
||||||
/// A component represents the raw data for one aspect of the object,
|
/// A component represents the raw data for one aspect of an entity.
|
||||||
/// and how it interacts with the world.
|
public protocol Component: AnyObject {
|
||||||
public protocol Component: class {
|
/// Unique, immutable identifier of this component type.
|
||||||
static var identifier: ComponentIdentifier { get }
|
static var identifier: ComponentIdentifier { get }
|
||||||
|
|
||||||
|
/// Unique, immutable identifier of this component type.
|
||||||
var identifier: ComponentIdentifier { get }
|
var identifier: ComponentIdentifier { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Component {
|
extension Component {
|
||||||
public static var identifier: ComponentIdentifier { return ComponentIdentifier(Self.self) }
|
public static var identifier: ComponentIdentifier { ComponentIdentifier(Self.self) }
|
||||||
@inlinable public var identifier: ComponentIdentifier { return Self.identifier }
|
@inline(__always)
|
||||||
|
public var identifier: ComponentIdentifier { Self.identifier }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,19 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
/// Identifies a component by it's meta type
|
/// Identifies a component by it's meta type
|
||||||
public struct ComponentIdentifier: Identifiable {
|
public struct ComponentIdentifier {
|
||||||
public let id: ObjectIdentifier
|
@usableFromInline
|
||||||
|
typealias Hash = Int
|
||||||
|
@usableFromInline
|
||||||
|
typealias StableId = UInt
|
||||||
|
|
||||||
init<T>(_ type: T.Type) where T: Component {
|
@usableFromInline let hash: Hash
|
||||||
self.id = ObjectIdentifier(type)
|
}
|
||||||
|
|
||||||
|
extension ComponentIdentifier {
|
||||||
|
@usableFromInline
|
||||||
|
init<C>(_ componentType: C.Type) where C: Component {
|
||||||
|
self.hash = Nexus.makeOrGetComponentId(componentType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@
|
||||||
extension Entity {
|
extension Entity {
|
||||||
@inlinable
|
@inlinable
|
||||||
public func get<C>() -> C? where C: Component {
|
public func get<C>() -> C? where C: Component {
|
||||||
return nexus.get(for: identifier)
|
nexus.get(for: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func get<A>(component compType: A.Type = A.self) -> A? where A: Component {
|
public func get<A>(component compType: A.Type = A.self) -> A? where A: Component {
|
||||||
return nexus.get(for: identifier)
|
nexus.get(for: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
|
|
|
||||||
|
|
@ -24,24 +24,24 @@ public struct Entity {
|
||||||
|
|
||||||
/// Returns the number of components for this entity.
|
/// Returns the number of components for this entity.
|
||||||
public var numComponents: Int {
|
public var numComponents: Int {
|
||||||
return nexus.count(components: identifier)
|
nexus.count(components: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a component with given type is assigned to this entity.
|
/// Checks if a component with given type is assigned to this entity.
|
||||||
/// - Parameter type: the component type.
|
/// - Parameter type: the component type.
|
||||||
public func has<C>(_ type: C.Type) -> Bool where C: Component {
|
public func has<C>(_ type: C.Type) -> Bool where C: Component {
|
||||||
return has(type.identifier)
|
has(type.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a component with a given component identifier is assigned to this entity.
|
/// Checks if a component with a given component identifier is assigned to this entity.
|
||||||
/// - Parameter compId: the component identifier.
|
/// - Parameter compId: the component identifier.
|
||||||
public func has(_ compId: ComponentIdentifier) -> Bool {
|
public func has(_ compId: ComponentIdentifier) -> Bool {
|
||||||
return nexus.has(componentId: compId, entityId: identifier)
|
nexus.has(componentId: compId, entityId: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if this entity has any components.
|
/// Checks if this entity has any components.
|
||||||
public var hasComponents: Bool {
|
public var hasComponents: Bool {
|
||||||
return nexus.count(components: identifier) > 0
|
nexus.count(components: identifier) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add one or more components to this entity.
|
/// Add one or more components to this entity.
|
||||||
|
|
@ -74,14 +74,14 @@ public struct Entity {
|
||||||
/// - Parameter component: the component.
|
/// - Parameter component: the component.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func remove<C>(_ component: C) -> Entity where C: Component {
|
public func remove<C>(_ component: C) -> Entity where C: Component {
|
||||||
return remove(component.identifier)
|
remove(component.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a component by type from this entity.
|
/// Remove a component by type from this entity.
|
||||||
/// - Parameter compType: the component type.
|
/// - Parameter compType: the component type.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func remove<C>(_ compType: C.Type) -> Entity where C: Component {
|
public func remove<C>(_ compType: C.Type) -> Entity where C: Component {
|
||||||
return remove(compType.identifier)
|
remove(compType.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a component by id from this entity.
|
/// Remove a component by id from this entity.
|
||||||
|
|
@ -106,31 +106,41 @@ public struct Entity {
|
||||||
/// - Parameter entity: The child entity.
|
/// - Parameter entity: The child entity.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func addChild(_ entity: Entity) -> Bool {
|
public func addChild(_ entity: Entity) -> Bool {
|
||||||
return nexus.addChild(entity, to: self)
|
nexus.addChild(entity, to: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove entity as child.
|
/// Remove entity as child.
|
||||||
/// - Parameter entity: The child entity.
|
/// - Parameter entity: The child entity.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func removeChild(_ entity: Entity) -> Bool {
|
public func removeChild(_ entity: Entity) -> Bool {
|
||||||
return nexus.removeChild(entity, from: self)
|
nexus.removeChild(entity, from: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all children from this entity.
|
/// Removes all children from this entity.
|
||||||
public func removeAllChildren() {
|
public func removeAllChildren() {
|
||||||
return nexus.removeAllChildren(from: self)
|
nexus.removeAllChildren(from: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of children for this entity.
|
/// Returns the number of children for this entity.
|
||||||
public var numChildren: Int {
|
public var numChildren: Int {
|
||||||
return nexus.numChildren(for: self)
|
nexus.numChildren(for: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Equatable
|
|
||||||
extension Entity: Equatable {
|
extension Entity: Equatable {
|
||||||
public static func == (lhs: Entity, rhs: Entity) -> Bool {
|
public static func == (lhs: Entity, rhs: Entity) -> Bool {
|
||||||
return lhs.nexus == rhs.nexus &&
|
lhs.nexus === rhs.nexus && lhs.identifier == rhs.identifier
|
||||||
lhs.identifier == rhs.identifier
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Entity: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
"<Entity id:\(identifier.id)>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Entity: CustomDebugStringConvertible {
|
||||||
|
public var debugDescription: String {
|
||||||
|
"<Entity id:\(identifier.id) numComponents:\(numComponents)>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,24 +5,18 @@
|
||||||
// Created by Christian Treffs on 08.10.17.
|
// Created by Christian Treffs on 08.10.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
public struct EntityIdentifier: Identifiable {
|
public struct EntityIdentifier {
|
||||||
/// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid.
|
static let invalid = EntityIdentifier(.max)
|
||||||
public let id: Int
|
|
||||||
|
|
||||||
public init(_ uint32: UInt32) {
|
/// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid.
|
||||||
|
@usableFromInline let id: Int
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
init(_ uint32: UInt32) {
|
||||||
self.id = Int(uint32)
|
self.id = Int(uint32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extension EntityIdentifier {
|
|
||||||
public static let invalid = EntityIdentifier(.max)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension EntityIdentifier: Equatable { }
|
extension EntityIdentifier: Equatable { }
|
||||||
extension EntityIdentifier: Hashable { }
|
extension EntityIdentifier: Hashable { }
|
||||||
extension EntityIdentifier: Codable { }
|
extension EntityIdentifier: Codable { }
|
||||||
extension EntityIdentifier: Comparable {
|
|
||||||
@inlinable
|
|
||||||
public static func < (lhs: EntityIdentifier, rhs: EntityIdentifier) -> Bool {
|
|
||||||
return lhs.id < rhs.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -18,39 +18,38 @@ public struct Family<R> where R: FamilyRequirementsManaging {
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable public var memberIds: UnorderedSparseSet<EntityIdentifier> {
|
@inlinable public var memberIds: UnorderedSparseSet<EntityIdentifier> {
|
||||||
return nexus.members(withFamilyTraits: traits)
|
nexus.members(withFamilyTraits: traits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable public var count: Int {
|
@inlinable public var count: Int {
|
||||||
return memberIds.count
|
memberIds.count
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable public var isEmpty: Bool {
|
@inlinable public var isEmpty: Bool {
|
||||||
return memberIds.isEmpty
|
memberIds.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func canBecomeMember(_ entity: Entity) -> Bool {
|
public func canBecomeMember(_ entity: Entity) -> Bool {
|
||||||
return nexus.canBecomeMember(entity, in: traits)
|
nexus.canBecomeMember(entity, in: traits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func isMember(_ entity: Entity) -> Bool {
|
public func isMember(_ entity: Entity) -> Bool {
|
||||||
return nexus.isMember(entity, in: traits)
|
nexus.isMember(entity, in: traits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Equatable
|
|
||||||
extension Family: Equatable {
|
extension Family: Equatable {
|
||||||
public static func == (lhs: Family<R>, rhs: Family<R>) -> Bool {
|
public static func == (lhs: Family<R>, rhs: Family<R>) -> Bool {
|
||||||
return lhs.nexus == rhs.nexus &&
|
lhs.nexus === rhs.nexus &&
|
||||||
lhs.traits == rhs.traits
|
lhs.traits == rhs.traits
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Family: Sequence {
|
extension Family: Sequence {
|
||||||
__consuming public func makeIterator() -> ComponentsIterator {
|
__consuming public func makeIterator() -> ComponentsIterator {
|
||||||
return ComponentsIterator(family: self)
|
ComponentsIterator(family: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +58,7 @@ extension Family: LazySequenceProtocol { }
|
||||||
// MARK: - components iterator
|
// MARK: - components iterator
|
||||||
extension Family {
|
extension Family {
|
||||||
public struct ComponentsIterator: IteratorProtocol {
|
public struct ComponentsIterator: IteratorProtocol {
|
||||||
@usableFromInline var memberIdsIterator: UnorderedSparseSetIterator<EntityIdentifier>
|
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier>.ElementIterator
|
||||||
@usableFromInline unowned let nexus: Nexus
|
@usableFromInline unowned let nexus: Nexus
|
||||||
|
|
||||||
public init(family: Family<R>) {
|
public init(family: Family<R>) {
|
||||||
|
|
@ -82,11 +81,11 @@ extension Family.ComponentsIterator: LazySequenceProtocol { }
|
||||||
// MARK: - entity iterator
|
// MARK: - entity iterator
|
||||||
extension Family {
|
extension Family {
|
||||||
@inlinable public var entities: EntityIterator {
|
@inlinable public var entities: EntityIterator {
|
||||||
return EntityIterator(family: self)
|
EntityIterator(family: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct EntityIterator: IteratorProtocol {
|
public struct EntityIterator: IteratorProtocol {
|
||||||
@usableFromInline var memberIdsIterator: UnorderedSparseSetIterator<EntityIdentifier>
|
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier>.ElementIterator
|
||||||
@usableFromInline unowned let nexus: Nexus
|
@usableFromInline unowned let nexus: Nexus
|
||||||
|
|
||||||
public init(family: Family<R>) {
|
public init(family: Family<R>) {
|
||||||
|
|
@ -108,11 +107,11 @@ extension Family.EntityIterator: LazySequenceProtocol { }
|
||||||
// MARK: - entity component iterator
|
// MARK: - entity component iterator
|
||||||
extension Family {
|
extension Family {
|
||||||
@inlinable public var entityAndComponents: EntityComponentIterator {
|
@inlinable public var entityAndComponents: EntityComponentIterator {
|
||||||
return EntityComponentIterator(family: self)
|
EntityComponentIterator(family: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct EntityComponentIterator: IteratorProtocol {
|
public struct EntityComponentIterator: IteratorProtocol {
|
||||||
@usableFromInline var memberIdsIterator: UnorderedSparseSetIterator<EntityIdentifier>
|
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier>.ElementIterator
|
||||||
@usableFromInline unowned let nexus: Nexus
|
@usableFromInline unowned let nexus: Nexus
|
||||||
|
|
||||||
public init(family: Family<R>) {
|
public init(family: Family<R>) {
|
||||||
|
|
@ -136,7 +135,7 @@ extension Family.EntityComponentIterator: LazySequenceProtocol { }
|
||||||
extension Family {
|
extension Family {
|
||||||
@inlinable
|
@inlinable
|
||||||
public func descendRelatives(from root: Entity) -> RelativesIterator {
|
public func descendRelatives(from root: Entity) -> RelativesIterator {
|
||||||
return RelativesIterator(family: self, root: root)
|
RelativesIterator(family: self, root: root)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RelativesIterator: IteratorProtocol {
|
public struct RelativesIterator: IteratorProtocol {
|
||||||
|
|
@ -161,7 +160,7 @@ extension Family {
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func aggregateRelativesBreathFirst(_ parent: EntityIdentifier) {
|
mutating func aggregateRelativesBreathFirst(_ parent: EntityIdentifier) {
|
||||||
guard let children = nexus.parentChildrenMap[parent] else {
|
guard let children = nexus.childrenByParentEntity[parent] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
children
|
children
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ extension Nexus {
|
||||||
requires componentA: A.Type,
|
requires componentA: A.Type,
|
||||||
excludesAll excludedComponents: Component.Type...
|
excludesAll excludedComponents: Component.Type...
|
||||||
) -> Family1<A> where A: Component {
|
) -> Family1<A> where A: Component {
|
||||||
return Family1<A>(nexus: self,
|
Family1<A>(nexus: self,
|
||||||
requiresAll: componentA,
|
requiresAll: componentA,
|
||||||
excludesAll: excludedComponents)
|
excludesAll: excludedComponents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ extension Nexus {
|
||||||
_ componentB: B.Type,
|
_ componentB: B.Type,
|
||||||
excludesAll excludedComponents: Component.Type...
|
excludesAll excludedComponents: Component.Type...
|
||||||
) -> Family2<A, B> where A: Component, B: Component {
|
) -> Family2<A, B> where A: Component, B: Component {
|
||||||
return Family2<A, B>(
|
Family2<A, B>(
|
||||||
nexus: self,
|
nexus: self,
|
||||||
requiresAll: (componentA, componentB),
|
requiresAll: (componentA, componentB),
|
||||||
excludesAll: excludedComponents
|
excludesAll: excludedComponents
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ extension Nexus {
|
||||||
_ componentC: C.Type,
|
_ componentC: C.Type,
|
||||||
excludesAll excludedComponents: Component.Type...
|
excludesAll excludedComponents: Component.Type...
|
||||||
) -> Family3<A, B, C> where A: Component, B: Component, C: Component {
|
) -> Family3<A, B, C> where A: Component, B: Component, C: Component {
|
||||||
return Family3(
|
Family3(
|
||||||
nexus: self,
|
nexus: self,
|
||||||
requiresAll: (componentA, componentB, componentC),
|
requiresAll: (componentA, componentB, componentC),
|
||||||
excludesAll: excludedComponents
|
excludesAll: excludedComponents
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ extension Nexus {
|
||||||
_ componentD: D.Type,
|
_ componentD: D.Type,
|
||||||
excludesAll excludedComponents: Component.Type...
|
excludesAll excludedComponents: Component.Type...
|
||||||
) -> Family4<A, B, C, D> where A: Component, B: Component, C: Component, D: Component {
|
) -> Family4<A, B, C, D> where A: Component, B: Component, C: Component, D: Component {
|
||||||
return Family4(
|
Family4(
|
||||||
nexus: self,
|
nexus: self,
|
||||||
requiresAll: (componentA, componentB, componentC, componentD),
|
requiresAll: (componentA, componentB, componentC, componentD),
|
||||||
excludesAll: excludedComponents
|
excludesAll: excludedComponents
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ extension Nexus {
|
||||||
_ componentE: E.Type,
|
_ componentE: E.Type,
|
||||||
excludesAll excludedComponents: Component.Type...
|
excludesAll excludedComponents: Component.Type...
|
||||||
) -> Family5<A, B, C, D, E> where A: Component, B: Component, C: Component, D: Component, E: Component {
|
) -> Family5<A, B, C, D, E> where A: Component, B: Component, C: Component, D: Component, E: Component {
|
||||||
return Family5(
|
Family5(
|
||||||
nexus: self,
|
nexus: self,
|
||||||
requiresAll: (componentA, componentB, componentC, componentD, componentE),
|
requiresAll: (componentA, componentB, componentC, componentD, componentE),
|
||||||
excludesAll: excludedComponents
|
excludesAll: excludedComponents
|
||||||
|
|
|
||||||
|
|
@ -23,50 +23,48 @@ public struct FamilyTraitSet {
|
||||||
self.setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll])
|
self.setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll])
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - match
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func isMatch(components: Set<ComponentIdentifier>) -> Bool {
|
public func isMatch(components: Set<ComponentIdentifier>) -> Bool {
|
||||||
return hasAll(components) && hasNone(components)
|
hasAll(components) && hasNone(components)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func hasAll(_ components: Set<ComponentIdentifier>) -> Bool {
|
public func hasAll(_ components: Set<ComponentIdentifier>) -> Bool {
|
||||||
return requiresAll.isSubset(of: components)
|
requiresAll.isSubset(of: components)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func hasNone(_ components: Set<ComponentIdentifier>) -> Bool {
|
public func hasNone(_ components: Set<ComponentIdentifier>) -> Bool {
|
||||||
return excludesAll.isDisjoint(with: components)
|
excludesAll.isDisjoint(with: components)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - valid
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public static func isValid(requiresAll: Set<ComponentIdentifier>, excludesAll: Set<ComponentIdentifier>) -> Bool {
|
public static func isValid(requiresAll: Set<ComponentIdentifier>, excludesAll: Set<ComponentIdentifier>) -> Bool {
|
||||||
return !requiresAll.isEmpty &&
|
!requiresAll.isEmpty &&
|
||||||
requiresAll.isDisjoint(with: excludesAll)
|
requiresAll.isDisjoint(with: excludesAll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Equatable
|
|
||||||
extension FamilyTraitSet: Equatable {
|
extension FamilyTraitSet: Equatable {
|
||||||
public static func == (lhs: FamilyTraitSet, rhs: FamilyTraitSet) -> Bool {
|
public static func == (lhs: FamilyTraitSet, rhs: FamilyTraitSet) -> Bool {
|
||||||
return lhs.setHash == rhs.setHash
|
lhs.setHash == rhs.setHash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Hashable
|
|
||||||
extension FamilyTraitSet: Hashable {
|
extension FamilyTraitSet: Hashable {
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(setHash)
|
hasher.combine(setHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FamilyTraitSet: CustomStringConvertible, CustomDebugStringConvertible {
|
extension FamilyTraitSet: CustomStringConvertible {
|
||||||
@inlinable public var description: String {
|
@inlinable public var description: String {
|
||||||
return "<FamilyTraitSet [requiresAll:\(requiresAll.description) excludesAll:\(excludesAll.description)]>"
|
"<FamilyTraitSet [requiresAll:\(requiresAll.description) excludesAll:\(excludesAll.description)]>"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@inlinable public var debugDescription: String {
|
|
||||||
return "<FamilyTraitSet [requiresAll:\(requiresAll.debugDescription) excludesAll: \(excludesAll.debugDescription)]>"
|
extension FamilyTraitSet: CustomDebugStringConvertible {
|
||||||
|
@inlinable public var debugDescription: String {
|
||||||
|
"<FamilyTraitSet [requiresAll:\(requiresAll.debugDescription) excludesAll: \(excludesAll.debugDescription)]>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,3 +83,52 @@ extension EntityComponentHash {
|
||||||
return EntityIdentifier(UInt32(truncatingIfNeeded: entityId))
|
return EntityIdentifier(UInt32(truncatingIfNeeded: entityId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - string hashing
|
||||||
|
/// https://stackoverflow.com/a/52440609
|
||||||
|
public enum StringHashing {
|
||||||
|
/// *Waren Singer djb2*
|
||||||
|
///
|
||||||
|
/// <https://stackoverflow.com/a/43149500>
|
||||||
|
public static func singer_djb2(_ utf8String: String) -> UInt {
|
||||||
|
var hash = UInt(5381)
|
||||||
|
var iter = utf8String.unicodeScalars.makeIterator()
|
||||||
|
while let char = iter.next() {
|
||||||
|
hash = 127 * (hash & 0x00ffffffffffffff) + UInt(char.value)
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// *Dan Bernstein djb2*
|
||||||
|
///
|
||||||
|
/// This algorithm (k=33) was first reported by dan bernstein many years ago in comp.lang.c.
|
||||||
|
/// Another version of this algorithm (now favored by bernstein) uses xor: hash(i) = hash(i - 1) * 33 ^ str[i];
|
||||||
|
/// The magic of number 33 (why it works better than many other constants, prime or not) has never been adequately explained.
|
||||||
|
///
|
||||||
|
/// <http://www.cse.yorku.ca/~oz/hash.html>
|
||||||
|
public static func bernstein_djb2(_ string: String) -> UInt {
|
||||||
|
var hash: UInt = 5381
|
||||||
|
var iter = string.unicodeScalars.makeIterator()
|
||||||
|
while let char = iter.next() {
|
||||||
|
hash = (hash << 5) &+ hash &+ UInt(char.value)
|
||||||
|
//hash = ((hash << 5) + hash) + UInt(c.value)
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// *sdbm*
|
||||||
|
///
|
||||||
|
/// This algorithm was created for sdbm (a public-domain reimplementation of ndbm) database library.
|
||||||
|
/// It was found to do well in scrambling bits, causing better distribution of the keys and fewer splits.
|
||||||
|
/// It also happens to be a good general hashing function with good distribution.
|
||||||
|
///
|
||||||
|
/// <http://www.cse.yorku.ca/~oz/hash.html>
|
||||||
|
public static func sdbm(_ string: String) -> UInt {
|
||||||
|
var hash: UInt = 0
|
||||||
|
var iter = string.unicodeScalars.makeIterator()
|
||||||
|
while let char = iter.next() {
|
||||||
|
hash = (UInt(char.value) &+ (hash << 6) &+ (hash << 16))
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
//
|
|
||||||
// ManagedContiguousArray.swift
|
|
||||||
// FirebladeECS
|
|
||||||
//
|
|
||||||
// Created by Christian Treffs on 28.10.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
public class ManagedContiguousArray<Element> {
|
|
||||||
public typealias Index = Int
|
|
||||||
private let chunkSize: Int
|
|
||||||
private var size: Int = 0
|
|
||||||
private var store: ContiguousArray<Element?> = []
|
|
||||||
|
|
||||||
public init(minCount: Int = 4096) {
|
|
||||||
chunkSize = minCount
|
|
||||||
store = ContiguousArray<Element?>(repeating: nil, count: minCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
public var count: Int {
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
public func insert(_ element: Element, at index: Int) -> Bool {
|
|
||||||
if needsToGrow(index) {
|
|
||||||
grow(to: index)
|
|
||||||
}
|
|
||||||
if store[index] == nil {
|
|
||||||
size += 1
|
|
||||||
}
|
|
||||||
store[index] = element
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
public func contains(_ index: Index) -> Bool {
|
|
||||||
if store.count <= index {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return store[index] != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public func get(at index: Index) -> Element? {
|
|
||||||
return store[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
public func get(unsafeAt index: Index) -> Element {
|
|
||||||
return store[index].unsafelyUnwrapped
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
public func remove(at index: Index) -> Bool {
|
|
||||||
if store[index] != nil {
|
|
||||||
size -= 1
|
|
||||||
}
|
|
||||||
store[index] = nil
|
|
||||||
if size == 0 {
|
|
||||||
clear()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public func clear(keepingCapacity: Bool = false) {
|
|
||||||
size = 0
|
|
||||||
store.removeAll(keepingCapacity: keepingCapacity)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func needsToGrow(_ index: Index) -> Bool {
|
|
||||||
return index > store.count - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
private func grow(to index: Index) {
|
|
||||||
let newCapacity: Int = calculateCapacity(to: index)
|
|
||||||
let newCount: Int = newCapacity - store.count
|
|
||||||
store += ContiguousArray<Element?>(repeating: nil, count: newCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func calculateCapacity(to index: Index) -> Int {
|
|
||||||
let delta = Float(index) / Float(chunkSize)
|
|
||||||
let multiplier = Int(delta.rounded(.up)) + 1
|
|
||||||
return multiplier * chunkSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Equatable
|
|
||||||
extension ManagedContiguousArray: Equatable where Element: Equatable {
|
|
||||||
public static func == (lhs: ManagedContiguousArray<Element>, rhs: ManagedContiguousArray<Element>) -> Bool {
|
|
||||||
return lhs.store == rhs.store
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
extension Nexus {
|
extension Nexus {
|
||||||
public final var numComponents: Int {
|
public final var numComponents: Int {
|
||||||
return componentsByType.reduce(0) { $0 + $1.value.count }
|
componentsByType.reduce(0) { $0 + $1.value.count }
|
||||||
}
|
}
|
||||||
|
|
||||||
public final func has(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> Bool {
|
public final func has(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> Bool {
|
||||||
|
|
@ -18,14 +18,14 @@ extension Nexus {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final func count(components entityId: EntityIdentifier) -> Int {
|
public final func count(components entityId: EntityIdentifier) -> Int {
|
||||||
return componentIdsByEntity[entityId]?.count ?? 0
|
componentIdsByEntity[entityId]?.count ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public final func assign(component: Component, to entity: Entity) {
|
public final func assign(component: Component, to entity: Entity) {
|
||||||
let componentId: ComponentIdentifier = component.identifier
|
let componentId: ComponentIdentifier = component.identifier
|
||||||
let entityId: EntityIdentifier = entity.identifier
|
let entityId: EntityIdentifier = entity.identifier
|
||||||
|
|
||||||
/// test if component is already assigned
|
// test if component is already assigned
|
||||||
guard !has(componentId: componentId, entityId: entityId) else {
|
guard !has(componentId: componentId, entityId: entityId) else {
|
||||||
delegate?.nexusNonFatalError("ComponentAdd collision: \(entityId) already has a component \(component)")
|
delegate?.nexusNonFatalError("ComponentAdd collision: \(entityId) already has a component \(component)")
|
||||||
assertionFailure("ComponentAdd collision: \(entityId) already has a component \(component)")
|
assertionFailure("ComponentAdd collision: \(entityId) already has a component \(component)")
|
||||||
|
|
@ -34,7 +34,7 @@ extension Nexus {
|
||||||
|
|
||||||
// add component instances to uniform component stores
|
// add component instances to uniform component stores
|
||||||
if componentsByType[componentId] == nil {
|
if componentsByType[componentId] == nil {
|
||||||
componentsByType[componentId] = ManagedContiguousArray<Component>()
|
componentsByType[componentId] = UnorderedSparseSet<Component>()
|
||||||
}
|
}
|
||||||
componentsByType[componentId]?.insert(component, at: entityId.id)
|
componentsByType[componentId]?.insert(component, at: entityId.id)
|
||||||
|
|
||||||
|
|
@ -76,13 +76,13 @@ extension Nexus {
|
||||||
@inlinable
|
@inlinable
|
||||||
public final func get<C>(unsafeComponentFor entityId: EntityIdentifier) -> C where C: Component {
|
public final func get<C>(unsafeComponentFor entityId: EntityIdentifier) -> C where C: Component {
|
||||||
let component: Component = get(unsafeComponent: C.identifier, for: entityId)
|
let component: Component = get(unsafeComponent: C.identifier, for: entityId)
|
||||||
/// components are guaranteed to be reference tyes so unsafeDowncast is applicable here
|
// components are guaranteed to be reference tyes so unsafeDowncast is applicable here
|
||||||
return unsafeDowncast(component, to: C.self)
|
return unsafeDowncast(component, to: C.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public final func get(components entityId: EntityIdentifier) -> Set<ComponentIdentifier>? {
|
public final func get(components entityId: EntityIdentifier) -> Set<ComponentIdentifier>? {
|
||||||
return componentIdsByEntity[entityId]
|
componentIdsByEntity[entityId]
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@ extension Nexus {
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func createEntity() -> Entity {
|
public func createEntity() -> Entity {
|
||||||
let newEntityIdentifier: EntityIdentifier = nextEntityId()
|
let newEntityIdentifier: EntityIdentifier = nextEntityId()
|
||||||
let newEntity = Entity(nexus: self, id: newEntityIdentifier)
|
entityStorage.insert(newEntityIdentifier, at: newEntityIdentifier.id)
|
||||||
entityStorage.insert(newEntity, at: newEntityIdentifier.id)
|
|
||||||
delegate?.nexusEvent(EntityCreated(entityId: newEntityIdentifier))
|
delegate?.nexusEvent(EntityCreated(entityId: newEntityIdentifier))
|
||||||
return newEntity
|
return Entity(nexus: self, id: newEntityIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
|
|
@ -32,31 +31,37 @@ extension Nexus {
|
||||||
|
|
||||||
/// Number of entities in nexus.
|
/// Number of entities in nexus.
|
||||||
public var numEntities: Int {
|
public var numEntities: Int {
|
||||||
return entityStorage.count
|
entityStorage.count
|
||||||
}
|
}
|
||||||
|
|
||||||
public func exists(entity entityId: EntityIdentifier) -> Bool {
|
public func exists(entity entityId: EntityIdentifier) -> Bool {
|
||||||
return entityStorage.contains(entityId.id)
|
entityStorage.contains(entityId.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func get(entity entityId: EntityIdentifier) -> Entity? {
|
public func get(entity entityId: EntityIdentifier) -> Entity? {
|
||||||
return entityStorage.get(at: entityId.id)
|
guard let id = entityStorage.get(at: entityId.id) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Entity(nexus: self, id: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func get(unsafeEntity entityId: EntityIdentifier) -> Entity {
|
public func get(unsafeEntity entityId: EntityIdentifier) -> Entity {
|
||||||
return entityStorage.get(unsafeAt: entityId.id)
|
Entity(nexus: self, id: entityStorage.get(unsafeAt: entityId.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func destroy(entity: Entity) -> Bool {
|
public func destroy(entity: Entity) -> Bool {
|
||||||
let entityId: EntityIdentifier = entity.identifier
|
self.destroy(entityId: entity.identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public func destroy(entityId: EntityIdentifier) -> Bool {
|
||||||
guard entityStorage.remove(at: entityId.id) != nil else {
|
guard entityStorage.remove(at: entityId.id) != nil else {
|
||||||
delegate?.nexusNonFatalError("EntityRemove failure: no entity \(entityId) to remove")
|
delegate?.nexusNonFatalError("EntityRemove failure: no entity \(entityId) to remove")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAllChildren(from: entity)
|
removeAllChildren(from: entityId)
|
||||||
|
|
||||||
if removeAll(componentes: entityId) {
|
if removeAll(componentes: entityId) {
|
||||||
update(familyMembership: entityId)
|
update(familyMembership: entityId)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
extension Nexus {
|
extension Nexus {
|
||||||
public final var numFamilies: Int {
|
public final var numFamilies: Int {
|
||||||
return familyMembersByTraits.keys.count
|
familyMembersByTraits.keys.count
|
||||||
}
|
}
|
||||||
|
|
||||||
public func canBecomeMember(_ entity: Entity, in traits: FamilyTraitSet) -> Bool {
|
public func canBecomeMember(_ entity: Entity, in traits: FamilyTraitSet) -> Bool {
|
||||||
|
|
@ -19,18 +19,18 @@ extension Nexus {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet<EntityIdentifier> {
|
public func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet<EntityIdentifier> {
|
||||||
return familyMembersByTraits[traits] ?? UnorderedSparseSet<EntityIdentifier>()
|
familyMembersByTraits[traits] ?? UnorderedSparseSet<EntityIdentifier>()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool {
|
public func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool {
|
||||||
return isMember(entity.identifier, in: family)
|
isMember(entity.identifier, in: family)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isMember(_ entityId: EntityIdentifier, in family: FamilyTraitSet) -> Bool {
|
public func isMember(_ entityId: EntityIdentifier, in family: FamilyTraitSet) -> Bool {
|
||||||
return isMember(entity: entityId, inFamilyWithTraits: family)
|
isMember(entity: entityId, inFamilyWithTraits: family)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool {
|
public func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool {
|
||||||
return members(withFamilyTraits: traits).contains(entityId.id)
|
members(withFamilyTraits: traits).contains(entityId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,15 @@ extension Nexus {
|
||||||
}
|
}
|
||||||
|
|
||||||
familyMembersByTraits[traits] = UnorderedSparseSet<EntityIdentifier>()
|
familyMembersByTraits[traits] = UnorderedSparseSet<EntityIdentifier>()
|
||||||
|
defer { delegate?.nexusEvent(FamilyCreated(family: traits)) }
|
||||||
update(familyMembership: traits)
|
update(familyMembership: traits)
|
||||||
}
|
}
|
||||||
|
|
||||||
final func update(familyMembership traits: FamilyTraitSet) {
|
final func update(familyMembership traits: FamilyTraitSet) {
|
||||||
// FIXME: iterating all entities is costly for many entities
|
// FIXME: iterating all entities is costly for many entities
|
||||||
var iter = entityStorage.makeIterator()
|
var iter = entityStorage.makeIterator()
|
||||||
while let entity = iter.next() {
|
while let entityId = iter.next() {
|
||||||
update(membership: traits, for: entity.identifier)
|
update(membership: traits, for: entityId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,25 +51,26 @@ extension Nexus {
|
||||||
case (true, false):
|
case (true, false):
|
||||||
add(entityWithId: entityId, toFamilyWithTraits: traits)
|
add(entityWithId: entityId, toFamilyWithTraits: traits)
|
||||||
delegate?.nexusEvent(FamilyMemberAdded(member: entityId, toFamily: traits))
|
delegate?.nexusEvent(FamilyMemberAdded(member: entityId, toFamily: traits))
|
||||||
return
|
|
||||||
|
|
||||||
case (false, true):
|
case (false, true):
|
||||||
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
|
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
|
||||||
delegate?.nexusEvent(FamilyMemberRemoved(member: entityId, from: traits))
|
delegate?.nexusEvent(FamilyMemberRemoved(member: entityId, from: traits))
|
||||||
return
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) {
|
final func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) {
|
||||||
precondition(familyMembersByTraits[traits] != nil)
|
familyMembersByTraits[traits]!.insert(entityId, at: entityId.id)
|
||||||
familyMembersByTraits[traits].unsafelyUnwrapped.insert(entityId, at: entityId.id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) {
|
final func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) {
|
||||||
precondition(familyMembersByTraits[traits] != nil)
|
familyMembersByTraits[traits]!.remove(at: entityId.id)
|
||||||
familyMembersByTraits[traits].unsafelyUnwrapped.remove(at: entityId.id)
|
if familyMembersByTraits[traits]!.isEmpty {
|
||||||
|
// delete family if no more entities are present
|
||||||
|
familyMembersByTraits[traits] = nil
|
||||||
|
delegate?.nexusEvent(FamilyDestroyed(family: traits))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
extension Nexus {
|
extension Nexus {
|
||||||
public final func addChild(_ child: Entity, to parent: Entity) -> Bool {
|
public final func addChild(_ child: Entity, to parent: Entity) -> Bool {
|
||||||
let inserted: Bool
|
let inserted: Bool
|
||||||
if parentChildrenMap[parent.identifier] == nil {
|
if childrenByParentEntity[parent.identifier] == nil {
|
||||||
parentChildrenMap[parent.identifier] = [child.identifier]
|
childrenByParentEntity[parent.identifier] = [child.identifier]
|
||||||
inserted = true
|
inserted = true
|
||||||
} else {
|
} else {
|
||||||
let (isNewMember, _) = parentChildrenMap[parent.identifier]!.insert(child.identifier)
|
let (isNewMember, _) = childrenByParentEntity[parent.identifier]!.insert(child.identifier)
|
||||||
inserted = isNewMember
|
inserted = isNewMember
|
||||||
}
|
}
|
||||||
if inserted {
|
if inserted {
|
||||||
|
|
@ -22,12 +22,12 @@ extension Nexus {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final func removeChild(_ child: Entity, from parent: Entity) -> Bool {
|
public final func removeChild(_ child: Entity, from parent: Entity) -> Bool {
|
||||||
return removeChild(child.identifier, from: parent.identifier)
|
removeChild(child.identifier, from: parent.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public final func removeChild(_ child: EntityIdentifier, from parent: EntityIdentifier) -> Bool {
|
public final func removeChild(_ child: EntityIdentifier, from parent: EntityIdentifier) -> Bool {
|
||||||
let removed: Bool = parentChildrenMap[parent]?.remove(child) != nil
|
let removed: Bool = childrenByParentEntity[parent]?.remove(child) != nil
|
||||||
if removed {
|
if removed {
|
||||||
delegate?.nexusEvent(ChildRemoved(parent: parent, child: child))
|
delegate?.nexusEvent(ChildRemoved(parent: parent, child: child))
|
||||||
}
|
}
|
||||||
|
|
@ -35,11 +35,15 @@ extension Nexus {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final func removeAllChildren(from parent: Entity) {
|
public final func removeAllChildren(from parent: Entity) {
|
||||||
parentChildrenMap[parent.identifier]?.forEach { removeChild($0, from: parent.identifier) }
|
self.removeAllChildren(from: parent.identifier)
|
||||||
return parentChildrenMap[parent.identifier] = nil
|
}
|
||||||
|
|
||||||
|
public final func removeAllChildren(from parentId: EntityIdentifier) {
|
||||||
|
childrenByParentEntity[parentId]?.forEach { removeChild($0, from: parentId) }
|
||||||
|
return childrenByParentEntity[parentId] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public final func numChildren(for entity: Entity) -> Int {
|
public final func numChildren(for entity: Entity) -> Int {
|
||||||
return parentChildrenMap[entity.identifier]?.count ?? 0
|
childrenByParentEntity[entity.identifier]?.count ?? 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,85 +6,95 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
public final class Nexus {
|
public final class Nexus {
|
||||||
public final weak var delegate: NexusEventDelegate?
|
|
||||||
|
|
||||||
/// Main entity storage.
|
/// Main entity storage.
|
||||||
/// Entities are tightly packed by EntityIdentifier.
|
/// Entities are tightly packed by EntityIdentifier.
|
||||||
@usableFromInline final var entityStorage: UnorderedSparseSet<Entity>
|
@usableFromInline final var entityStorage: UnorderedSparseSet<EntityIdentifier>
|
||||||
|
|
||||||
|
/// Entity ids that are currently not used.
|
||||||
|
@usableFromInline final var freeEntities: [EntityIdentifier]
|
||||||
|
|
||||||
/// - Key: ComponentIdentifier aka component type.
|
/// - Key: ComponentIdentifier aka component type.
|
||||||
/// - Value: Array of component instances of same type (uniform).
|
/// - Value: Array of component instances of same type (uniform).
|
||||||
/// New component instances are appended.
|
/// New component instances are appended.
|
||||||
@usableFromInline final var componentsByType: [ComponentIdentifier: ManagedContiguousArray<Component>]
|
@usableFromInline final var componentsByType: [ComponentIdentifier: UnorderedSparseSet<Component>]
|
||||||
|
|
||||||
/// - Key: EntityIdentifier aka entity index
|
/// - Key: EntityIdentifier aka entity index
|
||||||
/// - Value: Set of unique component types (ComponentIdentifier).
|
/// - Value: Set of unique component types (ComponentIdentifier).
|
||||||
/// Each element is a component identifier associated with this entity.
|
/// Each element is a component identifier associated with this entity.
|
||||||
@usableFromInline final var componentIdsByEntity: [EntityIdentifier: Set<ComponentIdentifier>]
|
@usableFromInline final var componentIdsByEntity: [EntityIdentifier: Set<ComponentIdentifier>]
|
||||||
|
|
||||||
/// Entity ids that are currently not used.
|
/// - Key: A parent entity id.
|
||||||
@usableFromInline final var freeEntities: ContiguousArray<EntityIdentifier>
|
/// - Value: Adjacency Set of all associated children.
|
||||||
|
@usableFromInline final var childrenByParentEntity: [EntityIdentifier: Set<EntityIdentifier>]
|
||||||
|
|
||||||
/// - Key: FamilyTraitSet aka component types that make up one distinct family.
|
/// - Key: FamilyTraitSet aka component types that make up one distinct family.
|
||||||
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
|
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
|
||||||
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier>]
|
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier>]
|
||||||
|
|
||||||
/// - Key: A parent entity id.
|
public final weak var delegate: NexusEventDelegate?
|
||||||
/// - Value: Adjacency Set of all associated children.
|
|
||||||
@usableFromInline final var parentChildrenMap: [EntityIdentifier: Set<EntityIdentifier>]
|
|
||||||
|
|
||||||
public init() {
|
public convenience init() {
|
||||||
entityStorage = UnorderedSparseSet<Entity>()
|
self.init(entityStorage: UnorderedSparseSet<EntityIdentifier>(),
|
||||||
componentsByType = [:]
|
componentsByType: [:],
|
||||||
componentIdsByEntity = [:]
|
componentsByEntity: [:],
|
||||||
freeEntities = ContiguousArray<EntityIdentifier>()
|
freeEntities: [],
|
||||||
familyMembersByTraits = [:]
|
familyMembersByTraits: [:],
|
||||||
parentChildrenMap = [:]
|
childrenByParentEntity: [:])
|
||||||
}
|
}
|
||||||
|
|
||||||
public final func clear() {
|
internal init(entityStorage: UnorderedSparseSet<EntityIdentifier>,
|
||||||
var iter = entityStorage.makeIterator()
|
componentsByType: [ComponentIdentifier: UnorderedSparseSet<Component>],
|
||||||
while let entity = iter.next() {
|
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
|
||||||
destroy(entity: entity)
|
freeEntities: [EntityIdentifier],
|
||||||
}
|
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier>],
|
||||||
|
childrenByParentEntity: [EntityIdentifier: Set<EntityIdentifier>]) {
|
||||||
entityStorage.removeAll()
|
self.entityStorage = entityStorage
|
||||||
freeEntities.removeAll()
|
self.componentsByType = componentsByType
|
||||||
|
self.componentIdsByEntity = componentsByEntity
|
||||||
assert(entityStorage.isEmpty)
|
self.freeEntities = freeEntities
|
||||||
assert(componentsByType.values.reduce(0) { $0 + $1.count } == 0)
|
self.familyMembersByTraits = familyMembersByTraits
|
||||||
assert(componentIdsByEntity.values.reduce(0) { $0 + $1.count } == 0)
|
self.childrenByParentEntity = childrenByParentEntity
|
||||||
assert(freeEntities.isEmpty)
|
|
||||||
assert(familyMembersByTraits.values.reduce(0) { $0 + $1.count } == 0)
|
|
||||||
|
|
||||||
componentsByType.removeAll()
|
|
||||||
componentIdsByEntity.removeAll()
|
|
||||||
familyMembersByTraits.removeAll()
|
|
||||||
parentChildrenMap.removeAll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
clear()
|
clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final func clear() {
|
||||||
|
entityStorage.forEach { destroy(entityId: $0) }
|
||||||
|
entityStorage.removeAll()
|
||||||
|
freeEntities.removeAll()
|
||||||
|
componentsByType.removeAll()
|
||||||
|
componentIdsByEntity.removeAll()
|
||||||
|
familyMembersByTraits.removeAll()
|
||||||
|
childrenByParentEntity.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var knownUniqueComponentTypes: Set<ComponentIdentifier> {
|
||||||
|
Set<ComponentIdentifier>(stableComponentIdentifierMap.keys.map { ComponentIdentifier(hash: $0) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Equatable
|
// MARK: - centralized component identifier mapping
|
||||||
extension Nexus: Equatable {
|
extension Nexus {
|
||||||
@inlinable
|
internal static var stableComponentIdentifierMap: [ComponentIdentifier.Hash: ComponentIdentifier.StableId] = [:]
|
||||||
public static func == (lhs: Nexus, rhs: Nexus) -> Bool {
|
|
||||||
return lhs.entityStorage == rhs.entityStorage &&
|
internal static func makeOrGetComponentId<C>(_ componentType: C.Type) -> ComponentIdentifier.Hash where C: Component {
|
||||||
lhs.componentIdsByEntity == rhs.componentIdsByEntity &&
|
/// object identifier hash (only stable during runtime) - arbitrary hash is ok.
|
||||||
lhs.freeEntities == rhs.freeEntities &&
|
let objIdHash = ObjectIdentifier(componentType).hashValue
|
||||||
lhs.familyMembersByTraits == rhs.familyMembersByTraits &&
|
// if we do not know this component type yet - we register a stable identifier generator for it.
|
||||||
lhs.componentsByType.keys == rhs.componentsByType.keys &&
|
if stableComponentIdentifierMap[objIdHash] == nil {
|
||||||
lhs.parentChildrenMap == rhs.parentChildrenMap
|
let string = String(describing: C.self)
|
||||||
// NOTE: components are not equatable (yet)
|
let stableHash = StringHashing.singer_djb2(string)
|
||||||
|
stableComponentIdentifierMap[objIdHash] = stableHash
|
||||||
|
}
|
||||||
|
return objIdHash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - CustomDebugStringConvertible
|
// MARK: - CustomDebugStringConvertible
|
||||||
extension Nexus: CustomDebugStringConvertible {
|
extension Nexus: CustomDebugStringConvertible {
|
||||||
public var debugDescription: String {
|
public var debugDescription: String {
|
||||||
return "<Nexus entities:\(numEntities) components:\(numComponents) families:\(numFamilies)>"
|
"<Nexus entities:\(numEntities) components:\(numComponents) families:\(numFamilies)>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,6 @@ public struct ComponentAdded: NexusEvent {
|
||||||
public let toEntity: EntityIdentifier
|
public let toEntity: EntityIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ComponentUpdated: NexusEvent {
|
|
||||||
public let atEnity: EntityIdentifier
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ComponentRemoved: NexusEvent {
|
public struct ComponentRemoved: NexusEvent {
|
||||||
public let component: ComponentIdentifier
|
public let component: ComponentIdentifier
|
||||||
public let from: EntityIdentifier
|
public let from: EntityIdentifier
|
||||||
|
|
|
||||||
|
|
@ -29,17 +29,23 @@ public struct Single<A> where A: SingleComponent {
|
||||||
public let entityId: EntityIdentifier
|
public let entityId: EntityIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Single: Equatable { }
|
extension Single: Equatable {
|
||||||
|
public static func == (lhs: Single<A>, rhs: Single<A>) -> Bool {
|
||||||
|
lhs.traits == rhs.traits &&
|
||||||
|
lhs.entityId == rhs.entityId &&
|
||||||
|
lhs.nexus === rhs.nexus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Single where A: SingleComponent {
|
extension Single where A: SingleComponent {
|
||||||
@inlinable public var component: A {
|
@inlinable public var component: A {
|
||||||
/// Since we guarantee that the component will always be present by managing the complete lifecycle of the entity
|
// Since we guarantee that the component will always be present by managing the complete lifecycle of the entity
|
||||||
/// and component assignment we may unsafelyUnwrap here.
|
// and component assignment we may unsafelyUnwrap here.
|
||||||
/// Since components will allways be of reference type (class) we may use unsafeDowncast here for performance reasons.
|
// Since components will allways be of reference type (class) we may use unsafeDowncast here for performance reasons.
|
||||||
return nexus.get(unsafeComponentFor: entityId)
|
return nexus.get(unsafeComponentFor: entityId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var entity: Entity {
|
public var entity: Entity {
|
||||||
return nexus.get(entity: entityId).unsafelyUnwrapped
|
nexus.get(entity: entityId).unsafelyUnwrapped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Christian Treffs on 30.10.17.
|
// Created by Christian Treffs on 30.10.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
open class UnorderedSparseSet<Element> {
|
public struct UnorderedSparseSet<Element> {
|
||||||
public typealias Index = Int
|
public typealias Index = Int
|
||||||
public typealias Key = Int
|
public typealias Key = Int
|
||||||
|
|
||||||
|
|
@ -18,20 +18,20 @@ open class UnorderedSparseSet<Element> {
|
||||||
@usableFromInline var sparse: [Index: Key]
|
@usableFromInline var sparse: [Index: Key]
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
sparse = [Index: Key]()
|
self.init(sparse: [:], dense: [])
|
||||||
dense = ContiguousArray<Entry>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
init(sparse: [Index: Key], dense: ContiguousArray<Entry>) {
|
||||||
removeAll()
|
self.sparse = sparse
|
||||||
|
self.dense = dense
|
||||||
}
|
}
|
||||||
|
|
||||||
public var count: Int { return dense.count }
|
public var count: Int { dense.count }
|
||||||
public var isEmpty: Bool { return dense.isEmpty }
|
public var isEmpty: Bool { dense.isEmpty }
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func contains(_ key: Key) -> Bool {
|
public func contains(_ key: Key) -> Bool {
|
||||||
return find(at: key) != nil
|
find(at: key) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inset an element for a given key into the set in O(1).
|
/// Inset an element for a given key into the set in O(1).
|
||||||
|
|
@ -42,7 +42,7 @@ open class UnorderedSparseSet<Element> {
|
||||||
/// - key: the key
|
/// - key: the key
|
||||||
/// - Returns: true if new, false if replaced.
|
/// - Returns: true if new, false if replaced.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func insert(_ element: Element, at key: Key) -> Bool {
|
public mutating func insert(_ element: Element, at key: Key) -> Bool {
|
||||||
if let (denseIndex, _) = find(at: key) {
|
if let (denseIndex, _) = find(at: key) {
|
||||||
dense[denseIndex] = Entry(key: key, element: element)
|
dense[denseIndex] = Entry(key: key, element: element)
|
||||||
return false
|
return false
|
||||||
|
|
@ -69,7 +69,7 @@ open class UnorderedSparseSet<Element> {
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func get(unsafeAt key: Key) -> Element {
|
public func get(unsafeAt key: Key) -> Element {
|
||||||
return find(at: key).unsafelyUnwrapped.1
|
find(at: key).unsafelyUnwrapped.1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the element entry for given key in O(1).
|
/// Removes the element entry for given key in O(1).
|
||||||
|
|
@ -77,7 +77,7 @@ open class UnorderedSparseSet<Element> {
|
||||||
/// - Parameter key: the key
|
/// - Parameter key: the key
|
||||||
/// - Returns: removed value or nil if key not found.
|
/// - Returns: removed value or nil if key not found.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func remove(at key: Key) -> Entry? {
|
public mutating func remove(at key: Key) -> Entry? {
|
||||||
guard let (denseIndex, _) = find(at: key) else {
|
guard let (denseIndex, _) = find(at: key) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -92,22 +92,17 @@ open class UnorderedSparseSet<Element> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func removeAll(keepingCapacity: Bool = false) {
|
public mutating func removeAll(keepingCapacity: Bool = false) {
|
||||||
sparse.removeAll(keepingCapacity: keepingCapacity)
|
sparse.removeAll(keepingCapacity: keepingCapacity)
|
||||||
dense.removeAll(keepingCapacity: keepingCapacity)
|
dense.removeAll(keepingCapacity: keepingCapacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
|
||||||
public func makeIterator() -> UnorderedSparseSetIterator<Element> {
|
|
||||||
return UnorderedSparseSetIterator<Element>(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes an element from the set and retuns it in O(1).
|
/// Removes an element from the set and retuns it in O(1).
|
||||||
/// The removed element is replaced with the last element of the set.
|
/// The removed element is replaced with the last element of the set.
|
||||||
///
|
///
|
||||||
/// - Parameter denseIndex: the dense index
|
/// - Parameter denseIndex: the dense index
|
||||||
/// - Returns: the element entry
|
/// - Returns: the element entry
|
||||||
private func swapRemove(at denseIndex: Int) -> Entry {
|
private mutating func swapRemove(at denseIndex: Int) -> Entry {
|
||||||
dense.swapAt(denseIndex, dense.count - 1)
|
dense.swapAt(denseIndex, dense.count - 1)
|
||||||
return dense.removeLast()
|
return dense.removeLast()
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +123,7 @@ open class UnorderedSparseSet<Element> {
|
||||||
@inlinable
|
@inlinable
|
||||||
public subscript(position: Index) -> Element {
|
public subscript(position: Index) -> Element {
|
||||||
get {
|
get {
|
||||||
return get(unsafeAt: position)
|
get(unsafeAt: position)
|
||||||
}
|
}
|
||||||
|
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
|
|
@ -137,30 +132,42 @@ open class UnorderedSparseSet<Element> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable public var first: Element? {
|
@inlinable public var first: Element? {
|
||||||
return dense.first?.element
|
dense.first?.element
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable public var last: Element? {
|
@inlinable public var last: Element? {
|
||||||
return dense.last?.element
|
dense.last?.element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Sequence
|
||||||
|
extension UnorderedSparseSet: Sequence {
|
||||||
|
public __consuming func makeIterator() -> ElementIterator {
|
||||||
|
ElementIterator(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UnorderedSparseSetIterator
|
||||||
|
public struct ElementIterator: IteratorProtocol {
|
||||||
|
public private(set) var iterator: IndexingIterator<ContiguousArray<UnorderedSparseSet<Element>.Entry>>
|
||||||
|
|
||||||
|
public init(_ sparseSet: UnorderedSparseSet<Element>) {
|
||||||
|
iterator = sparseSet.dense.makeIterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func next() -> Element? {
|
||||||
|
iterator.next()?.element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Equatable
|
||||||
extension UnorderedSparseSet.Entry: Equatable where Element: Equatable { }
|
extension UnorderedSparseSet.Entry: Equatable where Element: Equatable { }
|
||||||
extension UnorderedSparseSet: Equatable where Element: Equatable {
|
extension UnorderedSparseSet: Equatable where Element: Equatable {
|
||||||
public static func == (lhs: UnorderedSparseSet<Element>, rhs: UnorderedSparseSet<Element>) -> Bool {
|
public static func == (lhs: UnorderedSparseSet<Element>, rhs: UnorderedSparseSet<Element>) -> Bool {
|
||||||
return lhs.dense == rhs.dense && lhs.sparse == rhs.sparse
|
lhs.dense == rhs.dense && lhs.sparse == rhs.sparse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UnorderedSparseSetIterator
|
// MARK: - Codable
|
||||||
public struct UnorderedSparseSetIterator<Element>: IteratorProtocol {
|
extension UnorderedSparseSet.Entry: Codable where Element: Codable { }
|
||||||
public private(set) var iterator: IndexingIterator<ContiguousArray<UnorderedSparseSet<Element>.Entry>>
|
extension UnorderedSparseSet: Codable where Element: Codable { }
|
||||||
|
|
||||||
public init(_ sparseSet: UnorderedSparseSet<Element>) {
|
|
||||||
iterator = sparseSet.dense.makeIterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
public mutating func next() -> Element? {
|
|
||||||
return iterator.next()?.element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,12 @@
|
||||||
|
|
||||||
import FirebladeECS
|
import FirebladeECS
|
||||||
|
|
||||||
class EmptyComponent: Component { }
|
class EmptyComponent: Component {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class Name: Component {
|
class Name: Component {
|
||||||
|
|
||||||
var name: String
|
var name: String
|
||||||
init(name: String) {
|
init(name: String) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
@ -17,6 +20,7 @@ class Name: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Position: Component {
|
class Position: Component {
|
||||||
|
|
||||||
var x: Int
|
var x: Int
|
||||||
var y: Int
|
var y: Int
|
||||||
init(x: Int, y: Int) {
|
init(x: Int, y: Int) {
|
||||||
|
|
@ -26,6 +30,7 @@ class Position: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Velocity: Component {
|
class Velocity: Component {
|
||||||
|
|
||||||
var a: Float
|
var a: Float
|
||||||
init(a: Float) {
|
init(a: Float) {
|
||||||
self.a = a
|
self.a = a
|
||||||
|
|
@ -33,6 +38,7 @@ class Velocity: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Party: Component {
|
class Party: Component {
|
||||||
|
|
||||||
var partying: Bool
|
var partying: Bool
|
||||||
init(partying: Bool) {
|
init(partying: Bool) {
|
||||||
self.partying = partying
|
self.partying = partying
|
||||||
|
|
@ -40,6 +46,7 @@ class Party: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Color: Component {
|
class Color: Component {
|
||||||
|
|
||||||
var r: UInt8 = 0
|
var r: UInt8 = 0
|
||||||
var g: UInt8 = 0
|
var g: UInt8 = 0
|
||||||
var b: UInt8 = 0
|
var b: UInt8 = 0
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// ComponentPerformanceTests.swift
|
// ComponentIdentifierTests.swift
|
||||||
// FirebladeECSPerformanceTests
|
// FirebladeECSPerformanceTests
|
||||||
//
|
//
|
||||||
// Created by Christian Treffs on 14.02.19.
|
// Created by Christian Treffs on 14.02.19.
|
||||||
|
|
@ -8,9 +8,11 @@
|
||||||
import FirebladeECS
|
import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class ComponentTests: XCTestCase {
|
class ComponentIdentifierTests: XCTestCase {
|
||||||
|
|
||||||
|
/// debug: 0.456 sec
|
||||||
func testMeasureStaticComponentIdentifier() {
|
func testMeasureStaticComponentIdentifier() {
|
||||||
let number: Int = 10_000
|
let number: Int = 1_000_000
|
||||||
measure {
|
measure {
|
||||||
for _ in 0..<number {
|
for _ in 0..<number {
|
||||||
let id = Position.identifier
|
let id = Position.identifier
|
||||||
|
|
@ -19,8 +21,9 @@ class ComponentTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// debug: 0.413 sec
|
||||||
func testMeasureComponentIdentifier() {
|
func testMeasureComponentIdentifier() {
|
||||||
let number: Int = 10_000
|
let number: Int = 1_000_000
|
||||||
let pos = Position(x: 1, y: 2)
|
let pos = Position(x: 1, y: 2)
|
||||||
measure {
|
measure {
|
||||||
for _ in 0..<number {
|
for _ in 0..<number {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,16 @@
|
||||||
import FirebladeECS
|
import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
let isDebug: Bool = true
|
||||||
|
#else
|
||||||
|
let isDebug: Bool = false
|
||||||
|
#endif
|
||||||
|
|
||||||
class HashingPerformanceTests: XCTestCase {
|
class HashingPerformanceTests: XCTestCase {
|
||||||
|
|
||||||
|
/// release: 0.726 sec
|
||||||
|
/// debug: 3.179 sec
|
||||||
func testMeasureCombineHash() {
|
func testMeasureCombineHash() {
|
||||||
let a: Set<Int> = Set<Int>([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812])
|
let a: Set<Int> = Set<Int>([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812])
|
||||||
let b: Set<Int> = Set<Int>([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234])
|
let b: Set<Int> = Set<Int>([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234])
|
||||||
|
|
@ -23,6 +32,8 @@ class HashingPerformanceTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.494 sec
|
||||||
|
/// debug: 1.026 sec
|
||||||
func testMeasureSetOfSetHash() {
|
func testMeasureSetOfSetHash() {
|
||||||
let a: Set<Int> = Set<Int>([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812])
|
let a: Set<Int> = Set<Int>([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812])
|
||||||
let b: Set<Int> = Set<Int>([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234])
|
let b: Set<Int> = Set<Int>([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234])
|
||||||
|
|
@ -36,4 +47,58 @@ class HashingPerformanceTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.098 sec
|
||||||
|
/// debug: 16.702 sec
|
||||||
|
func testMeasureBernsteinDjb2() throws {
|
||||||
|
try XCTSkipIf(isDebug)
|
||||||
|
let string = "The quick brown fox jumps over the lazy dog"
|
||||||
|
measure {
|
||||||
|
for _ in 0..<1_000_000 {
|
||||||
|
let hash = StringHashing.bernstein_djb2(string)
|
||||||
|
_ = hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release: 0.087 sec
|
||||||
|
/// debug: 2.613 sec
|
||||||
|
func testMeasureSingerDjb2() throws {
|
||||||
|
let string = "The quick brown fox jumps over the lazy dog"
|
||||||
|
measure {
|
||||||
|
for _ in 0..<1_000_000 {
|
||||||
|
let hash = StringHashing.singer_djb2(string)
|
||||||
|
_ = hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release: 0.088 sec
|
||||||
|
/// debug: 30.766 sec
|
||||||
|
func testMeasureSDBM() throws {
|
||||||
|
try XCTSkipIf(isDebug)
|
||||||
|
let string = "The quick brown fox jumps over the lazy dog"
|
||||||
|
measure {
|
||||||
|
for _ in 0..<1_000_000 {
|
||||||
|
let hash = StringHashing.sdbm(string)
|
||||||
|
_ = hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release: 0.036 sec
|
||||||
|
/// debug: 0.546 sec
|
||||||
|
func testMeasureSwiftHasher() throws {
|
||||||
|
try XCTSkipIf(isDebug)
|
||||||
|
let string = "The quick brown fox jumps over the lazy dog"
|
||||||
|
measure {
|
||||||
|
for _ in 0..<1_000_000 {
|
||||||
|
var hasher = Hasher()
|
||||||
|
hasher.combine(string)
|
||||||
|
let hash = hasher.finalize()
|
||||||
|
_ = hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
//
|
||||||
|
// TypeIdentifierPerformanceTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Christian Treffs on 05.10.19.
|
||||||
|
//
|
||||||
|
|
||||||
|
import FirebladeECS
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class TypeIdentifierPerformanceTests: XCTestCase {
|
||||||
|
let maxIterations: Int = 100_000
|
||||||
|
|
||||||
|
// release: 0.000 sec
|
||||||
|
// debug: 0.051 sec
|
||||||
|
func testPerformanceObjectIdentifier() {
|
||||||
|
measure {
|
||||||
|
for _ in 0..<maxIterations {
|
||||||
|
_ = ObjectIdentifier(Color.self)
|
||||||
|
_ = ObjectIdentifier(EmptyComponent.self)
|
||||||
|
_ = ObjectIdentifier(Name.self)
|
||||||
|
_ = ObjectIdentifier(Party.self)
|
||||||
|
_ = ObjectIdentifier(Position.self)
|
||||||
|
_ = ObjectIdentifier(SingleGameState.self)
|
||||||
|
_ = ObjectIdentifier(Velocity.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release: 1.034 sec
|
||||||
|
/// debug:
|
||||||
|
func testPerformanceHash() {
|
||||||
|
measure {
|
||||||
|
for _ in 0..<maxIterations {
|
||||||
|
_ = StringHashing.singer_djb2(String(describing: Color.self))
|
||||||
|
_ = StringHashing.singer_djb2(String(describing: EmptyComponent.self))
|
||||||
|
_ = StringHashing.singer_djb2(String(describing: Name.self))
|
||||||
|
_ = StringHashing.singer_djb2(String(describing: Party.self))
|
||||||
|
_ = StringHashing.singer_djb2(String(describing: Position.self))
|
||||||
|
_ = StringHashing.singer_djb2(String(describing: SingleGameState.self))
|
||||||
|
_ = StringHashing.singer_djb2(String(describing: Velocity.self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release: 1.034 sec
|
||||||
|
/// debug: 1.287 sec
|
||||||
|
func testPerformanceStringDescribing() {
|
||||||
|
measure {
|
||||||
|
for _ in 0..<maxIterations {
|
||||||
|
_ = String(describing: Color.self)
|
||||||
|
_ = String(describing: EmptyComponent.self)
|
||||||
|
_ = String(describing: Name.self)
|
||||||
|
_ = String(describing: Party.self)
|
||||||
|
_ = String(describing: Position.self)
|
||||||
|
_ = String(describing: SingleGameState.self)
|
||||||
|
_ = String(describing: Velocity.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release: 1.187 sec
|
||||||
|
/// debug: 1.498 sec
|
||||||
|
func testPerformanceStringReflecting() {
|
||||||
|
measure {
|
||||||
|
for _ in 0..<maxIterations {
|
||||||
|
_ = String(reflecting: Color.self)
|
||||||
|
_ = String(reflecting: EmptyComponent.self)
|
||||||
|
_ = String(reflecting: Name.self)
|
||||||
|
_ = String(reflecting: Party.self)
|
||||||
|
_ = String(reflecting: Position.self)
|
||||||
|
_ = String(reflecting: SingleGameState.self)
|
||||||
|
_ = String(reflecting: Velocity.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release: 2.102 sec
|
||||||
|
/// debug: 2.647 sec
|
||||||
|
func testPerformanceMirrorReflectingDescription() {
|
||||||
|
measure {
|
||||||
|
for _ in 0..<maxIterations {
|
||||||
|
_ = Mirror(reflecting: Color.self).description
|
||||||
|
_ = Mirror(reflecting: EmptyComponent.self).description
|
||||||
|
_ = Mirror(reflecting: Name.self).description
|
||||||
|
_ = Mirror(reflecting: Party.self).description
|
||||||
|
_ = Mirror(reflecting: Position.self).description
|
||||||
|
_ = Mirror(reflecting: SingleGameState.self).description
|
||||||
|
_ = Mirror(reflecting: Velocity.self).description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,8 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.011 sec
|
||||||
|
/// debug: 0.017 sec
|
||||||
func testMeasureTraitMatching() {
|
func testMeasureTraitMatching() {
|
||||||
let a = nexus.createEntity()
|
let a = nexus.createEntity()
|
||||||
a.assign(Position(x: 1, y: 2))
|
a.assign(Position(x: 1, y: 2))
|
||||||
|
|
@ -48,6 +50,8 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.001 sec
|
||||||
|
/// debug: 0.008 sec
|
||||||
func testPerformanceTypedFamilyEntities() {
|
func testPerformanceTypedFamilyEntities() {
|
||||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -71,6 +75,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// debug: 0.004 sec
|
||||||
func testPerformanceArray() {
|
func testPerformanceArray() {
|
||||||
let positions = [Position](repeating: Position(x: Int.random(in: 0...10), y: Int.random(in: 0...10)), count: numEntities)
|
let positions = [Position](repeating: Position(x: Int.random(in: 0...10), y: Int.random(in: 0...10)), count: numEntities)
|
||||||
|
|
||||||
|
|
@ -87,6 +92,8 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, numEntities * 10)
|
XCTAssertEqual(loopCount, numEntities * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.003 sec
|
||||||
|
/// debug: 0.010 sec
|
||||||
func testPerformanceTypedFamilyOneComponent() {
|
func testPerformanceTypedFamilyOneComponent() {
|
||||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -109,6 +116,8 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.004 sec
|
||||||
|
/// debug: 0.016 sec
|
||||||
func testPerformanceTypedFamilyEntityOneComponent() {
|
func testPerformanceTypedFamilyEntityOneComponent() {
|
||||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -133,6 +142,8 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.005 sec
|
||||||
|
/// debug: 0.016 sec
|
||||||
func testPerformanceTypedFamilyTwoComponents() {
|
func testPerformanceTypedFamilyTwoComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -156,6 +167,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.006 sec
|
||||||
func testPerformanceTypedFamilyEntityTwoComponents() {
|
func testPerformanceTypedFamilyEntityTwoComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -181,6 +193,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.007 sec
|
||||||
func testPerformanceTypedFamilyThreeComponents() {
|
func testPerformanceTypedFamilyThreeComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -205,6 +218,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.008 sec
|
||||||
func testPerformanceTypedFamilyEntityThreeComponents() {
|
func testPerformanceTypedFamilyEntityThreeComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -231,6 +245,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.009 sec
|
||||||
func testPerformanceTypedFamilyFourComponents() {
|
func testPerformanceTypedFamilyFourComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -256,6 +271,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.010 sec
|
||||||
func testPerformanceTypedFamilyEntityFourComponents() {
|
func testPerformanceTypedFamilyEntityFourComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -283,6 +299,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.012 sec
|
||||||
func testPerformanceTypedFamilyFiveComponents() {
|
func testPerformanceTypedFamilyFiveComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
@ -308,6 +325,7 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
||||||
XCTAssertEqual(loopCount, family.count * 10)
|
XCTAssertEqual(loopCount, family.count * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// release: 0.012 sec
|
||||||
func testPerformanceTypedFamilyEntityFiveComponents() {
|
func testPerformanceTypedFamilyEntityFiveComponents() {
|
||||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#if !canImport(ObjectiveC)
|
#if !canImport(ObjectiveC)
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
extension ComponentTests {
|
extension ComponentIdentifierTests {
|
||||||
// DO NOT MODIFY: This is autogenerated, use:
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
// `swift test --generate-linuxmain`
|
// `swift test --generate-linuxmain`
|
||||||
// to regenerate.
|
// to regenerate.
|
||||||
static let __allTests__ComponentTests = [
|
static let __allTests__ComponentIdentifierTests = [
|
||||||
("testMeasureComponentIdentifier", testMeasureComponentIdentifier),
|
("testMeasureComponentIdentifier", testMeasureComponentIdentifier),
|
||||||
("testMeasureStaticComponentIdentifier", testMeasureStaticComponentIdentifier)
|
("testMeasureStaticComponentIdentifier", testMeasureStaticComponentIdentifier)
|
||||||
]
|
]
|
||||||
|
|
@ -16,8 +16,25 @@ extension HashingPerformanceTests {
|
||||||
// `swift test --generate-linuxmain`
|
// `swift test --generate-linuxmain`
|
||||||
// to regenerate.
|
// to regenerate.
|
||||||
static let __allTests__HashingPerformanceTests = [
|
static let __allTests__HashingPerformanceTests = [
|
||||||
|
("testMeasureBernsteinDjb2", testMeasureBernsteinDjb2),
|
||||||
("testMeasureCombineHash", testMeasureCombineHash),
|
("testMeasureCombineHash", testMeasureCombineHash),
|
||||||
("testMeasureSetOfSetHash", testMeasureSetOfSetHash)
|
("testMeasureSDBM", testMeasureSDBM),
|
||||||
|
("testMeasureSetOfSetHash", testMeasureSetOfSetHash),
|
||||||
|
("testMeasureSingerDjb2", testMeasureSingerDjb2),
|
||||||
|
("testMeasureSwiftHasher", testMeasureSwiftHasher)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TypeIdentifierPerformanceTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__TypeIdentifierPerformanceTests = [
|
||||||
|
("testPerformanceHash", testPerformanceHash),
|
||||||
|
("testPerformanceMirrorReflectingDescription", testPerformanceMirrorReflectingDescription),
|
||||||
|
("testPerformanceObjectIdentifier", testPerformanceObjectIdentifier),
|
||||||
|
("testPerformanceStringDescribing", testPerformanceStringDescribing),
|
||||||
|
("testPerformanceStringReflecting", testPerformanceStringReflecting)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,8 +61,9 @@ extension TypedFamilyPerformanceTests {
|
||||||
|
|
||||||
public func __allTests() -> [XCTestCaseEntry] {
|
public func __allTests() -> [XCTestCaseEntry] {
|
||||||
return [
|
return [
|
||||||
testCase(ComponentTests.__allTests__ComponentTests),
|
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
||||||
testCase(HashingPerformanceTests.__allTests__HashingPerformanceTests),
|
testCase(HashingPerformanceTests.__allTests__HashingPerformanceTests),
|
||||||
|
testCase(TypeIdentifierPerformanceTests.__allTests__TypeIdentifierPerformanceTests),
|
||||||
testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests)
|
testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
//
|
|
||||||
// AccessTests.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Christian Treffs on 25.06.19.
|
|
||||||
//
|
|
||||||
|
|
||||||
import FirebladeECS
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
#if swift(>=5.1)
|
|
||||||
class AccessTests: XCTestCase {
|
|
||||||
func testReadOnly() {
|
|
||||||
let pos = Position(x: 1, y: 2)
|
|
||||||
|
|
||||||
let readable = ReadableOnly<Position>(pos)
|
|
||||||
|
|
||||||
XCTAssertEqual(readable.x, 1)
|
|
||||||
XCTAssertEqual(readable.y, 2)
|
|
||||||
|
|
||||||
// readable.x = 3 // does not work and that's correct!
|
|
||||||
}
|
|
||||||
|
|
||||||
func testWrite() {
|
|
||||||
let pos = Position(x: 1, y: 2)
|
|
||||||
|
|
||||||
let writable = Writable<Position>(pos)
|
|
||||||
|
|
||||||
XCTAssertEqual(writable.x, 1)
|
|
||||||
XCTAssertEqual(writable.y, 2)
|
|
||||||
|
|
||||||
writable.x = 3
|
|
||||||
|
|
||||||
XCTAssertEqual(writable.x, 3)
|
|
||||||
XCTAssertEqual(pos.x, 3)
|
|
||||||
|
|
||||||
XCTAssertEqual(writable.y, 2)
|
|
||||||
XCTAssertEqual(pos.y, 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
import FirebladeECS
|
import FirebladeECS
|
||||||
|
|
||||||
class EmptyComponent: Component { }
|
class EmptyComponent: Component {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class Name: Component {
|
class Name: Component {
|
||||||
var name: String
|
var name: String
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// ComponentIdentifierTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Christian Treffs on 05.10.19.
|
||||||
|
//
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class ComponentIdentifierTests: XCTestCase {
|
||||||
|
|
||||||
|
func testMirrorAsStableIdentifier() {
|
||||||
|
let m = String(reflecting: Position.self)
|
||||||
|
let identifier: String = m
|
||||||
|
XCTAssertEqual(identifier, "FirebladeECSTests.Position")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testStringDescribingAsStableIdentifier() {
|
||||||
|
let s = String(describing: Position.self)
|
||||||
|
let identifier: String = s
|
||||||
|
XCTAssertEqual(identifier, "Position")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
// Created by Christian Treffs on 22.10.17.
|
// Created by Christian Treffs on 22.10.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
import FirebladeECS
|
#if DEBUG
|
||||||
|
@testable import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class EntityTests: XCTestCase {
|
class EntityTests: XCTestCase {
|
||||||
|
|
@ -21,9 +22,5 @@ class EntityTests: XCTestCase {
|
||||||
XCTAssertEqual(max, EntityIdentifier.invalid)
|
XCTAssertEqual(max, EntityIdentifier.invalid)
|
||||||
XCTAssertEqual(max.id, Int(UInt32.max))
|
XCTAssertEqual(max.id, Int(UInt32.max))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEntityIdentifierComparison() {
|
|
||||||
XCTAssertTrue(EntityIdentifier(1) < EntityIdentifier(2))
|
|
||||||
XCTAssertTrue(EntityIdentifier(23) > EntityIdentifier(4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// Created by Christian Treffs on 09.10.17.
|
// Created by Christian Treffs on 09.10.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
@testable import FirebladeECS
|
@testable import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
|
@ -31,7 +32,6 @@ class FamilyTests: XCTestCase {
|
||||||
let family = nexus.family(requires: Position.self,
|
let family = nexus.family(requires: Position.self,
|
||||||
excludesAll: Name.self)
|
excludesAll: Name.self)
|
||||||
|
|
||||||
XCTAssertEqual(family.nexus, self.nexus)
|
|
||||||
XCTAssertTrue(family.nexus === self.nexus)
|
XCTAssertTrue(family.nexus === self.nexus)
|
||||||
XCTAssertEqual(nexus.numFamilies, 1)
|
XCTAssertEqual(nexus.numFamilies, 1)
|
||||||
XCTAssertEqual(nexus.numComponents, 0)
|
XCTAssertEqual(nexus.numComponents, 0)
|
||||||
|
|
@ -73,11 +73,11 @@ class FamilyTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.numComponents, 1)
|
XCTAssertEqual(nexus.numComponents, 1)
|
||||||
XCTAssertEqual(nexus.numEntities, 1)
|
XCTAssertEqual(nexus.numEntities, 1)
|
||||||
entity.remove(Position.self)
|
entity.remove(Position.self)
|
||||||
XCTAssertEqual(nexus.numFamilies, 1)
|
XCTAssertEqual(nexus.numFamilies, 0)
|
||||||
XCTAssertEqual(nexus.numComponents, 0)
|
XCTAssertEqual(nexus.numComponents, 0)
|
||||||
XCTAssertEqual(nexus.numEntities, 1)
|
XCTAssertEqual(nexus.numEntities, 1)
|
||||||
nexus.destroy(entity: entity)
|
nexus.destroy(entity: entity)
|
||||||
XCTAssertEqual(nexus.numFamilies, 1)
|
XCTAssertEqual(nexus.numFamilies, 0)
|
||||||
XCTAssertEqual(nexus.numComponents, 0)
|
XCTAssertEqual(nexus.numComponents, 0)
|
||||||
XCTAssertEqual(nexus.numEntities, 0)
|
XCTAssertEqual(nexus.numEntities, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +132,7 @@ class FamilyTests: XCTestCase {
|
||||||
entity.remove(velocity)
|
entity.remove(velocity)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(familyA.count, 10)
|
XCTAssertEqual(familyA.count, 0)
|
||||||
XCTAssertEqual(familyB.count, 0)
|
XCTAssertEqual(familyB.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,3 +186,4 @@ class FamilyTests: XCTestCase {
|
||||||
XCTAssertEqual(family.memberIds.count, count + (count / 2))
|
XCTAssertEqual(family.memberIds.count, count + (count / 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// Created by Christian Treffs on 16.10.17.
|
// Created by Christian Treffs on 16.10.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
@testable import FirebladeECS
|
@testable import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
|
@ -50,4 +51,18 @@ class HashingTests: XCTestCase {
|
||||||
XCTAssert(EntityComponentHash.decompose(h, with: entityId) == cH)
|
XCTAssert(EntityComponentHash.decompose(h, with: entityId) == cH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testStringHashes() throws {
|
||||||
|
let string = "EiMersaufEn1"
|
||||||
|
|
||||||
|
XCTAssertEqual(StringHashing.bernstein_djb2(string), 13447802024599246090)
|
||||||
|
XCTAssertEqual(StringHashing.singer_djb2(string), 5428736256651916664)
|
||||||
|
XCTAssertEqual(StringHashing.sdbm(string), 15559770072020577201)
|
||||||
|
|
||||||
|
XCTAssertEqual(StringHashing.bernstein_djb2("gamedev"), 229466792000542)
|
||||||
|
XCTAssertEqual(StringHashing.singer_djb2("gamedev"), 2867840411746895486)
|
||||||
|
XCTAssertEqual(StringHashing.sdbm("gamedev"), 2761443862055442870)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
// Created by Christian Treffs on 09.10.17.
|
// Created by Christian Treffs on 09.10.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
import FirebladeECS
|
#if DEBUG
|
||||||
|
@testable import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class NexusTests: XCTestCase {
|
class NexusTests: XCTestCase {
|
||||||
|
|
@ -33,9 +34,6 @@ class NexusTests: XCTestCase {
|
||||||
|
|
||||||
XCTAssert(e1.identifier.id == 1)
|
XCTAssert(e1.identifier.id == 1)
|
||||||
XCTAssert(nexus.numEntities == 2)
|
XCTAssert(nexus.numEntities == 2)
|
||||||
|
|
||||||
//FIXME: XCTAssertNil(e0.name)
|
|
||||||
//FIXME: XCTAssertEqual(e1.name, "Entity 1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEntityDestroy() {
|
func testEntityDestroy() {
|
||||||
|
|
@ -158,3 +156,4 @@ class NexusTests: XCTestCase {
|
||||||
XCTAssert(pB.y != pA.y)
|
XCTAssert(pB.y != pA.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// Created by Christian Treffs on 13.02.19.
|
// Created by Christian Treffs on 13.02.19.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
@testable import FirebladeECS
|
@testable import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
|
@ -23,7 +24,6 @@ class SingleTests: XCTestCase {
|
||||||
|
|
||||||
func testSingleCreation() {
|
func testSingleCreation() {
|
||||||
let single = nexus.single(SingleGameState.self)
|
let single = nexus.single(SingleGameState.self)
|
||||||
XCTAssertEqual(single.nexus, self.nexus)
|
|
||||||
XCTAssertTrue(single.nexus === self.nexus)
|
XCTAssertTrue(single.nexus === self.nexus)
|
||||||
XCTAssertEqual(single.traits.requiresAll.count, 1)
|
XCTAssertEqual(single.traits.requiresAll.count, 1)
|
||||||
XCTAssertEqual(single.traits.excludesAll.count, 0)
|
XCTAssertEqual(single.traits.excludesAll.count, 0)
|
||||||
|
|
@ -63,3 +63,4 @@ class SingleTests: XCTestCase {
|
||||||
XCTAssertTrue(singleGame === single.component)
|
XCTAssertTrue(singleGame === single.component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// Created by Christian Treffs on 31.10.17.
|
// Created by Christian Treffs on 31.10.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
@testable import FirebladeECS
|
@testable import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
|
@ -387,7 +388,7 @@ class SparseSetTests: XCTestCase {
|
||||||
|
|
||||||
func testSparseSetDoubleRemove() {
|
func testSparseSetDoubleRemove() {
|
||||||
class AClass { }
|
class AClass { }
|
||||||
let set = UnorderedSparseSet<AClass>()
|
var set = UnorderedSparseSet<AClass>()
|
||||||
let a = AClass()
|
let a = AClass()
|
||||||
let b = AClass()
|
let b = AClass()
|
||||||
set.insert(a, at: 0)
|
set.insert(a, at: 0)
|
||||||
|
|
@ -471,7 +472,7 @@ class SparseSetTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSparseSetReduce() {
|
func testSparseSetReduce() {
|
||||||
let characters = UnorderedSparseSet<Character>()
|
var characters = UnorderedSparseSet<Character>()
|
||||||
|
|
||||||
characters.insert("H", at: 4)
|
characters.insert("H", at: 4)
|
||||||
characters.insert("e", at: 13)
|
characters.insert("e", at: 13)
|
||||||
|
|
@ -497,7 +498,7 @@ class SparseSetTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSubscript() {
|
func testSubscript() {
|
||||||
let characters = UnorderedSparseSet<Character>()
|
var characters = UnorderedSparseSet<Character>()
|
||||||
|
|
||||||
characters[4] = "H"
|
characters[4] = "H"
|
||||||
characters[13] = "e"
|
characters[13] = "e"
|
||||||
|
|
@ -528,7 +529,7 @@ class SparseSetTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testStartEndIndex() {
|
func testStartEndIndex() {
|
||||||
let set = UnorderedSparseSet<Character>()
|
var set = UnorderedSparseSet<Character>()
|
||||||
|
|
||||||
set.insert("C", at: 33)
|
set.insert("C", at: 33)
|
||||||
set.insert("A", at: 11)
|
set.insert("A", at: 11)
|
||||||
|
|
@ -539,3 +540,4 @@ class SparseSetTests: XCTestCase {
|
||||||
XCTAssertEqual(mapped, ["C", "A", "B"])
|
XCTAssertEqual(mapped, ["C", "A", "B"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// Created by Christian Treffs on 10.05.18.
|
// Created by Christian Treffs on 10.05.18.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
@testable import FirebladeECS
|
@testable import FirebladeECS
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
|
@ -124,3 +125,4 @@ class SystemsTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
#if !canImport(ObjectiveC)
|
#if !canImport(ObjectiveC)
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
extension ComponentIdentifierTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__ComponentIdentifierTests = [
|
||||||
|
("testMirrorAsStableIdentifier", testMirrorAsStableIdentifier),
|
||||||
|
("testStringDescribingAsStableIdentifier", testStringDescribingAsStableIdentifier)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
extension ComponentTests {
|
extension ComponentTests {
|
||||||
// DO NOT MODIFY: This is autogenerated, use:
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
// `swift test --generate-linuxmain`
|
// `swift test --generate-linuxmain`
|
||||||
|
|
@ -15,8 +25,7 @@ extension EntityTests {
|
||||||
// `swift test --generate-linuxmain`
|
// `swift test --generate-linuxmain`
|
||||||
// to regenerate.
|
// to regenerate.
|
||||||
static let __allTests__EntityTests = [
|
static let __allTests__EntityTests = [
|
||||||
("testEntityIdentifierAndIndex", testEntityIdentifierAndIndex),
|
("testEntityIdentifierAndIndex", testEntityIdentifierAndIndex)
|
||||||
("testEntityIdentifierComparison", testEntityIdentifierComparison)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +59,8 @@ extension HashingTests {
|
||||||
// `swift test --generate-linuxmain`
|
// `swift test --generate-linuxmain`
|
||||||
// to regenerate.
|
// to regenerate.
|
||||||
static let __allTests__HashingTests = [
|
static let __allTests__HashingTests = [
|
||||||
("testCollisionsInCritialRange", testCollisionsInCritialRange)
|
("testCollisionsInCritialRange", testCollisionsInCritialRange),
|
||||||
|
("testStringHashes", testStringHashes)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,6 +134,7 @@ extension SystemsTests {
|
||||||
|
|
||||||
public func __allTests() -> [XCTestCaseEntry] {
|
public func __allTests() -> [XCTestCaseEntry] {
|
||||||
return [
|
return [
|
||||||
|
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
||||||
testCase(ComponentTests.__allTests__ComponentTests),
|
testCase(ComponentTests.__allTests__ComponentTests),
|
||||||
testCase(EntityTests.__allTests__EntityTests),
|
testCase(EntityTests.__allTests__EntityTests),
|
||||||
testCase(FamilyTests.__allTests__FamilyTests),
|
testCase(FamilyTests.__allTests__FamilyTests),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue