diff --git a/.sourcery.yml b/.sourcery.yml deleted file mode 100644 index 8daf1cb0..00000000 --- a/.sourcery.yml +++ /dev/null @@ -1,6 +0,0 @@ -sources: # you can provide either single path or several paths using `-` - - Sources -templates: # as well as for templates - - Sources/FirebladeECS/Stencils -output: # note that there is no `-` here as only single output path is supported - Sources/FirebladeECS/Generated diff --git a/.sourceryTests.yml b/.sourceryTests.yml deleted file mode 100644 index 80db363f..00000000 --- a/.sourceryTests.yml +++ /dev/null @@ -1,6 +0,0 @@ -sources: # you can provide either single path or several paths using `-` - - Sources -templates: # as well as for templates - - Tests/FirebladeECSTests/Stencils -output: # note that there is no `-` here as only single output path is supported - Tests/FirebladeECSTests/Generated diff --git a/Benchmarks/Benchmarks/ECSBenchmark/Base.swift b/Benchmarks/Benchmarks/ECSBenchmark/Base.swift index fe0da9d2..f4663830 100644 --- a/Benchmarks/Benchmarks/ECSBenchmark/Base.swift +++ b/Benchmarks/Benchmarks/ECSBenchmark/Base.swift @@ -46,7 +46,7 @@ class Color: Component { } class ExampleSystem { - private let family: Family2 + private let family: Family init(nexus: Nexus) { family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self) diff --git a/Makefile b/Makefile index e7a2662c..5e7483db 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ DOCS_VERSION_PATH ?= main # The full base path for hosting HOSTING_BASE_PATH ?= $(REPO_NAME)/$(DOCS_VERSION_PATH) -.PHONY: setup generate-code lint lint-fix test test-coverage testReadme build-debug build-release docs docs-preview docs-generate docs-coverage docs-check-coverage docs-check-links preview-analysis-docs generate-docs-githubpages pre-commit clean clean-sourcery +.PHONY: setup lint lint-fix test test-coverage testReadme build-debug build-release docs docs-preview docs-generate docs-coverage docs-check-coverage docs-check-links preview-analysis-docs generate-docs-githubpages pre-commit clean # --- Setup --- @@ -26,12 +26,6 @@ setup: mint bootstrap swift package resolve $(SWIFT_FLAGS) -# --- Codegen --- - -generate-code: - mint run sourcery --quiet --config ./.sourcery.yml - mint run sourcery --quiet --config ./.sourceryTests.yml - # --- Quality Assurance --- lint: @@ -130,10 +124,7 @@ pre-commit: lint-fix test # --- Cleanup --- -clean: clean-sourcery +clean: swift package clean rm -rdf .build - rm -rdf .swiftpm - -clean-sourcery: - rm -rdf ${HOME}/Library/Caches/Sourcery \ No newline at end of file + rm -rdf .swiftpm \ No newline at end of file diff --git a/Mintfile b/Mintfile index b0a9d084..eddfea3a 100644 --- a/Mintfile +++ b/Mintfile @@ -1,4 +1,3 @@ realm/SwiftLint@0.63.2 nicklockwood/SwiftFormat@0.59.1 -krzysztofzablocki/Sourcery@2.3.0 ldomaradzki/xcsift@v1.1.3 diff --git a/Package.swift b/Package.swift index abc9dcf8..afd2f630 100644 --- a/Package.swift +++ b/Package.swift @@ -3,6 +3,12 @@ import PackageDescription let package = Package( name: "FirebladeECS", + platforms: [ + .macOS(.v14), + .iOS(.v17), + .tvOS(.v17), + .watchOS(.v10) + ], products: [ .library(name: "FirebladeECS", targets: ["FirebladeECS"]) @@ -12,11 +18,9 @@ let package = Package( ], targets: [ .target(name: "FirebladeECS", - exclude: ["Stencils/Family.stencil"], swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]), .testTarget(name: "FirebladeECSTests", dependencies: ["FirebladeECS"], - exclude: ["Stencils/FamilyTests.stencil"], swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]), .testTarget(name: "FirebladeECSPerformanceTests", dependencies: ["FirebladeECS"], diff --git a/Sources/FirebladeECS/Documentation.docc/Documentation.md b/Sources/FirebladeECS/Documentation.docc/Documentation.md index 2e369c2b..d5bc7328 100644 --- a/Sources/FirebladeECS/Documentation.docc/Documentation.md +++ b/Sources/FirebladeECS/Documentation.docc/Documentation.md @@ -52,45 +52,16 @@ For a more detailed example of FirebladeECS in action, see the [Fireblade ECS De - ``EntityComponentHash`` - ``StateComponentMapping`` - ``DynamicComponentProvider`` -- ``RequiringComponents1`` -- ``RequiringComponents2`` -- ``RequiringComponents3`` -- ``RequiringComponents4`` -- ``RequiringComponents5`` -- ``RequiringComponents6`` -- ``RequiringComponents7`` -- ``RequiringComponents8`` - ``DefaultInitializable`` - ``SingleComponent`` ### Systems - ``Family`` -- ``FamilyEncoding`` -- ``FamilyDecoding`` - ``FamilyMemberAdded`` - ``FamilyMemberRemoved`` -- ``FamilyMemberBuilder-3f2i6`` -- ``FamilyMemberBuilder`` - ``FamilyTraitSet`` -- ``Requires1`` -- ``Requires2`` -- ``Requires3`` -- ``Requires4`` -- ``Requires5`` -- ``Requires6`` -- ``Requires7`` -- ``Requires8`` - ``Single`` -- ``Family1`` -- ``Family2`` -- ``Family3`` -- ``Family4`` -- ``Family5`` -- ``Family6`` -- ``Family7`` -- ``Family8`` -- ``FamilyRequirementsManaging`` ### Coding Strategies diff --git a/Sources/FirebladeECS/Entity+Component.swift b/Sources/FirebladeECS/Entity+Component.swift index edfa2ad7..f1b78b10 100644 --- a/Sources/FirebladeECS/Entity+Component.swift +++ b/Sources/FirebladeECS/Entity+Component.swift @@ -23,33 +23,14 @@ extension Entity { nexus.get(safe: identifier) } - /// Retrieves two components of the specified types assigned to this entity. + /// Retrieves components of the specified types assigned to this entity. /// - Parameters: - /// - _: The first component type. - /// - _: The second component type. - /// - Returns: A tuple containing the component instances (or `nil` if not found). + /// - components: The component types to retrieve. + /// - Returns: A tuple containing the optional component instances. /// - Complexity: O(1) @inlinable - public func get(components _: A.Type, _: B.Type) -> (A?, B?) where A: Component, B: Component { - let compA: A? = get(component: A.self) - let compB: B? = get(component: B.self) - return (compA, compB) - } - - // swiftlint:disable large_tuple - /// Retrieves three components of the specified types assigned to this entity. - /// - Parameters: - /// - _: The first component type. - /// - _: The second component type. - /// - _: The third component type. - /// - Returns: A tuple containing the component instances (or `nil` if not found). - /// - Complexity: O(1) - @inlinable - public func get(components _: A.Type, _: B.Type, _: C.Type) -> (A?, B?, C?) where A: Component, B: Component, C: Component { - let compA: A? = get(component: A.self) - let compB: B? = get(component: B.self) - let compC: C? = get(component: C.self) - return (compA, compB, compC) + public func get(components: repeat (each C).Type) -> (repeat (each C)?) { + (repeat get(component: (each C).self)) } /// Get or set component instance by type via subscript. diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index f17ff932..b0c4dcfe 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -49,8 +49,8 @@ public struct Entity { /// - Returns: The created entity. /// - Complexity: O(C + M) where C is the number of components and M is the number of families. @discardableResult - public func createEntity(with components: Component...) -> Entity { - createEntity(with: components) + public func createEntity(with components: repeat each C) -> Entity { + nexus.createEntity(with: repeat each components) } /// Creates a new entity with the provided components. @@ -86,12 +86,12 @@ public struct Entity { /// - Parameter components: one or more components. /// - Complexity: O(M) where M is the number of families. @discardableResult - public func assign(_ components: Component...) -> Entity { - assign(components) + public func assign(_ components: repeat each C) -> Entity { + nexus.assign(components: repeat each components, to: self) return self } - /// Add a component to this entity. + /// Add a single component to this entity. /// - Parameter component: a component. /// - Complexity: O(M) where M is the number of families. @discardableResult diff --git a/Sources/FirebladeECS/Family+Coding+Foundation.swift b/Sources/FirebladeECS/Family+Coding+Foundation.swift new file mode 100644 index 00000000..1ccf2bc2 --- /dev/null +++ b/Sources/FirebladeECS/Family+Coding+Foundation.swift @@ -0,0 +1,45 @@ +// +// Family+Coding+Foundation.swift +// FirebladeECS +// +// Created by Christian Treffs on 13.02.26. +// + +#if canImport(Foundation) +import Foundation + +extension Family where repeat each C: Encodable { + /// Encode family members (entities) to data using a given encoder. + /// + /// The encoded members will *NOT* be removed from the nexus and will also stay present in this family. + /// - Parameter encoder: The data encoder. Data encoder respects the coding strategy set at `nexus.codingStrategy`. + /// - Returns: The encoded data. + /// - Complexity: O(N) where N is the number of family members. + public func encodeMembers(using encoder: inout JSONEncoder) throws -> Data { + encoder.userInfo[CodingUserInfoKey.nexusCodingStrategy] = nexus.codingStrategy + let container = FamilyMemberContainer(components: makeIterator()) + return try encoder.encode(container) + } +} + +extension Family where repeat each C: Decodable { + /// Decode family members (entities) from given data using a decoder. + /// + /// The decoded members will be added to the nexus and will be present in this family. + /// - Parameters: + /// - data: The data decoded by decoder. An unkeyed container of family members (keyed component containers) is expected. + /// - decoder: The decoder to use for decoding family member data. Decoder respects the coding strategy set at `nexus.codingStrategy`. + /// - Returns: returns the newly added entities. + /// - Complexity: O(N) where N is the number of family members in the data. + @discardableResult + public func decodeMembers(from data: Data, using decoder: inout JSONDecoder) throws -> [Entity] { + decoder.userInfo[CodingUserInfoKey.nexusCodingStrategy] = nexus.codingStrategy + let familyMembers = try decoder.decode(FamilyMemberContainer.self, from: data) + return familyMembers.components + .map { (memberComponents: (repeat each C)) in + createMember(with: repeat each memberComponents) + } + } +} + +#endif diff --git a/Sources/FirebladeECS/Family+Coding.swift b/Sources/FirebladeECS/Family+Coding.swift index 0b67f21c..64b83321 100644 --- a/Sources/FirebladeECS/Family+Coding.swift +++ b/Sources/FirebladeECS/Family+Coding.swift @@ -5,22 +5,28 @@ // Created by Christian Treffs on 22.07.20. // -#if canImport(Darwin) || swift(>=6.2) -public typealias UserInfoValue = any Sendable -#else -public typealias UserInfoValue = Any -#endif - /// A container for family members (components) used for encoding and decoding. -public struct FamilyMemberContainer { +public struct FamilyMemberContainer { /// The components of the family members. - public let components: [R.Components] + public let components: [(repeat each C)] /// Creates a new family member container. /// - Parameter components: The components to contain. - public init(components: [R.Components]) { + public init(components: (repeat each C)...) { self.components = components } + + /// Creates a new family member container from a sequence of components. + /// - Parameter component: A sequence of component tuples. + public init(component: S) where S: Sequence, S.Element == (repeat each C) { + components = Array(component) + } + + /// Creates a new family member container from a family components iterator. + /// - Parameter components: The iterator providing component tuples. + public init(components: Family.ComponentsIterator) { + self.components = Array(components) + } } extension CodingUserInfoKey { @@ -28,98 +34,62 @@ extension CodingUserInfoKey { /// /// This key is used to pass the `CodingStrategy` from the `Nexus` to the `FamilyMemberContainer` /// so that it knows how to encode or decode component types. - static let nexusCodingStrategy = CodingUserInfoKey(rawValue: "nexusCodingStrategy").unsafelyUnwrapped + public static let nexusCodingStrategy = CodingUserInfoKey(rawValue: "nexusCodingStrategy").unsafelyUnwrapped } // MARK: - encoding -extension FamilyMemberContainer: Encodable where R: FamilyEncoding { +extension FamilyMemberContainer: Encodable where repeat each C: Encodable { /// Encodes the family members into the given encoder. /// - Parameter encoder: The encoder to write data to. /// - Throws: An error if encoding fails. public func encode(to encoder: Encoder) throws { - let strategy = encoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy() + let strategy = encoder.userInfo[CodingUserInfoKey.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy() var familyContainer = encoder.unkeyedContainer() - try R.encode(componentsArray: components, into: &familyContainer, using: strategy) + for memberComponents in components { + var container = familyContainer.nestedContainer(keyedBy: DynamicCodingKey.self) + _ = try (repeat container.encode(each memberComponents, forKey: strategy.codingKey(for: (each C).self))) + } } } -/// A type that can encode values into a native format. -public protocol TopLevelEncoder { - /// The type this encoder produces. - associatedtype Output - - /// Encodes an instance of the indicated type. - /// - /// - Parameter value: The instance to encode. - /// - Returns: The encoded data. - /// - Throws: An error if encoding fails. - func encode(_ value: T) throws -> Self.Output - - /// Contextual user-provided information for use during decoding. - var userInfo: [CodingUserInfoKey: UserInfoValue] { get set } -} - -extension Family where R: FamilyEncoding { - /// Encode family members (entities) to data using a given encoder. - /// - /// The encoded members will *NOT* be removed from the nexus and will also stay present in this family. - /// - Parameter encoder: The data encoder. Data encoder respects the coding strategy set at `nexus.codingStrategy`. - /// - Returns: The encoded data. - /// - Complexity: O(N) where N is the number of family members. - public func encodeMembers(using encoder: inout Encoder) throws -> Encoder.Output { - encoder.userInfo[.nexusCodingStrategy] = nexus.codingStrategy - let components = [R.Components](self) - let container = FamilyMemberContainer(components: components) - return try encoder.encode(container) +extension Family where repeat each C: Encodable { + /// Encodes components into a keyed container using a strategy. + public static func encode( + components: (repeat each C), + into container: inout KeyedEncodingContainer, + using strategy: CodingStrategy + ) throws { + _ = try (repeat container.encode(each components, forKey: strategy.codingKey(for: (each C).self))) } } // MARK: - decoding -extension FamilyMemberContainer: Decodable where R: FamilyDecoding { - /// Creates a new family member container by decoding from the given decoder. - /// - Parameter decoder: The decoder to read data from. - /// - Throws: An error if decoding fails. - /// - Complexity: O(N) where N is the number of components in the container. +extension FamilyMemberContainer: Decodable where repeat each C: Decodable { + // Decodes the family members from the given decoder. + // - Parameter decoder: The decoder to read data from. + // - Throws: An error if decoding fails. + // - Complexity: O(N) where N is the number of components in the container. public init(from decoder: Decoder) throws { + let strategy = decoder.userInfo[CodingUserInfoKey.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy() var familyContainer = try decoder.unkeyedContainer() - let strategy = decoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy() - components = try R.decode(componentsIn: &familyContainer, using: strategy) + var componentsList: [(repeat each C)] = [] + while !familyContainer.isAtEnd { + let container = try familyContainer.nestedContainer(keyedBy: DynamicCodingKey.self) + let memberComponents = try (repeat container.decode((each C).self, forKey: strategy.codingKey(for: (each C).self))) + componentsList.append(memberComponents) + } + components = componentsList } } -/// A type that can decode values from a native format. -public protocol TopLevelDecoder { - /// The type this decoder accepts. - associatedtype Input - - /// Decodes an instance of the indicated type. - /// - Parameters: - /// - type: The type of the value to decode. - /// - from: The data to decode from. - /// - Returns: The decoded value. - /// - Throws: An error if decoding fails. - func decode(_ type: T.Type, from: Self.Input) throws -> T - - /// Contextual user-provided information for use during decoding. - var userInfo: [CodingUserInfoKey: UserInfoValue] { get set } -} - -extension Family where R: FamilyDecoding { - /// Decode family members (entities) from given data using a decoder. - /// - /// The decoded members will be added to the nexus and will be present in this family. - /// - Parameters: - /// - data: The data decoded by decoder. An unkeyed container of family members (keyed component containers) is expected. - /// - decoder: The decoder to use for decoding family member data. Decoder respects the coding strategy set at `nexus.codingStrategy`. - /// - Returns: returns the newly added entities. - /// - Complexity: O(N) where N is the number of family members in the data. - @discardableResult - public func decodeMembers(from data: Decoder.Input, using decoder: inout Decoder) throws -> [Entity] { - decoder.userInfo[.nexusCodingStrategy] = nexus.codingStrategy - let familyMembers = try decoder.decode(FamilyMemberContainer.self, from: data) - return familyMembers.components - .map { createMember(with: $0) } +extension Family where repeat each C: Decodable { + /// Decodes components from a keyed container using a strategy. + public static func decode( + from container: KeyedDecodingContainer, + using strategy: CodingStrategy + ) throws -> (repeat each C) { + try (repeat container.decode((each C).self, forKey: strategy.codingKey(for: (each C).self))) } } diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index b69cd736..d4de8a1f 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -9,7 +9,7 @@ /// /// Families are the primary way to iterate over entities in the ECS. /// They are defined by a set of required components (`requiresAll`) and optionally excluded components (`excludesAll`). -public struct Family { +public struct Family { /// The Nexus managing this family. @usableFromInline unowned let nexus: Nexus @@ -19,14 +19,18 @@ public struct Family { /// Initializes a new family. /// - Parameters: /// - nexus: The nexus instance. - /// - requiresAll: A closure returning the required component types. + /// - requiresAll: The required component types. /// - excludesAll: A list of excluded component types. - /// - Complexity: O(R + E) where R is the number of required components and E is the number of excluded components. - public init(nexus: Nexus, requiresAll: @autoclosure () -> R.ComponentTypes, excludesAll: [Component.Type]) { - let required = R(requiresAll()) + public init(nexus: Nexus, requiresAll: repeat (each C).Type, excludesAll: [Component.Type]) { self.nexus = nexus - let traits = FamilyTraitSet(requiresAll: required.componentTypes, excludesAll: excludesAll) - self.traits = traits + + var requiredIdentifiers: [ComponentIdentifier] = [] + // We iterate the pack to extract identifiers + _ = (repeat requiredIdentifiers.append((each C).identifier)) + + let excludedIdentifiers = excludesAll.map { $0.identifier } + + traits = FamilyTraitSet(requiresAll: Set(requiredIdentifiers), excludesAll: Set(excludedIdentifiers)) nexus.onFamilyInit(traits: traits) } @@ -75,19 +79,10 @@ public struct Family { public func destroyMembers() -> Bool { entities.reduce(!isEmpty) { $0 && nexus.destroy(entity: $1) } } - - /// Create a member entity with the given components assigned. - /// - Parameter builder: The family member builder. - /// - Returns: The newly created member entity. - /// - Complexity: O(M) where M is the number of families. - @discardableResult - public func createMember(@FamilyMemberBuilder using builder: () -> R.Components) -> Entity { - createMember(with: builder()) - } } extension Family: Equatable { - public static func == (lhs: Family, rhs: Family) -> Bool { + public static func == (lhs: Family, rhs: Family) -> Bool { lhs.nexus === rhs.nexus && lhs.traits == rhs.traits } @@ -114,7 +109,7 @@ extension Family { /// Creates a new iterator for the given family. /// - Parameter family: The family to iterate over. /// - Complexity: O(1) - public init(family: Family) { + public init(family: Family) { nexus = family.nexus memberIdsIterator = family.memberIds.makeIterator() } @@ -122,12 +117,11 @@ extension Family { /// Advances to the next component collection and returns it, or `nil` if no next element exists. /// - Returns: The next component collection in the sequence, or `nil`. /// - Complexity: O(R) where R is the number of required components. - public mutating func next() -> R.Components? { + public mutating func next() -> (repeat each C)? { guard let entityId: EntityIdentifier = memberIdsIterator.next() else { return nil } - - return R.components(nexus: nexus, entityId: entityId) + return (repeat nexus.get(unsafe: entityId) as (each C)) } } } @@ -152,7 +146,7 @@ extension Family { /// Creates a new iterator for the given family. /// - Parameter family: The family to iterate over. /// - Complexity: O(1) - public init(family: Family) { + public init(family: Family) { nexus = family.nexus memberIdsIterator = family.memberIds.makeIterator() } @@ -189,7 +183,7 @@ extension Family { /// Creates a new iterator for the given family. /// - Parameter family: The family to iterate over. /// - Complexity: O(1) - public init(family: Family) { + public init(family: Family) { nexus = family.nexus memberIdsIterator = family.memberIds.makeIterator() } @@ -197,11 +191,13 @@ extension Family { /// Advances to the next entity and components pair and returns it, or `nil` if no next element exists. /// - Returns: The next entity and components pair in the sequence, or `nil`. /// - Complexity: O(R) where R is the number of required components. - public mutating func next() -> R.EntityAndComponents? { + public mutating func next() -> (Entity, repeat each C)? { guard let entityId = memberIdsIterator.next() else { return nil } - return R.entityAndComponents(nexus: nexus, entityId: entityId) + let entity = Entity(nexus: nexus, id: entityId) + let components = (repeat nexus.get(unsafe: entityId) as (each C)) + return (entity, repeat each components) } } } @@ -219,8 +215,8 @@ extension Family { /// - Parameter components: The components required by this family. /// - Returns: The newly created entity. @discardableResult - public func createMember(with components: R.Components) -> Entity { - R.createMember(nexus: nexus, components: components) + public func createMember(with components: repeat each C) -> Entity { + nexus.createEntity(with: repeat each components) } } diff --git a/Sources/FirebladeECS/FamilyDecoding.swift b/Sources/FirebladeECS/FamilyDecoding.swift deleted file mode 100644 index 1aec5e0a..00000000 --- a/Sources/FirebladeECS/FamilyDecoding.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// FamilyDecoding.swift -// FirebladeECS -// -// Created by Christian Treffs on 05.08.20. -// - -/// A protocol that defines the requirements for decoding a family of components. -public protocol FamilyDecoding: FamilyRequirementsManaging { - /// Decodes an array of component collections from an unkeyed container. - /// - Parameters: - /// - unkeyedContainer: The unkeyed decoding container to read from. - /// - strategy: The coding strategy to use for determining coding keys. - /// - Returns: An array of decoded component collections. - /// - Throws: An error if decoding fails. - static func decode(componentsIn unkeyedContainer: inout UnkeyedDecodingContainer, using strategy: CodingStrategy) throws -> [Components] - - /// Decodes a collection of components from a keyed container. - /// - Parameters: - /// - container: The keyed decoding container to read from. - /// - strategy: The coding strategy to use for determining coding keys. - /// - Returns: A decoded component collection. - /// - Throws: An error if decoding fails. - static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> Components -} - -extension FamilyDecoding { - /// Decodes an array of component collections from an unkeyed container. - /// - /// This implementation iterates through the unkeyed container, decoding each element as a nested keyed container. - /// - /// - Parameters: - /// - unkeyedContainer: The unkeyed decoding container to read from. - /// - strategy: The coding strategy to use for determining coding keys. - /// - Returns: An array of decoded component collections. - /// - Throws: An error if decoding fails. - /// - Complexity: O(N) where N is the number of elements in the container. - public static func decode(componentsIn unkeyedContainer: inout UnkeyedDecodingContainer, using strategy: CodingStrategy) throws -> [Components] { - var components = [Components]() - if let count = unkeyedContainer.count { - components.reserveCapacity(count) - } - while !unkeyedContainer.isAtEnd { - let container = try unkeyedContainer.nestedContainer(keyedBy: DynamicCodingKey.self) - let comps = try Self.decode(componentsIn: container, using: strategy) - components.append(comps) - } - return components - } -} diff --git a/Sources/FirebladeECS/FamilyEncoding.swift b/Sources/FirebladeECS/FamilyEncoding.swift deleted file mode 100644 index 9d29444e..00000000 --- a/Sources/FirebladeECS/FamilyEncoding.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// FamilyEncoding.swift -// FirebladeECS -// -// Created by Christian Treffs on 05.08.20. -// - -/// A protocol that defines the requirements for encoding a family of components. -public protocol FamilyEncoding: FamilyRequirementsManaging { - /// Encodes an array of component collections into an unkeyed container. - /// - Parameters: - /// - componentsArray: The array of component collections to encode. - /// - container: The unkeyed encoding container to write to. - /// - strategy: The coding strategy to use for determining coding keys. - /// - Throws: An error if encoding fails. - static func encode(componentsArray: [Components], into container: inout UnkeyedEncodingContainer, using strategy: CodingStrategy) throws - - /// Encodes a collection of components into a keyed container. - /// - Parameters: - /// - components: The component collection to encode. - /// - container: The keyed encoding container to write to. - /// - strategy: The coding strategy to use for determining coding keys. - /// - Throws: An error if encoding fails. - static func encode(components: Components, into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws -} - -extension FamilyEncoding { - /// Encodes an array of component collections into an unkeyed container. - /// - /// This implementation iterates through the array, encoding each element as a nested keyed container. - /// - /// - Parameters: - /// - componentsArray: The array of component collections to encode. - /// - container: The unkeyed encoding container to write to. - /// - strategy: The coding strategy to use for determining coding keys. - /// - Throws: An error if encoding fails. - /// - Complexity: O(N) where N is the number of elements in the array. - public static func encode(componentsArray: [Components], into container: inout UnkeyedEncodingContainer, using strategy: CodingStrategy) throws { - for comps in componentsArray { - var container = container.nestedContainer(keyedBy: DynamicCodingKey.self) - try Self.encode(components: comps, into: &container, using: strategy) - } - } -} diff --git a/Sources/FirebladeECS/FamilyMemberBuilder.swift b/Sources/FirebladeECS/FamilyMemberBuilder.swift deleted file mode 100644 index 7c0f10c1..00000000 --- a/Sources/FirebladeECS/FamilyMemberBuilder.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// FamilyMemberBuilder.swift -// FirebladeECS -// -// Created by Christian Treffs on 07.08.20. -// - -/// A result builder for constructing family member component collections. -/// -/// This builder is used to provide a DSL-like syntax for creating family members -/// with the required components in a type-safe manner. -@resultBuilder -public enum FamilyMemberBuilder: Sendable {} diff --git a/Sources/FirebladeECS/FamilyRequirementsManaging.swift b/Sources/FirebladeECS/FamilyRequirementsManaging.swift deleted file mode 100644 index 42613c1d..00000000 --- a/Sources/FirebladeECS/FamilyRequirementsManaging.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// FamilyRequirementsManaging.swift -// FirebladeECS -// -// Created by Christian Treffs on 21.08.19. -// - -/// A protocol defining the requirements for a family. -/// -/// This protocol is used to define the components required by a family and how to retrieve them. -public protocol FamilyRequirementsManaging { - /// A tuple of component instances. - associatedtype Components - /// A tuple of component types. - associatedtype ComponentTypes - /// A tuple containing the entity and its components. - associatedtype EntityAndComponents - - /// Initializes with component types. - /// - Parameter types: The component types. - init(_ types: ComponentTypes) - - /// The component types required by this family. - var componentTypes: [Component.Type] { get } - - /// Retrieves the components for a given entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The entity identifier. - /// - Returns: The components. - static func components(nexus: Nexus, entityId: EntityIdentifier) -> Components - - /// Retrieves the entity and its components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The entity identifier. - /// - Returns: The entity and components. - static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> EntityAndComponents - - /// Creates a new member entity with the given components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - static func createMember(nexus: Nexus, components: Components) -> Entity -} diff --git a/Sources/FirebladeECS/FamilyTraitSet.swift b/Sources/FirebladeECS/FamilyTraitSet.swift index c4f2f87c..dc6c941b 100644 --- a/Sources/FirebladeECS/FamilyTraitSet.swift +++ b/Sources/FirebladeECS/FamilyTraitSet.swift @@ -25,7 +25,14 @@ public struct FamilyTraitSet { public init(requiresAll: [Component.Type], excludesAll: [Component.Type]) { let requiresAll = Set(requiresAll.map { $0.identifier }) let excludesAll = Set(excludesAll.map { $0.identifier }) + self.init(requiresAll: requiresAll, excludesAll: excludesAll) + } + /// Initializes a new family trait set. + /// - Parameters: + /// - requiresAll: The component identifiers required for membership. + /// - excludesAll: The component identifiers excluded from membership. + public init(requiresAll: Set, excludesAll: Set) { assert(FamilyTraitSet.isValid(requiresAll: requiresAll, excludesAll: excludesAll), "invalid family trait created - requiresAll: \(requiresAll), excludesAll: \(excludesAll)") self.requiresAll = requiresAll diff --git a/Sources/FirebladeECS/Foundation+Extensions.swift b/Sources/FirebladeECS/Foundation+Extensions.swift index fdb672d8..9978afb8 100644 --- a/Sources/FirebladeECS/Foundation+Extensions.swift +++ b/Sources/FirebladeECS/Foundation+Extensions.swift @@ -12,4 +12,43 @@ import Foundation extension JSONEncoder: TopLevelEncoder {} /// Conformance of `JSONDecoder` to `TopLevelDecoder` to support JSON decoding in ECS serialization. extension JSONDecoder: TopLevelDecoder {} + +#if canImport(Darwin) || swift(>=6.2) +public typealias UserInfoValue = any Sendable +#else +public typealias UserInfoValue = Any +#endif + +/// A type that can encode values into a native format. +public protocol TopLevelEncoder { + /// The type this encoder produces. + associatedtype Output + + /// Encodes an instance of the indicated type. + /// + /// - Parameter value: The instance to encode. + /// - Returns: The encoded data. + /// - Throws: An error if encoding fails. + func encode(_ value: T) throws -> Self.Output + + /// Contextual user-provided information for use during decoding. + var userInfo: [CodingUserInfoKey: UserInfoValue] { get set } +} + +/// A type that can decode values from a native format. +public protocol TopLevelDecoder { + /// The type this decoder accepts. + associatedtype Input + + /// Decodes an instance of the indicated type. + /// - Parameters: + /// - type: The type of the value to decode. + /// - from: The data to decode from. + /// - Returns: The decoded value. + /// - Throws: An error if decoding fails. + func decode(_ type: T.Type, from: Self.Input) throws -> T + + /// Contextual user-provided information for use during decoding. + var userInfo: [CodingUserInfoKey: UserInfoValue] { get set } +} #endif diff --git a/Sources/FirebladeECS/Generated/.gitkeep b/Sources/FirebladeECS/Generated/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Sources/FirebladeECS/Generated/Family.generated.swift b/Sources/FirebladeECS/Generated/Family.generated.swift deleted file mode 100644 index 2b4aa391..00000000 --- a/Sources/FirebladeECS/Generated/Family.generated.swift +++ /dev/null @@ -1,1219 +0,0 @@ -// Generated using Sourcery 2.3.0 — https://github.com/krzysztofzablocki/Sourcery -// DO NOT EDIT -// swiftlint:disable file_length -// swiftlint:disable function_parameter_count -// swiftlint:disable large_tuple -// swiftlint:disable line_length -// swiftlint:disable multiline_parameters - -// MARK: - Family 1 - -/// A family of entities with 1 components. -public typealias Family1 = Family> where Comp1: Component - -/// A protocol defining requirements for a family with 1 components. -public protocol RequiringComponents1: FamilyRequirementsManaging where Components == (Comp1) { - /// Component type 1. - associatedtype Comp1: Component -} - -/// A requirements manager for a family with 1 components. -public struct Requires1: FamilyRequirementsManaging where Comp1: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type)) { - componentTypes = [Comp1.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - return (comp1) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - return (entity, comp1) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1)) -> Entity { - nexus.createEntity(with: components) - } -} - -extension Requires1: RequiringComponents1 { } - -extension FamilyMemberBuilder where R: RequiringComponents1 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1) -> (R.Components) { - return (comp1) - } -} - -extension Requires1: FamilyEncoding where Comp1: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components, forKey: strategy.codingKey(for: Comp1.self)) - } -} - -extension Requires1: FamilyDecoding where Comp1: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - return comp1 - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 1 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 1 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requires: Comp1.self) - /// // iterate each entity's components - /// family.forEach { (comp1) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 1 required components each. - public func family( - requires comp1: Comp1.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family1 where Comp1: Component { - Family1( - nexus: self, - requiresAll: (comp1), - excludesAll: excludedComponents - ) - } -} - -// MARK: - Family 2 - -/// A family of entities with 2 components. -public typealias Family2 = Family> where Comp1: Component, Comp2: Component - -/// A protocol defining requirements for a family with 2 components. -public protocol RequiringComponents2: FamilyRequirementsManaging where Components == (Comp1, Comp2) { - /// Component type 1. - associatedtype Comp1: Component - /// Component type 2. - associatedtype Comp2: Component -} - -/// A requirements manager for a family with 2 components. -public struct Requires2: FamilyRequirementsManaging where Comp1: Component, Comp2: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type, Comp2.Type)) { - componentTypes = [Comp1.self, Comp2.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1, Comp2) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - return (comp1, comp2) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1, Comp2) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - return (entity, comp1, comp2) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1, Comp2)) -> Entity { - nexus.createEntity(with: components.0, components.1) - } -} - -extension Requires2: RequiringComponents2 { } - -extension FamilyMemberBuilder where R: RequiringComponents2 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1, _ comp2: R.Comp2) -> (R.Components) { - return (comp1, comp2) - } -} - -extension Requires2: FamilyEncoding where Comp1: Encodable, Comp2: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1, Comp2), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components.0, forKey: strategy.codingKey(for: Comp1.self)) - try container.encode(components.1, forKey: strategy.codingKey(for: Comp2.self)) - } -} - -extension Requires2: FamilyDecoding where Comp1: Decodable, Comp2: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1, Comp2) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - let comp2 = try container.decode(Comp2.self, forKey: strategy.codingKey(for: Comp2.self)) - return Components(comp1, comp2) - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 2 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 2 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - /// // iterate each entity's components - /// family.forEach { (comp1, comp2) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - comp2: Component type 2 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 2 required components each. - public func family( - requiresAll comp1: Comp1.Type, _ comp2: Comp2.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family2 where Comp1: Component, Comp2: Component { - Family2( - nexus: self, - requiresAll: (comp1, comp2), - excludesAll: excludedComponents - ) - } -} - -// MARK: - Family 3 - -/// A family of entities with 3 components. -public typealias Family3 = Family> where Comp1: Component, Comp2: Component, Comp3: Component - -/// A protocol defining requirements for a family with 3 components. -public protocol RequiringComponents3: FamilyRequirementsManaging where Components == (Comp1, Comp2, Comp3) { - /// Component type 1. - associatedtype Comp1: Component - /// Component type 2. - associatedtype Comp2: Component - /// Component type 3. - associatedtype Comp3: Component -} - -/// A requirements manager for a family with 3 components. -public struct Requires3: FamilyRequirementsManaging where Comp1: Component, Comp2: Component, Comp3: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type, Comp2.Type, Comp3.Type)) { - componentTypes = [Comp1.self, Comp2.self, Comp3.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1, Comp2, Comp3) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - return (comp1, comp2, comp3) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1, Comp2, Comp3) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - return (entity, comp1, comp2, comp3) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1, Comp2, Comp3)) -> Entity { - nexus.createEntity(with: components.0, components.1, components.2) - } -} - -extension Requires3: RequiringComponents3 { } - -extension FamilyMemberBuilder where R: RequiringComponents3 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1, _ comp2: R.Comp2, _ comp3: R.Comp3) -> (R.Components) { - return (comp1, comp2, comp3) - } -} - -extension Requires3: FamilyEncoding where Comp1: Encodable, Comp2: Encodable, Comp3: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1, Comp2, Comp3), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components.0, forKey: strategy.codingKey(for: Comp1.self)) - try container.encode(components.1, forKey: strategy.codingKey(for: Comp2.self)) - try container.encode(components.2, forKey: strategy.codingKey(for: Comp3.self)) - } -} - -extension Requires3: FamilyDecoding where Comp1: Decodable, Comp2: Decodable, Comp3: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1, Comp2, Comp3) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - let comp2 = try container.decode(Comp2.self, forKey: strategy.codingKey(for: Comp2.self)) - let comp3 = try container.decode(Comp3.self, forKey: strategy.codingKey(for: Comp3.self)) - return Components(comp1, comp2, comp3) - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 3 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 3 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - /// // iterate each entity's components - /// family.forEach { (comp1, comp2, comp3) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - comp2: Component type 2 required by members of this family. - /// - comp3: Component type 3 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 3 required components each. - public func family( - requiresAll comp1: Comp1.Type, _ comp2: Comp2.Type, _ comp3: Comp3.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family3 where Comp1: Component, Comp2: Component, Comp3: Component { - Family3( - nexus: self, - requiresAll: (comp1, comp2, comp3), - excludesAll: excludedComponents - ) - } -} - -// MARK: - Family 4 - -/// A family of entities with 4 components. -public typealias Family4 = Family> where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component - -/// A protocol defining requirements for a family with 4 components. -public protocol RequiringComponents4: FamilyRequirementsManaging where Components == (Comp1, Comp2, Comp3, Comp4) { - /// Component type 1. - associatedtype Comp1: Component - /// Component type 2. - associatedtype Comp2: Component - /// Component type 3. - associatedtype Comp3: Component - /// Component type 4. - associatedtype Comp4: Component -} - -/// A requirements manager for a family with 4 components. -public struct Requires4: FamilyRequirementsManaging where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type, Comp2.Type, Comp3.Type, Comp4.Type)) { - componentTypes = [Comp1.self, Comp2.self, Comp3.self, Comp4.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1, Comp2, Comp3, Comp4) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - return (comp1, comp2, comp3, comp4) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1, Comp2, Comp3, Comp4) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - return (entity, comp1, comp2, comp3, comp4) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1, Comp2, Comp3, Comp4)) -> Entity { - nexus.createEntity(with: components.0, components.1, components.2, components.3) - } -} - -extension Requires4: RequiringComponents4 { } - -extension FamilyMemberBuilder where R: RequiringComponents4 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1, _ comp2: R.Comp2, _ comp3: R.Comp3, _ comp4: R.Comp4) -> (R.Components) { - return (comp1, comp2, comp3, comp4) - } -} - -extension Requires4: FamilyEncoding where Comp1: Encodable, Comp2: Encodable, Comp3: Encodable, Comp4: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1, Comp2, Comp3, Comp4), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components.0, forKey: strategy.codingKey(for: Comp1.self)) - try container.encode(components.1, forKey: strategy.codingKey(for: Comp2.self)) - try container.encode(components.2, forKey: strategy.codingKey(for: Comp3.self)) - try container.encode(components.3, forKey: strategy.codingKey(for: Comp4.self)) - } -} - -extension Requires4: FamilyDecoding where Comp1: Decodable, Comp2: Decodable, Comp3: Decodable, Comp4: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1, Comp2, Comp3, Comp4) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - let comp2 = try container.decode(Comp2.self, forKey: strategy.codingKey(for: Comp2.self)) - let comp3 = try container.decode(Comp3.self, forKey: strategy.codingKey(for: Comp3.self)) - let comp4 = try container.decode(Comp4.self, forKey: strategy.codingKey(for: Comp4.self)) - return Components(comp1, comp2, comp3, comp4) - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 4 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 4 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - /// // iterate each entity's components - /// family.forEach { (comp1, comp2, comp3, comp4) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - comp2: Component type 2 required by members of this family. - /// - comp3: Component type 3 required by members of this family. - /// - comp4: Component type 4 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 4 required components each. - public func family( - requiresAll comp1: Comp1.Type, _ comp2: Comp2.Type, _ comp3: Comp3.Type, _ comp4: Comp4.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family4 where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component { - Family4( - nexus: self, - requiresAll: (comp1, comp2, comp3, comp4), - excludesAll: excludedComponents - ) - } -} - -// MARK: - Family 5 - -/// A family of entities with 5 components. -public typealias Family5 = Family> where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component - -/// A protocol defining requirements for a family with 5 components. -public protocol RequiringComponents5: FamilyRequirementsManaging where Components == (Comp1, Comp2, Comp3, Comp4, Comp5) { - /// Component type 1. - associatedtype Comp1: Component - /// Component type 2. - associatedtype Comp2: Component - /// Component type 3. - associatedtype Comp3: Component - /// Component type 4. - associatedtype Comp4: Component - /// Component type 5. - associatedtype Comp5: Component -} - -/// A requirements manager for a family with 5 components. -public struct Requires5: FamilyRequirementsManaging where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type, Comp2.Type, Comp3.Type, Comp4.Type, Comp5.Type)) { - componentTypes = [Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1, Comp2, Comp3, Comp4, Comp5) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - return (comp1, comp2, comp3, comp4, comp5) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1, Comp2, Comp3, Comp4, Comp5) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - return (entity, comp1, comp2, comp3, comp4, comp5) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1, Comp2, Comp3, Comp4, Comp5)) -> Entity { - nexus.createEntity(with: components.0, components.1, components.2, components.3, components.4) - } -} - -extension Requires5: RequiringComponents5 { } - -extension FamilyMemberBuilder where R: RequiringComponents5 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1, _ comp2: R.Comp2, _ comp3: R.Comp3, _ comp4: R.Comp4, _ comp5: R.Comp5) -> (R.Components) { - return (comp1, comp2, comp3, comp4, comp5) - } -} - -extension Requires5: FamilyEncoding where Comp1: Encodable, Comp2: Encodable, Comp3: Encodable, Comp4: Encodable, Comp5: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1, Comp2, Comp3, Comp4, Comp5), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components.0, forKey: strategy.codingKey(for: Comp1.self)) - try container.encode(components.1, forKey: strategy.codingKey(for: Comp2.self)) - try container.encode(components.2, forKey: strategy.codingKey(for: Comp3.self)) - try container.encode(components.3, forKey: strategy.codingKey(for: Comp4.self)) - try container.encode(components.4, forKey: strategy.codingKey(for: Comp5.self)) - } -} - -extension Requires5: FamilyDecoding where Comp1: Decodable, Comp2: Decodable, Comp3: Decodable, Comp4: Decodable, Comp5: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1, Comp2, Comp3, Comp4, Comp5) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - let comp2 = try container.decode(Comp2.self, forKey: strategy.codingKey(for: Comp2.self)) - let comp3 = try container.decode(Comp3.self, forKey: strategy.codingKey(for: Comp3.self)) - let comp4 = try container.decode(Comp4.self, forKey: strategy.codingKey(for: Comp4.self)) - let comp5 = try container.decode(Comp5.self, forKey: strategy.codingKey(for: Comp5.self)) - return Components(comp1, comp2, comp3, comp4, comp5) - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 5 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 5 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - /// // iterate each entity's components - /// family.forEach { (comp1, comp2, comp3, comp4, comp5) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - comp2: Component type 2 required by members of this family. - /// - comp3: Component type 3 required by members of this family. - /// - comp4: Component type 4 required by members of this family. - /// - comp5: Component type 5 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 5 required components each. - public func family( - requiresAll comp1: Comp1.Type, _ comp2: Comp2.Type, _ comp3: Comp3.Type, _ comp4: Comp4.Type, _ comp5: Comp5.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family5 where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component { - Family5( - nexus: self, - requiresAll: (comp1, comp2, comp3, comp4, comp5), - excludesAll: excludedComponents - ) - } -} - -// MARK: - Family 6 - -/// A family of entities with 6 components. -public typealias Family6 = Family> where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component - -/// A protocol defining requirements for a family with 6 components. -public protocol RequiringComponents6: FamilyRequirementsManaging where Components == (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6) { - /// Component type 1. - associatedtype Comp1: Component - /// Component type 2. - associatedtype Comp2: Component - /// Component type 3. - associatedtype Comp3: Component - /// Component type 4. - associatedtype Comp4: Component - /// Component type 5. - associatedtype Comp5: Component - /// Component type 6. - associatedtype Comp6: Component -} - -/// A requirements manager for a family with 6 components. -public struct Requires6: FamilyRequirementsManaging where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type, Comp2.Type, Comp3.Type, Comp4.Type, Comp5.Type, Comp6.Type)) { - componentTypes = [Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - let comp6: Comp6 = nexus.get(unsafe: entityId) - return (comp1, comp2, comp3, comp4, comp5, comp6) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - let comp6: Comp6 = nexus.get(unsafe: entityId) - return (entity, comp1, comp2, comp3, comp4, comp5, comp6) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6)) -> Entity { - nexus.createEntity(with: components.0, components.1, components.2, components.3, components.4, components.5) - } -} - -extension Requires6: RequiringComponents6 { } - -extension FamilyMemberBuilder where R: RequiringComponents6 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1, _ comp2: R.Comp2, _ comp3: R.Comp3, _ comp4: R.Comp4, _ comp5: R.Comp5, _ comp6: R.Comp6) -> (R.Components) { - return (comp1, comp2, comp3, comp4, comp5, comp6) - } -} - -extension Requires6: FamilyEncoding where Comp1: Encodable, Comp2: Encodable, Comp3: Encodable, Comp4: Encodable, Comp5: Encodable, Comp6: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components.0, forKey: strategy.codingKey(for: Comp1.self)) - try container.encode(components.1, forKey: strategy.codingKey(for: Comp2.self)) - try container.encode(components.2, forKey: strategy.codingKey(for: Comp3.self)) - try container.encode(components.3, forKey: strategy.codingKey(for: Comp4.self)) - try container.encode(components.4, forKey: strategy.codingKey(for: Comp5.self)) - try container.encode(components.5, forKey: strategy.codingKey(for: Comp6.self)) - } -} - -extension Requires6: FamilyDecoding where Comp1: Decodable, Comp2: Decodable, Comp3: Decodable, Comp4: Decodable, Comp5: Decodable, Comp6: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - let comp2 = try container.decode(Comp2.self, forKey: strategy.codingKey(for: Comp2.self)) - let comp3 = try container.decode(Comp3.self, forKey: strategy.codingKey(for: Comp3.self)) - let comp4 = try container.decode(Comp4.self, forKey: strategy.codingKey(for: Comp4.self)) - let comp5 = try container.decode(Comp5.self, forKey: strategy.codingKey(for: Comp5.self)) - let comp6 = try container.decode(Comp6.self, forKey: strategy.codingKey(for: Comp6.self)) - return Components(comp1, comp2, comp3, comp4, comp5, comp6) - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 6 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 6 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - /// // iterate each entity's components - /// family.forEach { (comp1, comp2, comp3, comp4, comp5, comp6) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - comp2: Component type 2 required by members of this family. - /// - comp3: Component type 3 required by members of this family. - /// - comp4: Component type 4 required by members of this family. - /// - comp5: Component type 5 required by members of this family. - /// - comp6: Component type 6 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 6 required components each. - public func family( - requiresAll comp1: Comp1.Type, _ comp2: Comp2.Type, _ comp3: Comp3.Type, _ comp4: Comp4.Type, _ comp5: Comp5.Type, _ comp6: Comp6.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family6 where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component { - Family6( - nexus: self, - requiresAll: (comp1, comp2, comp3, comp4, comp5, comp6), - excludesAll: excludedComponents - ) - } -} - -// MARK: - Family 7 - -/// A family of entities with 7 components. -public typealias Family7 = Family> where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component, Comp7: Component - -/// A protocol defining requirements for a family with 7 components. -public protocol RequiringComponents7: FamilyRequirementsManaging where Components == (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7) { - /// Component type 1. - associatedtype Comp1: Component - /// Component type 2. - associatedtype Comp2: Component - /// Component type 3. - associatedtype Comp3: Component - /// Component type 4. - associatedtype Comp4: Component - /// Component type 5. - associatedtype Comp5: Component - /// Component type 6. - associatedtype Comp6: Component - /// Component type 7. - associatedtype Comp7: Component -} - -/// A requirements manager for a family with 7 components. -public struct Requires7: FamilyRequirementsManaging where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component, Comp7: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type, Comp2.Type, Comp3.Type, Comp4.Type, Comp5.Type, Comp6.Type, Comp7.Type)) { - componentTypes = [Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - let comp6: Comp6 = nexus.get(unsafe: entityId) - let comp7: Comp7 = nexus.get(unsafe: entityId) - return (comp1, comp2, comp3, comp4, comp5, comp6, comp7) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - let comp6: Comp6 = nexus.get(unsafe: entityId) - let comp7: Comp7 = nexus.get(unsafe: entityId) - return (entity, comp1, comp2, comp3, comp4, comp5, comp6, comp7) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7)) -> Entity { - nexus.createEntity(with: components.0, components.1, components.2, components.3, components.4, components.5, components.6) - } -} - -extension Requires7: RequiringComponents7 { } - -extension FamilyMemberBuilder where R: RequiringComponents7 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1, _ comp2: R.Comp2, _ comp3: R.Comp3, _ comp4: R.Comp4, _ comp5: R.Comp5, _ comp6: R.Comp6, _ comp7: R.Comp7) -> (R.Components) { - return (comp1, comp2, comp3, comp4, comp5, comp6, comp7) - } -} - -extension Requires7: FamilyEncoding where Comp1: Encodable, Comp2: Encodable, Comp3: Encodable, Comp4: Encodable, Comp5: Encodable, Comp6: Encodable, Comp7: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components.0, forKey: strategy.codingKey(for: Comp1.self)) - try container.encode(components.1, forKey: strategy.codingKey(for: Comp2.self)) - try container.encode(components.2, forKey: strategy.codingKey(for: Comp3.self)) - try container.encode(components.3, forKey: strategy.codingKey(for: Comp4.self)) - try container.encode(components.4, forKey: strategy.codingKey(for: Comp5.self)) - try container.encode(components.5, forKey: strategy.codingKey(for: Comp6.self)) - try container.encode(components.6, forKey: strategy.codingKey(for: Comp7.self)) - } -} - -extension Requires7: FamilyDecoding where Comp1: Decodable, Comp2: Decodable, Comp3: Decodable, Comp4: Decodable, Comp5: Decodable, Comp6: Decodable, Comp7: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - let comp2 = try container.decode(Comp2.self, forKey: strategy.codingKey(for: Comp2.self)) - let comp3 = try container.decode(Comp3.self, forKey: strategy.codingKey(for: Comp3.self)) - let comp4 = try container.decode(Comp4.self, forKey: strategy.codingKey(for: Comp4.self)) - let comp5 = try container.decode(Comp5.self, forKey: strategy.codingKey(for: Comp5.self)) - let comp6 = try container.decode(Comp6.self, forKey: strategy.codingKey(for: Comp6.self)) - let comp7 = try container.decode(Comp7.self, forKey: strategy.codingKey(for: Comp7.self)) - return Components(comp1, comp2, comp3, comp4, comp5, comp6, comp7) - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 7 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 7 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - /// // iterate each entity's components - /// family.forEach { (comp1, comp2, comp3, comp4, comp5, comp6, comp7) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - comp2: Component type 2 required by members of this family. - /// - comp3: Component type 3 required by members of this family. - /// - comp4: Component type 4 required by members of this family. - /// - comp5: Component type 5 required by members of this family. - /// - comp6: Component type 6 required by members of this family. - /// - comp7: Component type 7 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 7 required components each. - public func family( - requiresAll comp1: Comp1.Type, _ comp2: Comp2.Type, _ comp3: Comp3.Type, _ comp4: Comp4.Type, _ comp5: Comp5.Type, _ comp6: Comp6.Type, _ comp7: Comp7.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family7 where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component, Comp7: Component { - Family7( - nexus: self, - requiresAll: (comp1, comp2, comp3, comp4, comp5, comp6, comp7), - excludesAll: excludedComponents - ) - } -} - -// MARK: - Family 8 - -/// A family of entities with 8 components. -public typealias Family8 = Family> where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component, Comp7: Component, Comp8: Component - -/// A protocol defining requirements for a family with 8 components. -public protocol RequiringComponents8: FamilyRequirementsManaging where Components == (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8) { - /// Component type 1. - associatedtype Comp1: Component - /// Component type 2. - associatedtype Comp2: Component - /// Component type 3. - associatedtype Comp3: Component - /// Component type 4. - associatedtype Comp4: Component - /// Component type 5. - associatedtype Comp5: Component - /// Component type 6. - associatedtype Comp6: Component - /// Component type 7. - associatedtype Comp7: Component - /// Component type 8. - associatedtype Comp8: Component -} - -/// A requirements manager for a family with 8 components. -public struct Requires8: FamilyRequirementsManaging where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component, Comp7: Component, Comp8: Component { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: (Comp1.Type, Comp2.Type, Comp3.Type, Comp4.Type, Comp5.Type, Comp6.Type, Comp7.Type, Comp8.Type)) { - componentTypes = [Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8) { - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - let comp6: Comp6 = nexus.get(unsafe: entityId) - let comp7: Comp7 = nexus.get(unsafe: entityId) - let comp8: Comp8 = nexus.get(unsafe: entityId) - return (comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8) { - let entity = Entity(nexus: nexus, id: entityId) - let comp1: Comp1 = nexus.get(unsafe: entityId) - let comp2: Comp2 = nexus.get(unsafe: entityId) - let comp3: Comp3 = nexus.get(unsafe: entityId) - let comp4: Comp4 = nexus.get(unsafe: entityId) - let comp5: Comp5 = nexus.get(unsafe: entityId) - let comp6: Comp6 = nexus.get(unsafe: entityId) - let comp7: Comp7 = nexus.get(unsafe: entityId) - let comp8: Comp8 = nexus.get(unsafe: entityId) - return (entity, comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8)) -> Entity { - nexus.createEntity(with: components.0, components.1, components.2, components.3, components.4, components.5, components.6, components.7) - } -} - -extension Requires8: RequiringComponents8 { } - -extension FamilyMemberBuilder where R: RequiringComponents8 { - /// Builds a block of components for a family member. - public static func buildBlock(_ comp1: R.Comp1, _ comp2: R.Comp2, _ comp3: R.Comp3, _ comp4: R.Comp4, _ comp5: R.Comp5, _ comp6: R.Comp6, _ comp7: R.Comp7, _ comp8: R.Comp8) -> (R.Components) { - return (comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8) - } -} - -extension Requires8: FamilyEncoding where Comp1: Encodable, Comp2: Encodable, Comp3: Encodable, Comp4: Encodable, Comp5: Encodable, Comp6: Encodable, Comp7: Encodable, Comp8: Encodable { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - try container.encode(components.0, forKey: strategy.codingKey(for: Comp1.self)) - try container.encode(components.1, forKey: strategy.codingKey(for: Comp2.self)) - try container.encode(components.2, forKey: strategy.codingKey(for: Comp3.self)) - try container.encode(components.3, forKey: strategy.codingKey(for: Comp4.self)) - try container.encode(components.4, forKey: strategy.codingKey(for: Comp5.self)) - try container.encode(components.5, forKey: strategy.codingKey(for: Comp6.self)) - try container.encode(components.6, forKey: strategy.codingKey(for: Comp7.self)) - try container.encode(components.7, forKey: strategy.codingKey(for: Comp8.self)) - } -} - -extension Requires8: FamilyDecoding where Comp1: Decodable, Comp2: Decodable, Comp3: Decodable, Comp4: Decodable, Comp5: Decodable, Comp6: Decodable, Comp7: Decodable, Comp8: Decodable { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> (Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8) { - let comp1 = try container.decode(Comp1.self, forKey: strategy.codingKey(for: Comp1.self)) - let comp2 = try container.decode(Comp2.self, forKey: strategy.codingKey(for: Comp2.self)) - let comp3 = try container.decode(Comp3.self, forKey: strategy.codingKey(for: Comp3.self)) - let comp4 = try container.decode(Comp4.self, forKey: strategy.codingKey(for: Comp4.self)) - let comp5 = try container.decode(Comp5.self, forKey: strategy.codingKey(for: Comp5.self)) - let comp6 = try container.decode(Comp6.self, forKey: strategy.codingKey(for: Comp6.self)) - let comp7 = try container.decode(Comp7.self, forKey: strategy.codingKey(for: Comp7.self)) - let comp8 = try container.decode(Comp8.self, forKey: strategy.codingKey(for: Comp8.self)) - return Components(comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8) - } -} - -extension Nexus { - /// Create a family of entities (aka members) having 8 required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the 8 required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - /// // iterate each entity's components - /// family.forEach { (comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - /// - comp1: Component type 1 required by members of this family. - /// - comp2: Component type 2 required by members of this family. - /// - comp3: Component type 3 required by members of this family. - /// - comp4: Component type 4 required by members of this family. - /// - comp5: Component type 5 required by members of this family. - /// - comp6: Component type 6 required by members of this family. - /// - comp7: Component type 7 required by members of this family. - /// - comp8: Component type 8 required by members of this family. - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having 8 required components each. - public func family( - requiresAll comp1: Comp1.Type, _ comp2: Comp2.Type, _ comp3: Comp3.Type, _ comp4: Comp4.Type, _ comp5: Comp5.Type, _ comp6: Comp6.Type, _ comp7: Comp7.Type, _ comp8: Comp8.Type, - excludesAll excludedComponents: Component.Type... - ) -> Family8 where Comp1: Component, Comp2: Component, Comp3: Component, Comp4: Component, Comp5: Component, Comp6: Component, Comp7: Component, Comp8: Component { - Family8( - nexus: self, - requiresAll: (comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8), - excludesAll: excludedComponents - ) - } -} diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index 46330cd6..9a12001f 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -45,6 +45,19 @@ extension Nexus { return assign(component: component, entityId: entityId) } + /// Assigns multiple components to an entity. + /// - Parameters: + /// - components: The components to assign. + /// - entity: The entity to assign the components to. + /// - Returns: `true` if all assignments were successful. + /// - Complexity: O(C * M) where C is the number of components and M is the number of families. + @discardableResult + public final func assign(components: repeat each C, to entity: Entity) -> Bool { + var success = true + _ = (repeat (success = success && assign(component: each components, to: entity))) + return success + } + /// Assigns a collection of components to an entity. /// - Parameters: /// - components: The collection of components to assign. diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index a3788d67..776229cc 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -28,6 +28,17 @@ extension Nexus { return newEntity } + /// Creates a new entity with the provided components. + /// - Parameter components: The components to assign to the new entity. + /// - Returns: The newly created entity. + /// - Complexity: O(C + M) where C is the number of components and M is the number of families. + @discardableResult + public func createEntity(with components: repeat each C) -> Entity { + let newEntity = createEntity() + assign(components: repeat each components, to: newEntity) + return newEntity + } + /// Creates a new entity with a collection of components. /// - Parameter components: The components to assign to the new entity. /// - Returns: The newly created entity. diff --git a/Sources/FirebladeECS/Nexus+Family.swift b/Sources/FirebladeECS/Nexus+Family.swift index b7ab7780..5e787537 100644 --- a/Sources/FirebladeECS/Nexus+Family.swift +++ b/Sources/FirebladeECS/Nexus+Family.swift @@ -62,4 +62,81 @@ extension Nexus { public func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool { members(withFamilyTraits: traits).contains(entityId.id) } + + /// Create a family of entities (aka members) having 1 required components. + /// + /// A family is a collection of entities with uniform component types per entity. + /// Entities that are be part of this family will have at least the 1 required components, + /// but may have more components assigned. + /// + /// A family is just a view on (component) data, creating them is cheap. + /// Use them to iterate efficiently over entities with the same components assigned. + /// Families with the same requirements provide a view on the same collection of entities (aka members). + /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. + /// + /// **General usage** + /// ```swift + /// let family = nexus.family(requires: Comp1.self) + /// // iterate each entity's components + /// family.forEach { (comp1) in + /// ... + /// } + /// ``` + /// **Caveats** + /// - Component types must be unique per family + /// - Component type order is arbitrary + /// + /// - Parameters: + /// - comp: Component type required by members of this family. + /// - excludedComponents: All component types that must not be assigned to an entity in this family. + /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. + /// - Returns: The family of entities having 1 required components each. + public func family( + requires comp: Comp.Type, + excludesAll excludedComponents: Component.Type... + ) -> Family where Comp: Component { + Family( + nexus: self, + requiresAll: (comp), + excludesAll: excludedComponents + ) + } + + /// Create a family of entities (aka members) having the required components. + /// + /// A family is a collection of entities with uniform component types per entity. + /// Entities that are be part of this family will have at least the required components, + /// but may have more components assigned. + /// + /// A family is just a view on (component) data, creating them is cheap. + /// Use them to iterate efficiently over entities with the same components assigned. + /// Families with the same requirements provide a view on the same collection of entities (aka members). + /// + /// **General usage** + /// ```swift + /// let family = nexus.family(requiresAll: Comp1.self, Comp2.self) + /// // iterate each entity's components + /// family.forEach { (comp1: Comp1, comp2: Comp2) in + /// ... + /// } + /// ``` + /// **Caveats** + /// - Component types must be unique per family + /// - Component type order is arbitrary + /// + /// - Parameters: + /// - componentTypes: Component types required by members of this family. + /// - excludedComponents: All component types that must not be assigned to an entity in this family. + /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. + /// - Returns: The family of entities having the required components. + public func family( + requiresAll componentTypes: repeat (each C).Type, + excludesAll excludedComponents: Component.Type... + ) -> Family { + Family( + nexus: self, + requiresAll: repeat each componentTypes, + excludesAll: excludedComponents + ) + } } diff --git a/Sources/FirebladeECS/Stencils/Family.stencil b/Sources/FirebladeECS/Stencils/Family.stencil deleted file mode 100644 index 53e2bb97..00000000 --- a/Sources/FirebladeECS/Stencils/Family.stencil +++ /dev/null @@ -1,175 +0,0 @@ -// swiftlint:disable file_length -// swiftlint:disable function_parameter_count -// swiftlint:disable large_tuple -// swiftlint:disable line_length -// swiftlint:disable multiline_parameters -{% for idx in 1...8 %} -{% map 1...idx into components using index %}Comp{{ index }}{% endmap %} -{% set CompParams %}{{components|join: ", "}}{% endset %} -{% map components into compWhere using comp %}{{ comp }}: Component{% endmap %} -{% set CompsWhere %}{{compWhere|join: ", "}}{% endset %} -{% map components into compEncodable using comp %}{{ comp }}: Encodable{% endmap %} -{% set CompsWhereEncodable %}{{compEncodable|join: ", "}}{% endset %} -{% map components into compsDecodable using comp %}{{ comp }}: Decodable{% endmap %} -{% set CompsWhereDecodable %}{{compsDecodable|join: ", "}}{% endset %} -{% map components into compTypes using comp %}{{ comp }}.Type{% endmap %} -{% set CompsTypes %}{{compTypes|join: ", "}}{% endset %} -{% map components into compSelf using comp %}{{ comp }}.self{% endmap %} -{% set CompsSelf %}{{compSelf|join: ", "}}{% endset %} -{% map components into compsLowercased using comp %}{{ comp|lowercase }}{% endmap %} -{% set CompsLowercased %}{{compsLowercased|join: ", "}}{% endset %} -{% set CompsTuple %}{% for comp in components %}components.{{ forloop.counter0 }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endset %} -{% map components into compsTypeParams using comp %}{% if not maploop.first %}_ {% endif %}{{ comp|lowercase }}: {{ comp }}.Type{% endmap %} -{% set CompsTypeParams %}{{compsTypeParams|join: ", "}}{% endset %} -{% map components into compsNamedParams using comp %}{% if not maploop.first %}_ {% endif %}{{ comp|lowercase }}: {{ comp }}{% endmap %} -{% set CompsNamedParams %}{{compsNamedParams|join: ", "}}{% endset %} -{% map components into compsNamedRParams using comp %}_ {{ comp|lowercase }}: R.{{ comp }}{% endmap %} -{% set CompsNamedRParams %}{{compsNamedRParams|join: ", "}}{% endset %} - -// MARK: - Family {{ idx }} - -/// A family of entities with {{ idx }} components. -public typealias Family{{ idx }}<{{ CompParams }}> = Family> where {{ CompsWhere }} - -/// A protocol defining requirements for a family with {{ idx }} components. -public protocol RequiringComponents{{ idx }}: FamilyRequirementsManaging where Components == ({{ CompParams }}) { - {% for comp in components %} - /// Component type {{ forloop.counter }}. - associatedtype {{ comp }}: Component - {% endfor %} -} - -/// A requirements manager for a family with {{ idx }} components. -public struct Requires{{ idx }}<{{ CompParams }}>: FamilyRequirementsManaging where {{ CompsWhere }} { - /// The component types. - public let componentTypes: [Component.Type] - - /// Initializes with component types. - public init(_ components: ({{ CompsTypes }})) { - componentTypes = [{{ CompsSelf}}] - } - - /// Retrieves components for an entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The components of the entity. - public static func components(nexus: Nexus, entityId: EntityIdentifier) -> ({{ CompParams }}) { - {% for comp in components %} - let {{ comp|lowercase }}: {{ comp }} = nexus.get(unsafe: entityId) - {% endfor %} - return ({{ CompsLowercased }}) - } - - /// Retrieves entity and components. - /// - Parameters: - /// - nexus: The nexus instance. - /// - entityId: The identifier of the entity. - /// - Returns: The entity and its components. - public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, {{ CompParams }}) { - let entity = Entity(nexus: nexus, id: entityId) - {% for comp in components %} - let {{ comp|lowercase }}: {{ comp }} = nexus.get(unsafe: entityId) - {% endfor %} - return (entity, {{ CompsLowercased }}) - } - - /// Creates a member entity. - /// - Parameters: - /// - nexus: The nexus instance. - /// - components: The components to assign. - /// - Returns: The created entity. - public static func createMember(nexus: Nexus, components: ({{ CompParams }})) -> Entity { - {% if compEncodable.count == 1 %}nexus.createEntity(with: components){% else %}nexus.createEntity(with: {{ CompsTuple }}){% endif %} - } -} - -extension Requires{{ idx }}: RequiringComponents{{ idx }} { } - -extension FamilyMemberBuilder where R: RequiringComponents{{ idx }} { - /// Builds a block of components for a family member. - public static func buildBlock({{ CompsNamedRParams }}) -> (R.Components) { - return ({{ CompsLowercased }}) - } -} - -extension Requires{{ idx }}: FamilyEncoding where {{ CompsWhereEncodable }} { - /// Encodes the components. - /// - Parameters: - /// - components: The components to encode. - /// - container: The encoding container. - /// - strategy: The coding strategy. - /// - Throws: An error if encoding fails. - public static func encode(components: ({{ CompParams }}), into container: inout KeyedEncodingContainer, using strategy: CodingStrategy) throws { - {% if compEncodable.count == 1 %} - try container.encode(components, forKey: strategy.codingKey(for: {{ CompsSelf }})) - {% else %} - {% for comp in compSelf %} - try container.encode(components.{{ forloop.counter0 }}, forKey: strategy.codingKey(for: {{ comp }})) - {% endfor %} - {% endif %} - } -} - -extension Requires{{ idx }}: FamilyDecoding where {{ CompsWhereDecodable }} { - /// Decodes the components. - /// - Parameters: - /// - container: The decoding container. - /// - strategy: The coding strategy. - /// - Returns: The decoded components. - /// - Throws: An error if decoding fails. - public static func decode(componentsIn container: KeyedDecodingContainer, using strategy: CodingStrategy) throws -> ({{ CompParams }}) { - {% for comp in components %} - let {{ comp|lowercase }} = try container.decode({{ comp }}.self, forKey: strategy.codingKey(for: {{ comp }}.self)) - {% endfor %} - {% if compEncodable.count == 1 %} - return {{ CompsLowercased }} - {% else %} - return Components({{ CompsLowercased }}) - {% endif %} - } -} - -extension Nexus { - /// Create a family of entities (aka members) having {{ components.count }} required components. - /// - /// A family is a collection of entities with uniform component types per entity. - /// Entities that are be part of this family will have at least the {{ components.count }} required components, - /// but may have more components assigned. - /// - /// A family is just a view on (component) data, creating them is cheap. - /// Use them to iterate efficiently over entities with the same components assigned. - /// Families with the same requirements provide a view on the same collection of entities (aka members). - /// A family conforms to the `LazySequenceProtocol` and therefore can be accessed like any other (lazy) sequence. - /// - /// **General usage** - /// ```swift - /// let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - /// // iterate each entity's components - /// family.forEach { ({{ CompsLowercased }}) in - /// ... - /// } - /// ``` - /// **Caveats** - /// - Component types must be unique per family - /// - Component type order is arbitrary - /// - /// - Parameters: - {% for comp in compsLowercased %} - /// - {{ comp }}: Component type {{ forloop.counter }} required by members of this family. - {% endfor %} - /// - excludedComponents: All component types that must not be assigned to an entity in this family. - /// - Complexity: O(1) for existing families, O(N) where N is the number of entities for new families. - /// - Returns: The family of entities having {{ components.count }} required components each. - public func family<{{ CompParams }}>( - {% if components.count == 1 %}requires{% else %}requiresAll{%endif%} {{ CompsTypeParams }}, - excludesAll excludedComponents: Component.Type... - ) -> Family{{ idx }}<{{ CompParams }}> where {{ CompsWhere }} { - Family{{ idx }}<{{ CompParams }}>( - nexus: self, - requiresAll: ({{ CompsLowercased }}), - excludesAll: excludedComponents - ) - } -} -{% endfor %} diff --git a/Tests/FirebladeECSPerformanceTests/Base.swift b/Tests/FirebladeECSPerformanceTests/Base.swift index f344c00c..126a4f24 100644 --- a/Tests/FirebladeECSPerformanceTests/Base.swift +++ b/Tests/FirebladeECSPerformanceTests/Base.swift @@ -48,7 +48,7 @@ class Color: Component, @unchecked Sendable { } class ExampleSystem { - private let family: Family2 + private let family: Family init(nexus: Nexus) { family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self) diff --git a/Tests/FirebladeECSTests/Base.swift b/Tests/FirebladeECSTests/Base.swift index 72ad41f1..52b9acce 100644 --- a/Tests/FirebladeECSTests/Base.swift +++ b/Tests/FirebladeECSTests/Base.swift @@ -43,6 +43,40 @@ final class Name: Component, DefaultInitializable, @unchecked Sendable { self.init(name: "") } } +extension Name: Codable { } + +final class Comp1: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} +final class Comp2: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} +final class Comp3: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} +final class Comp4: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} +final class Comp5: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} +final class Comp6: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} +final class Comp7: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} +final class Comp8: Component, @unchecked Sendable, Codable { + var value: Int + init(_ value: Int) { self.value = value } +} final class Position: Component, DefaultInitializable, @unchecked Sendable { var x: Int @@ -101,7 +135,7 @@ class Index: Component, @unchecked Sendable { } } -final class MyComponent: Component, @unchecked Sendable { +final class MyComponent: Component, Codable, @unchecked Sendable { var name: String var flag: Bool @@ -110,8 +144,6 @@ final class MyComponent: Component, @unchecked Sendable { self.flag = flag } } -extension MyComponent: Decodable { } -extension MyComponent: Encodable { } final class YourComponent: Component, @unchecked Sendable { var number: Float @@ -129,7 +161,7 @@ final class SingleGameState: SingleComponent, @unchecked Sendable { } class ExampleSystem { - private let family: Family2 + private let family: Family init(nexus: Nexus) { family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self) @@ -145,7 +177,7 @@ class ExampleSystem { class ColorSystem { let nexus: Nexus - lazy var colors = nexus.family(requires: Color.self) + lazy var colors = nexus.family(requiresAll: Color.self) init(nexus: Nexus) { self.nexus = nexus @@ -162,12 +194,12 @@ class ColorSystem { } class PositionSystem { - let positions: Family1 + let positions: Family var velocity: Double = 4.0 init(nexus: Nexus) { - positions = nexus.family(requires: Position.self) + positions = nexus.family(requiresAll: Position.self) } func randNorm() -> Double { diff --git a/Tests/FirebladeECSTests/FamilyCodingTests.swift b/Tests/FirebladeECSTests/FamilyCodingTests.swift index 2fe65233..4c85e8ce 100644 --- a/Tests/FirebladeECSTests/FamilyCodingTests.swift +++ b/Tests/FirebladeECSTests/FamilyCodingTests.swift @@ -55,8 +55,8 @@ import Foundation let nexus = Nexus() let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self) - family.createMember(with: (MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23))) - family.createMember(with: (MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45))) + family.createMember(with: MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23)) + family.createMember(with: MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45)) #expect(family.count == 2) var jsonEncoder = JSONEncoder() @@ -104,8 +104,8 @@ import Foundation let nexus = Nexus() let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self, Position.self) - family.createMember(with: (MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23), Position(x: 1, y: 2))) - family.createMember(with: (MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45), Position(x: 3, y: 4))) + family.createMember(with: MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23), Position(x: 1, y: 2)) + family.createMember(with: MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45), Position(x: 3, y: 4)) #expect(family.count == 2) var jsonEncoder = JSONEncoder() @@ -163,8 +163,8 @@ import Foundation let nexus = Nexus() let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self, Position.self, Color.self) - family.createMember(with: (MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23), Position(x: 1, y: 2), Color(r: 1, g: 2, b: 3))) - family.createMember(with: (MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45), Position(x: 3, y: 4), Color(r: 4, g: 5, b: 6))) + family.createMember(with: MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23), Position(x: 1, y: 2), Color(r: 1, g: 2, b: 3)) + family.createMember(with: MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45), Position(x: 3, y: 4), Color(r: 4, g: 5, b: 6)) #expect(family.count == 2) var jsonEncoder = JSONEncoder() @@ -230,8 +230,8 @@ import Foundation let nexus = Nexus() let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self, Position.self, Color.self, Party.self) - family.createMember(with: (MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23), Position(x: 1, y: 2), Color(r: 1, g: 2, b: 3), Party(partying: true))) - family.createMember(with: (MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45), Position(x: 3, y: 4), Color(r: 4, g: 5, b: 6), Party(partying: false))) + family.createMember(with: MyComponent(name: "My Name", flag: true), YourComponent(number: 1.23), Position(x: 1, y: 2), Color(r: 1, g: 2, b: 3), Party(partying: true)) + family.createMember(with: MyComponent(name: "Your Name", flag: false), YourComponent(number: 3.45), Position(x: 3, y: 4), Color(r: 4, g: 5, b: 6), Party(partying: false)) #expect(family.count == 2) var jsonEncoder = JSONEncoder() @@ -350,8 +350,9 @@ import Foundation } @Test func codingStrategyFallback() throws { - let component = MyComponent(name: "A", flag: true) - let container = FamilyMemberContainer>(components: [component]) + let component1 = MyComponent(name: "A", flag: true) + // Use two different components to avoid key collision + let container = FamilyMemberContainer(components: component1) let encoder = JSONEncoder() // No user info set, so it should fallback to DefaultCodingStrategy @@ -359,7 +360,7 @@ import Foundation let decoder = JSONDecoder() // No user info set, so it should fallback to DefaultCodingStrategy - let decoded = try decoder.decode(FamilyMemberContainer>.self, from: data) + let decoded = try decoder.decode(FamilyMemberContainer.self, from: data) #expect(decoded.components.count == 1) #expect(decoded.components[0].name == "A") diff --git a/Tests/FirebladeECSTests/FamilyTests.swift b/Tests/FirebladeECSTests/FamilyTests.swift deleted file mode 100644 index 752f971f..00000000 --- a/Tests/FirebladeECSTests/FamilyTests.swift +++ /dev/null @@ -1,246 +0,0 @@ -// -// FamilyTests.swift -// FirebladeECSTests -// -// Created by Christian Treffs on 09.10.17. -// - -@testable import FirebladeECS -import Testing - -@Suite struct FamilyTests { - private func createDefaultEntity(in nexus: Nexus) { - let e = nexus.createEntity() - e.assign(Position(x: 1, y: 2)) - e.assign(Color()) - } - - @Test func familyCreation() { - let nexus = Nexus() - let family = nexus.family(requires: Position.self, - excludesAll: Name.self) - - #expect(family.nexus === nexus) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 0) - #expect(nexus.numEntities == 0) - #expect(!family.traits.description.isEmpty) - #expect(!family.traits.debugDescription.isEmpty) - - let traits = FamilyTraitSet(requiresAll: [Position.self], excludesAll: [Name.self]) - #expect(family.traits == traits) - } - - @Test func familyReuse() { - let nexus = Nexus() - let familyA = nexus.family(requires: Position.self, - excludesAll: Name.self) - - let familyB = nexus.family(requires: Position.self, - excludesAll: Name.self) - - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 0) - - #expect(familyA == familyB) - } - - @Test func familyAbandoned() { - let nexus = Nexus() - #expect(nexus.numFamilies == 0) - #expect(nexus.numComponents == 0) - #expect(nexus.numEntities == 0) - _ = nexus.family(requires: Position.self) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 0) - #expect(nexus.numEntities == 0) - let entity = nexus.createEntity() - #expect(!entity.has(Position.self)) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 0) - #expect(nexus.numEntities == 1) - entity.assign(Position(x: 1, y: 1)) - #expect(entity.has(Position.self)) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 1) - #expect(nexus.numEntities == 1) - entity.remove(Position.self) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 0) - #expect(nexus.numEntities == 1) - nexus.destroy(entity: entity) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 0) - #expect(nexus.numEntities == 0) - } - - @Test func familyLateMember() { - let nexus = Nexus() - let eEarly = nexus.createEntity(with: Position(x: 1, y: 2)) - #expect(nexus.numFamilies == 0) - #expect(nexus.numComponents == 1) - #expect(nexus.numEntities == 1) - let family = nexus.family(requires: Position.self) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 1) - #expect(nexus.numEntities == 1) - let eLate = nexus.createEntity(with: Position(x: 1, y: 2)) - #expect(nexus.numFamilies == 1) - #expect(nexus.numComponents == 2) - #expect(nexus.numEntities == 2) - #expect(family.isMember(eEarly)) - #expect(family.isMember(eLate)) - } - - @Test func familyExchange() { - let nexus = Nexus() - let number: Int = 10 - - for i in 0...encode(components: components, into: &container, using: strategy) + } + } + + let comp = Comp1(42) + let wrapper = FamilyWrapper(components: (comp), strategy: strategy) + let data = try encoder.encode(wrapper) + + #expect(data.count > 0) + } +} diff --git a/Tests/FirebladeECSTests/FamilyTests2.swift b/Tests/FirebladeECSTests/FamilyTests2.swift new file mode 100644 index 00000000..5cb80293 --- /dev/null +++ b/Tests/FirebladeECSTests/FamilyTests2.swift @@ -0,0 +1,45 @@ +// +// FamilyTests2.swift +// FirebladeECSTests +// +// Created by Conductor on 2026-02-13. +// + +import Testing +@testable import FirebladeECS + +@Suite struct FamilyTests2 { + + @Test func memberCreation() { + let nexus = Nexus() + let family = nexus.family(requiresAll: Comp1.self, Comp2.self) + #expect(family.isEmpty) + let entity = family.createMember(with: Comp1(1), Comp2(2)) + + #expect(family.count == 1) + #expect(entity.numComponents == 2) + #expect(entity.has(Comp1.self)) + #expect(entity.has(Comp2.self)) + } + + @Test func componentIteration() { + let nexus = Nexus() + let family = nexus.family(requiresAll: Comp1.self, Comp2.self) + #expect(family.isEmpty) + + let count = 100 + for i in 0.. 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 } }, - { "Comp1":{ "value" : 0 } }, - { "Comp1":{ "value" : 0 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requires: Comp1.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requires: Comp1.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Family 2 test case -@Suite struct Family2Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - Comp1(0), Comp2(1) - )) - #expect(family.count == 1) - #expect(entity.numComponents == 2) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 2) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - let entity = family.createMember { - Comp1(0) - Comp2(1) - } - #expect(family.count == 1) - #expect(entity.numComponents == 2) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 2) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { (comp1, comp2) in - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(comp2.value == 1 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == 2) - #expect(entity[\Comp1.value] == 0 * 1_000_000 + idx) - #expect(entity[\Comp2.value] == 1 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, comp1, comp2) in - #expect(entity.numComponents == 2) - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(entity[\Comp1.self] == comp1) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(entity[\Comp2.self] == comp2) - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i) - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Family 3 test case -@Suite struct Family3Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - Comp1(0), Comp2(1), Comp3(2) - )) - #expect(family.count == 1) - #expect(entity.numComponents == 3) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 3) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - let entity = family.createMember { - Comp1(0) - Comp2(1) - Comp3(2) - } - #expect(family.count == 1) - #expect(entity.numComponents == 3) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 3) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { (comp1, comp2, comp3) in - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(comp3.value == 2 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == 3) - #expect(entity[\Comp1.value] == 0 * 1_000_000 + idx) - #expect(entity[\Comp2.value] == 1 * 1_000_000 + idx) - #expect(entity[\Comp3.value] == 2 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, comp1, comp2, comp3) in - #expect(entity.numComponents == 3) - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(entity[\Comp1.self] == comp1) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(entity[\Comp2.self] == comp2) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(entity[\Comp3.self] == comp3) - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i) - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Family 4 test case -@Suite struct Family4Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - Comp1(0), Comp2(1), Comp3(2), Comp4(3) - )) - #expect(family.count == 1) - #expect(entity.numComponents == 4) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 4) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - let entity = family.createMember { - Comp1(0) - Comp2(1) - Comp3(2) - Comp4(3) - } - #expect(family.count == 1) - #expect(entity.numComponents == 4) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 4) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { (comp1, comp2, comp3, comp4) in - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(comp4.value == 3 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == 4) - #expect(entity[\Comp1.value] == 0 * 1_000_000 + idx) - #expect(entity[\Comp2.value] == 1 * 1_000_000 + idx) - #expect(entity[\Comp3.value] == 2 * 1_000_000 + idx) - #expect(entity[\Comp4.value] == 3 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, comp1, comp2, comp3, comp4) in - #expect(entity.numComponents == 4) - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(entity[\Comp1.self] == comp1) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(entity[\Comp2.self] == comp2) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(entity[\Comp3.self] == comp3) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(entity[\Comp4.self] == comp4) - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i) - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Family 5 test case -@Suite struct Family5Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - Comp1(0), Comp2(1), Comp3(2), Comp4(3), Comp5(4) - )) - #expect(family.count == 1) - #expect(entity.numComponents == 5) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 5) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - let entity = family.createMember { - Comp1(0) - Comp2(1) - Comp3(2) - Comp4(3) - Comp5(4) - } - #expect(family.count == 1) - #expect(entity.numComponents == 5) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 5) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { (comp1, comp2, comp3, comp4, comp5) in - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(comp5.value == 4 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == 5) - #expect(entity[\Comp1.value] == 0 * 1_000_000 + idx) - #expect(entity[\Comp2.value] == 1 * 1_000_000 + idx) - #expect(entity[\Comp3.value] == 2 * 1_000_000 + idx) - #expect(entity[\Comp4.value] == 3 * 1_000_000 + idx) - #expect(entity[\Comp5.value] == 4 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, comp1, comp2, comp3, comp4, comp5) in - #expect(entity.numComponents == 5) - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(entity[\Comp1.self] == comp1) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(entity[\Comp2.self] == comp2) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(entity[\Comp3.self] == comp3) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(entity[\Comp4.self] == comp4) - #expect(comp5.value == 4 * 1_000_000 + idx) - #expect(entity[\Comp5.self] == comp5) - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i) - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Family 6 test case -@Suite struct Family6Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - Comp1(0), Comp2(1), Comp3(2), Comp4(3), Comp5(4), Comp6(5) - )) - #expect(family.count == 1) - #expect(entity.numComponents == 6) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 6) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - #expect(entity[\Comp6.value] == 5) - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - let entity = family.createMember { - Comp1(0) - Comp2(1) - Comp3(2) - Comp4(3) - Comp5(4) - Comp6(5) - } - #expect(family.count == 1) - #expect(entity.numComponents == 6) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 6) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - #expect(entity[\Comp6.value] == 5) - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { (comp1, comp2, comp3, comp4, comp5, comp6) in - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(comp5.value == 4 * 1_000_000 + idx) - #expect(comp6.value == 5 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == 6) - #expect(entity[\Comp1.value] == 0 * 1_000_000 + idx) - #expect(entity[\Comp2.value] == 1 * 1_000_000 + idx) - #expect(entity[\Comp3.value] == 2 * 1_000_000 + idx) - #expect(entity[\Comp4.value] == 3 * 1_000_000 + idx) - #expect(entity[\Comp5.value] == 4 * 1_000_000 + idx) - #expect(entity[\Comp6.value] == 5 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, comp1, comp2, comp3, comp4, comp5, comp6) in - #expect(entity.numComponents == 6) - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(entity[\Comp1.self] == comp1) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(entity[\Comp2.self] == comp2) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(entity[\Comp3.self] == comp3) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(entity[\Comp4.self] == comp4) - #expect(comp5.value == 4 * 1_000_000 + idx) - #expect(entity[\Comp5.self] == comp5) - #expect(comp6.value == 5 * 1_000_000 + idx) - #expect(entity[\Comp6.self] == comp6) - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i) - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Family 7 test case -@Suite struct Family7Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - Comp1(0), Comp2(1), Comp3(2), Comp4(3), Comp5(4), Comp6(5), Comp7(6) - )) - #expect(family.count == 1) - #expect(entity.numComponents == 7) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 7) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - #expect(entity[\Comp6.value] == 5) - #expect(entity[\Comp7.value] == 6) - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - let entity = family.createMember { - Comp1(0) - Comp2(1) - Comp3(2) - Comp4(3) - Comp5(4) - Comp6(5) - Comp7(6) - } - #expect(family.count == 1) - #expect(entity.numComponents == 7) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 7) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - #expect(entity[\Comp6.value] == 5) - #expect(entity[\Comp7.value] == 6) - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { (comp1, comp2, comp3, comp4, comp5, comp6, comp7) in - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(comp5.value == 4 * 1_000_000 + idx) - #expect(comp6.value == 5 * 1_000_000 + idx) - #expect(comp7.value == 6 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == 7) - #expect(entity[\Comp1.value] == 0 * 1_000_000 + idx) - #expect(entity[\Comp2.value] == 1 * 1_000_000 + idx) - #expect(entity[\Comp3.value] == 2 * 1_000_000 + idx) - #expect(entity[\Comp4.value] == 3 * 1_000_000 + idx) - #expect(entity[\Comp5.value] == 4 * 1_000_000 + idx) - #expect(entity[\Comp6.value] == 5 * 1_000_000 + idx) - #expect(entity[\Comp7.value] == 6 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, comp1, comp2, comp3, comp4, comp5, comp6, comp7) in - #expect(entity.numComponents == 7) - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(entity[\Comp1.self] == comp1) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(entity[\Comp2.self] == comp2) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(entity[\Comp3.self] == comp3) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(entity[\Comp4.self] == comp4) - #expect(comp5.value == 4 * 1_000_000 + idx) - #expect(entity[\Comp5.self] == comp5) - #expect(comp6.value == 5 * 1_000_000 + idx) - #expect(entity[\Comp6.self] == comp6) - #expect(comp7.value == 6 * 1_000_000 + idx) - #expect(entity[\Comp7.self] == comp7) - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i) - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 },"Comp7":{ "value" : 6 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 },"Comp7":{ "value" : 6 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 },"Comp7":{ "value" : 6 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Family 8 test case -@Suite struct Family8Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - Comp1(0), Comp2(1), Comp3(2), Comp4(3), Comp5(4), Comp6(5), Comp7(6), Comp8(7) - )) - #expect(family.count == 1) - #expect(entity.numComponents == 8) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 8) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - #expect(entity[\Comp6.value] == 5) - #expect(entity[\Comp7.value] == 6) - #expect(entity[\Comp8.value] == 7) - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - let entity = family.createMember { - Comp1(0) - Comp2(1) - Comp3(2) - Comp4(3) - Comp5(4) - Comp6(5) - Comp7(6) - Comp8(7) - } - #expect(family.count == 1) - #expect(entity.numComponents == 8) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == 8) - #expect(entity[\Comp1.value] == 0) - #expect(entity[\Comp2.value] == 1) - #expect(entity[\Comp3.value] == 2) - #expect(entity[\Comp4.value] == 3) - #expect(entity[\Comp5.value] == 4) - #expect(entity[\Comp6.value] == 5) - #expect(entity[\Comp7.value] == 6) - #expect(entity[\Comp8.value] == 7) - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i), Comp8(7 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { (comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8) in - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(comp5.value == 4 * 1_000_000 + idx) - #expect(comp6.value == 5 * 1_000_000 + idx) - #expect(comp7.value == 6 * 1_000_000 + idx) - #expect(comp8.value == 7 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i), Comp8(7 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == 8) - #expect(entity[\Comp1.value] == 0 * 1_000_000 + idx) - #expect(entity[\Comp2.value] == 1 * 1_000_000 + idx) - #expect(entity[\Comp3.value] == 2 * 1_000_000 + idx) - #expect(entity[\Comp4.value] == 3 * 1_000_000 + idx) - #expect(entity[\Comp5.value] == 4 * 1_000_000 + idx) - #expect(entity[\Comp6.value] == 5 * 1_000_000 + idx) - #expect(entity[\Comp7.value] == 6 * 1_000_000 + idx) - #expect(entity[\Comp8.value] == 7 * 1_000_000 + idx) - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i), Comp8(7 * 1_000_000 + i) - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8) in - #expect(entity.numComponents == 8) - #expect(comp1.value == 0 * 1_000_000 + idx) - #expect(entity[\Comp1.self] == comp1) - #expect(comp2.value == 1 * 1_000_000 + idx) - #expect(entity[\Comp2.self] == comp2) - #expect(comp3.value == 2 * 1_000_000 + idx) - #expect(entity[\Comp3.self] == comp3) - #expect(comp4.value == 3 * 1_000_000 + idx) - #expect(entity[\Comp4.self] == comp4) - #expect(comp5.value == 4 * 1_000_000 + idx) - #expect(entity[\Comp5.self] == comp5) - #expect(comp6.value == 5 * 1_000_000 + idx) - #expect(entity[\Comp6.self] == comp6) - #expect(comp7.value == 6 * 1_000_000 + idx) - #expect(entity[\Comp7.self] == comp7) - #expect(comp8.value == 7 * 1_000_000 + idx) - #expect(entity[\Comp8.self] == comp8) - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - Comp1(0 * 1_000_000 + i), Comp2(1 * 1_000_000 + i), Comp3(2 * 1_000_000 + i), Comp4(3 * 1_000_000 + i), Comp5(4 * 1_000_000 + i), Comp6(5 * 1_000_000 + i), Comp7(6 * 1_000_000 + i), Comp8(7 * 1_000_000 + i) - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 },"Comp7":{ "value" : 6 },"Comp8":{ "value" : 7 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 },"Comp7":{ "value" : 6 },"Comp8":{ "value" : 7 } }, - { "Comp1":{ "value" : 0 },"Comp2":{ "value" : 1 },"Comp3":{ "value" : 2 },"Comp4":{ "value" : 3 },"Comp5":{ "value" : 4 },"Comp6":{ "value" : 5 },"Comp7":{ "value" : 6 },"Comp8":{ "value" : 7 } } - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family(requiresAll: Comp1.self, Comp2.self, Comp3.self, Comp4.self, Comp5.self, Comp6.self, Comp7.self, Comp8.self) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -// MARK: - Components -final class Comp1: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp1: Equatable { - static func == (lhs: Comp1, rhs: Comp1) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp1: Codable { } - -final class Comp2: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp2: Equatable { - static func == (lhs: Comp2, rhs: Comp2) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp2: Codable { } - -final class Comp3: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp3: Equatable { - static func == (lhs: Comp3, rhs: Comp3) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp3: Codable { } - -final class Comp4: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp4: Equatable { - static func == (lhs: Comp4, rhs: Comp4) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp4: Codable { } - -final class Comp5: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp5: Equatable { - static func == (lhs: Comp5, rhs: Comp5) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp5: Codable { } - -final class Comp6: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp6: Equatable { - static func == (lhs: Comp6, rhs: Comp6) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp6: Codable { } - -final class Comp7: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp7: Equatable { - static func == (lhs: Comp7, rhs: Comp7) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp7: Codable { } - -final class Comp8: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp8: Equatable { - static func == (lhs: Comp8, rhs: Comp8) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp8: Codable { } - diff --git a/Tests/FirebladeECSTests/NexusEventDelegateTests.swift b/Tests/FirebladeECSTests/NexusEventDelegateTests.swift index da1e1cda..b2eb7078 100644 --- a/Tests/FirebladeECSTests/NexusEventDelegateTests.swift +++ b/Tests/FirebladeECSTests/NexusEventDelegateTests.swift @@ -149,9 +149,9 @@ import Testing let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self) nexus.delegate = delegateTester - family.createMember(with: (MyComponent(name: "Bla", flag: true), YourComponent(number: 85))) - family.createMember(with: (MyComponent(name: "Hello", flag: false), YourComponent(number: 05050))) - family.createMember(with: (MyComponent(name: "asdasd", flag: true), YourComponent(number: 9494949))) + family.createMember(with: MyComponent(name: "Bla", flag: true), YourComponent(number: 85)) + family.createMember(with: MyComponent(name: "Hello", flag: false), YourComponent(number: 05050)) + family.createMember(with: MyComponent(name: "asdasd", flag: true), YourComponent(number: 9494949)) #expect(eventsFamilyMemberRemoved.count == 0) #expect(eventsComponentRemoved.count == 0) @@ -193,14 +193,14 @@ import Testing #expect(eventsComponentAdded.count == 0) #expect(eventsEntityCreated.count == 0) - family.createMember(with: (MyComponent(name: "Bla", flag: true), YourComponent(number: 85))) + family.createMember(with: MyComponent(name: "Bla", flag: true), YourComponent(number: 85)) #expect(family.count == 1) #expect(eventsMemberAdded.count == 1) #expect(eventsComponentAdded.count == 2) #expect(eventsEntityCreated.count == 1) - family.createMember(with: (MyComponent(name: "Hello", flag: false), YourComponent(number: 05050))) + family.createMember(with: MyComponent(name: "Hello", flag: false), YourComponent(number: 05050)) #expect(family.count == 2) #expect(eventsMemberAdded.count == 2) #expect(eventsComponentAdded.count == 4) diff --git a/Tests/FirebladeECSTests/NexusFamilyEdgeCaseTests.swift b/Tests/FirebladeECSTests/NexusFamilyEdgeCaseTests.swift index 226329ac..d3f4265f 100644 --- a/Tests/FirebladeECSTests/NexusFamilyEdgeCaseTests.swift +++ b/Tests/FirebladeECSTests/NexusFamilyEdgeCaseTests.swift @@ -18,7 +18,7 @@ import Testing // Check if entity1 can become member in nexus2 // It should return false because nexus2 doesn't know about entity1 - let family2 = nexus2.family(requires: Position.self) + let family2 = nexus2.family(requiresAll: Position.self) #expect(nexus2.canBecomeMember(entity1, in: family2.traits) == false) } diff --git a/Tests/FirebladeECSTests/Stencils/FamilyTests.stencil b/Tests/FirebladeECSTests/Stencils/FamilyTests.stencil deleted file mode 100644 index 730710a3..00000000 --- a/Tests/FirebladeECSTests/Stencils/FamilyTests.stencil +++ /dev/null @@ -1,202 +0,0 @@ -import FirebladeECS -import Testing -import Foundation - -{% for idx in 1...8 %} -{% map 1...idx into components using index %}Comp{{ index }}{% endmap %} -{% set CompParams %}{{components|join: ", "}}{% endset %} -{% map components into compWhere using comp %}{{ comp }}: Component{% endmap %} -{% set CompsWhere %}{{compWhere|join: ", "}}{% endset %} -{% map components into compEncodable using comp %}{{ comp }}: Encodable{% endmap %} -{% set CompsWhereEncodable %}{{compEncodable|join: ", "}}{% endset %} -{% map components into compsDecodable using comp %}{{ comp }}: Decodable{% endmap %} -{% set CompsWhereDecodable %}{{compsDecodable|join: ", "}}{% endset %} -{% map components into compTypes using comp %}{{ comp }}.Type{% endmap %} -{% set CompsTypes %}{{compTypes|join: ", "}}{% endset %} -{% map components into compSelf using comp %}{{ comp }}.self{% endmap %} -{% set CompsSelf %}{{compSelf|join: ", "}}{% endset %} -{% map components into compsLowercased using comp %}{{ comp|lowercase }}{% endmap %} -{% set CompsLowercased %}{{compsLowercased|join: ", "}}{% endset %} -{% map components into compsTuple using comp %}components.{{ maploop.counter }}{% endmap %} -{% set CompsTuple %}{{compsTuple|join: ", "}}{% endset %} -{% map components into compsParams using comp %}{% if not maploop.first %}_ {% endif %}{{ comp|lowercase }}: {{ comp }}.Type{% endmap %} -{% set CompsParams %}{{compsParams|join: ", "}}{% endset %} -{% map components into compsJsonInner using comp %}"{{ comp }}":{ "value" : {{ maploop.counter0 }} }{% endmap %} -{% map 0...2 into compsJson %}{ {{compsJsonInner|join: ","}} }{% endmap %} -// MARK: - Family {{ idx }} test case -@Suite struct Family{{ idx }}Tests { - @Test func memberCreation() { - let nexus = Nexus() - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - let entity = family.createMember(with: ( - {% for comp in components %}{{ comp }}({{ forloop.counter0 }}){% if not forloop.last %}, {% endif %}{% endfor %} - )) - #expect(family.count == 1) - #expect(entity.numComponents == {{ idx }}) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == {{ idx }}) - {% for comp in components %} - #expect(entity[\{{ comp }}.value] == {{ forloop.counter0 }}) - {% endfor %} - } - - @Test func memberCreationBuilder() { - let nexus = Nexus() - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - let entity = family.createMember { - {% for comp in components %} - {{ comp }}({{ forloop.counter0 }}) - {% endfor %} - } - #expect(family.count == 1) - #expect(entity.numComponents == {{ idx }}) - #expect(nexus.numFamilies == 1) - #expect(nexus.numEntities == 1) - #expect(nexus.numComponents == {{ idx }}) - {% for comp in components %} - #expect(entity[\{{ comp }}.value] == {{ forloop.counter0 }}) - {% endfor %} - } - - @Test func componentIteration() { - let nexus = Nexus() - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - {% for comp in components %}{{ comp }}({{ forloop.counter0 }} * 1_000_000 + i){% if not forloop.last %}, {% endif %}{% endfor %} - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.forEach { ({{ CompsLowercased }}) in - {% for comp in compsLowercased %} - #expect({{ comp }}.value == {{ forloop.counter0 }} * 1_000_000 + idx) - {% endfor %} - idx += 1 - } - } - - @Test func entityIteration() { - let nexus = Nexus() - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - {% for comp in components %}{{ comp }}({{ forloop.counter0 }} * 1_000_000 + i){% if not forloop.last %}, {% endif %}{% endfor %} - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entities.forEach { (entity) in - #expect(entity.numComponents == {{ idx }}) - {% for comp in components %} - #expect(entity[\{{ comp }}.value] == {{ forloop.counter0 }} * 1_000_000 + idx) - {% endfor %} - idx += 1 - } - } - - @Test func entityComponentIteration() { - let nexus = Nexus() - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - for i in 0..<10_000 { - family.createMember(with: ( - {% for comp in components %}{{ comp }}({{ forloop.counter0 }} * 1_000_000 + i){% if not forloop.last %}, {% endif %}{% endfor %} - )) - } - #expect(family.count == 10_000) - var idx: Int = 0 - family.entityAndComponents.forEach { (entity, {{ CompsLowercased }}) in - #expect(entity.numComponents == {{ idx }}) - {% for comp in components %} - #expect({{ comp|lowercase }}.value == {{ forloop.counter0 }} * 1_000_000 + idx) - #expect(entity[\{{ comp }}.self] == {{ comp|lowercase }}) - {% endfor %} - idx += 1 - } - } - - @Test func familyEncoding() throws { - let nexus = Nexus() - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - for i in 0..<100 { - family.createMember(with: ( - {% for comp in components %}{{ comp }}({{ forloop.counter0 }} * 1_000_000 + i){% if not forloop.last %}, {% endif %}{% endfor %} - )) - } - #expect(family.count == 100) - - var jsonEncoder = JSONEncoder() - let encodedData = try family.encodeMembers(using: &jsonEncoder) - #expect(encodedData.count > 10) - guard let jsonString = String(data: encodedData, encoding: .utf8) else { - Issue.record("Failed to read string from encoded data \(encodedData.count)") - return - } - - let expectedStart = "[{" - #expect(String(jsonString.prefix(expectedStart.count)) == expectedStart) - let expectedEnd = "}]" - #expect(String(jsonString.suffix(expectedEnd.count)) == expectedEnd) - } - - @Test func familyDecoding() throws { - let nexus = Nexus() - let jsonString: String = """ - [ - {% for i in 0...2 %} - { {% for comp in components %}"{{ comp }}":{ "value" : {{ forloop.counter0 }} }{% if not forloop.last %},{% endif %}{% endfor %} }{% if not forloop.last %},{% endif %} - {% endfor %} - ] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder) - #expect(newEntities.count == 3) - #expect(family.count == 3) - } - - @Test func familyFailDecoding() { - let nexus = Nexus() - let jsonString = """ - [{ "SomeOtherComp": { "someValue": "fail" } }] - """ - guard let jsonData = jsonString.data(using: .utf8) else { - Issue.record("Failed to read data from json string \(jsonString.count)") - return - } - let family = nexus.family({% if components.count == 1 %}requires{% else %}requiresAll{%endif%}: {{ CompsSelf }}) - #expect(family.isEmpty) - var jsonDecoder = JSONDecoder() - #expect(throws: Error.self) { - try family.decodeMembers(from: jsonData, using: &jsonDecoder) - } - } -} - -{% endfor %} -// MARK: - Components -{% for idx in 1...8 %} -final class Comp{{ idx }}: Component, @unchecked Sendable { - var value: Int - init(_ value: Int) { self.value = value } -} -extension Comp{{ idx }}: Equatable { - static func == (lhs: Comp{{ idx }}, rhs: Comp{{ idx }}) -> Bool { - lhs === rhs && lhs.value == rhs.value - } -} -extension Comp{{ idx }}: Codable { } - -{% endfor %} \ No newline at end of file