Merge branch 'feature/family-coding' into develop
This commit is contained in:
commit
a0e6f05edc
|
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// Family+Coding.swift
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 22.07.20.
|
||||
//
|
||||
|
||||
private struct FamilyMemberContainer<R> where R: FamilyRequirementsManaging {
|
||||
let components: [R.Components]
|
||||
}
|
||||
|
||||
extension CodingUserInfoKey {
|
||||
fileprivate static let nexusCodingStrategy = CodingUserInfoKey(rawValue: "nexusCodingStrategy")!
|
||||
}
|
||||
|
||||
// MARK: - encoding
|
||||
extension FamilyMemberContainer: Encodable where R: FamilyEncoding {
|
||||
func encode(to encoder: Encoder) throws {
|
||||
let strategy = encoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy()
|
||||
var familyContainer = encoder.unkeyedContainer()
|
||||
try R.encode(components: components, into: &familyContainer, using: strategy)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TopLevelEncoder {
|
||||
/// The type this encoder produces.
|
||||
associatedtype Output
|
||||
|
||||
/// Encodes an instance of the indicated type.
|
||||
///
|
||||
/// - Parameter value: The instance to encode.
|
||||
func encode<T>(_ value: T) throws -> Self.Output where T: Encodable
|
||||
|
||||
/// Contextual user-provided information for use during decoding.
|
||||
var userInfo: [CodingUserInfoKey: Any] { 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.
|
||||
public func encodeMembers<Encoder>(using encoder: inout Encoder) throws -> Encoder.Output where Encoder: TopLevelEncoder {
|
||||
encoder.userInfo[.nexusCodingStrategy] = nexus.codingStrategy
|
||||
let components: [R.Components] = self.map { $0 }
|
||||
let container = FamilyMemberContainer<R>(components: components)
|
||||
return try encoder.encode(container)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - decoding
|
||||
extension FamilyMemberContainer: Decodable where R: FamilyDecoding {
|
||||
init(from decoder: Decoder) throws {
|
||||
var familyContainer = try decoder.unkeyedContainer()
|
||||
let strategy = decoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy()
|
||||
self.components = try R.decode(componentsIn: &familyContainer, using: strategy)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TopLevelDecoder {
|
||||
/// The type this decoder accepts.
|
||||
associatedtype Input
|
||||
|
||||
/// Decodes an instance of the indicated type.
|
||||
func decode<T>(_ type: T.Type, from: Self.Input) throws -> T where T: Decodable
|
||||
|
||||
/// Contextual user-provided information for use during decoding.
|
||||
var userInfo: [CodingUserInfoKey: Any] { 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. A 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.
|
||||
@discardableResult
|
||||
public func decodeMembers<Decoder>(from data: Decoder.Input, using decoder: inout Decoder) throws -> [Entity] where Decoder: TopLevelDecoder {
|
||||
decoder.userInfo[.nexusCodingStrategy] = nexus.codingStrategy
|
||||
let familyMembers = try decoder.decode(FamilyMemberContainer<R>.self, from: data)
|
||||
return familyMembers.components
|
||||
.map { createMember(with: $0) }
|
||||
}
|
||||
}
|
||||
|
|
@ -184,3 +184,17 @@ extension Family {
|
|||
}
|
||||
|
||||
extension Family.RelativesIterator: LazySequenceProtocol { }
|
||||
|
||||
// MARK: - member creation
|
||||
extension Family {
|
||||
/// Create a new entity with components required by this family.
|
||||
///
|
||||
/// Since the created entity will meet the requirements of this family it
|
||||
/// will automatically become member of this 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,22 @@ public struct Requires1<A>: FamilyRequirementsManaging where A: Component {
|
|||
let childCompA: A = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: parentCompA, child: childCompA)
|
||||
}
|
||||
|
||||
public static func createMember(nexus: Nexus, components: A) -> Entity {
|
||||
nexus.createEntity(with: components)
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires1: FamilyDecoding where A: Decodable {
|
||||
public static func decode(componentsIn container: KeyedDecodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws -> A {
|
||||
try container.decode(A.self, forKey: strategy.codingKey(for: A.self))
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires1: FamilyEncoding where A: Encodable {
|
||||
public static func encode(components: Components, into container: inout KeyedEncodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws {
|
||||
try container.encode(components, forKey: strategy.codingKey(for: A.self))
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public struct Requires2<A, B>: FamilyRequirementsManaging where A: Component, B:
|
|||
public init(_ components: (A.Type, B.Type)) {
|
||||
componentTypes = [ A.self, B.self]
|
||||
}
|
||||
|
||||
public static func components(nexus: Nexus, entityId: EntityIdentifier) -> (A, B) {
|
||||
let compA: A = nexus.get(unsafeComponentFor: entityId)
|
||||
let compB: B = nexus.get(unsafeComponentFor: entityId)
|
||||
|
|
@ -35,6 +36,25 @@ public struct Requires2<A, B>: FamilyRequirementsManaging where A: Component, B:
|
|||
let ccB: B = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: (pcA, pcB), child: (ccA, ccB))
|
||||
}
|
||||
|
||||
public static func createMember(nexus: Nexus, components: (A, B)) -> Entity {
|
||||
nexus.createEntity(with: components.0, components.1)
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires2: FamilyEncoding where A: Encodable, B: Encodable {
|
||||
public static func encode(components: (A, B), into container: inout KeyedEncodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws {
|
||||
try container.encode(components.0, forKey: strategy.codingKey(for: A.self))
|
||||
try container.encode(components.1, forKey: strategy.codingKey(for: B.self))
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires2: FamilyDecoding where A: Decodable, B: Decodable {
|
||||
public static func decode(componentsIn container: KeyedDecodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws -> (A, B) {
|
||||
let compA = try container.decode(A.self, forKey: strategy.codingKey(for: A.self))
|
||||
let compB = try container.decode(B.self, forKey: strategy.codingKey(for: B.self))
|
||||
return Components(compA, compB)
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,27 @@ public struct Requires3<A, B, C>: FamilyRequirementsManaging where A: Component,
|
|||
let ccC: C = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: (pcA, pcB, pcC), child: (ccA, ccB, ccC))
|
||||
}
|
||||
|
||||
public static func createMember(nexus: Nexus, components: (A, B, C)) -> Entity {
|
||||
nexus.createEntity(with: components.0, components.1, components.2)
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires3: FamilyEncoding where A: Encodable, B: Encodable, C: Encodable {
|
||||
public static func encode(components: (A, B, C), into container: inout KeyedEncodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws {
|
||||
try container.encode(components.0, forKey: strategy.codingKey(for: A.self))
|
||||
try container.encode(components.1, forKey: strategy.codingKey(for: B.self))
|
||||
try container.encode(components.2, forKey: strategy.codingKey(for: C.self))
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires3: FamilyDecoding where A: Decodable, B: Decodable, C: Decodable {
|
||||
public static func decode(componentsIn container: KeyedDecodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws -> (A, B, C) {
|
||||
let compA = try container.decode(A.self, forKey: strategy.codingKey(for: A.self))
|
||||
let compB = try container.decode(B.self, forKey: strategy.codingKey(for: B.self))
|
||||
let compC = try container.decode(C.self, forKey: strategy.codingKey(for: C.self))
|
||||
return Components(compA, compB, compC)
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,29 @@ public struct Requires4<A, B, C, D>: FamilyRequirementsManaging where A: Compone
|
|||
let ccD: D = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: (pcA, pcB, pcC, pcD), child: (ccA, ccB, ccC, ccD))
|
||||
}
|
||||
|
||||
public static func createMember(nexus: Nexus, components: (A, B, C, D)) -> Entity {
|
||||
nexus.createEntity(with: components.0, components.1, components.2, components.3)
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires4: FamilyEncoding where A: Encodable, B: Encodable, C: Encodable, D: Encodable {
|
||||
public static func encode(components: (A, B, C, D), into container: inout KeyedEncodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws {
|
||||
try container.encode(components.0, forKey: strategy.codingKey(for: A.self))
|
||||
try container.encode(components.1, forKey: strategy.codingKey(for: B.self))
|
||||
try container.encode(components.2, forKey: strategy.codingKey(for: C.self))
|
||||
try container.encode(components.3, forKey: strategy.codingKey(for: D.self))
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires4: FamilyDecoding where A: Decodable, B: Decodable, C: Decodable, D: Decodable {
|
||||
public static func decode(componentsIn container: KeyedDecodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws -> (A, B, C, D) {
|
||||
let compA = try container.decode(A.self, forKey: strategy.codingKey(for: A.self))
|
||||
let compB = try container.decode(B.self, forKey: strategy.codingKey(for: B.self))
|
||||
let compC = try container.decode(C.self, forKey: strategy.codingKey(for: C.self))
|
||||
let compD = try container.decode(D.self, forKey: strategy.codingKey(for: D.self))
|
||||
return Components(compA, compB, compC, compD)
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,31 @@ public struct Requires5<A, B, C, D, E>: FamilyRequirementsManaging where A: Comp
|
|||
return (parent: (pcA, pcB, pcC, pcD, pcE),
|
||||
child: (ccA, ccB, ccC, ccD, ccE))
|
||||
}
|
||||
|
||||
public static func createMember(nexus: Nexus, components: (A, B, C, D, E)) -> Entity {
|
||||
nexus.createEntity(with: components.0, components.1, components.2, components.3, components.4)
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires5: FamilyEncoding where A: Encodable, B: Encodable, C: Encodable, D: Encodable, E: Encodable {
|
||||
public static func encode(components: (A, B, C, D, E), into container: inout KeyedEncodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws {
|
||||
try container.encode(components.0, forKey: strategy.codingKey(for: A.self))
|
||||
try container.encode(components.1, forKey: strategy.codingKey(for: B.self))
|
||||
try container.encode(components.2, forKey: strategy.codingKey(for: C.self))
|
||||
try container.encode(components.3, forKey: strategy.codingKey(for: D.self))
|
||||
try container.encode(components.4, forKey: strategy.codingKey(for: E.self))
|
||||
}
|
||||
}
|
||||
|
||||
extension Requires5: FamilyDecoding where A: Decodable, B: Decodable, C: Decodable, D: Decodable, E: Decodable {
|
||||
public static func decode(componentsIn container: KeyedDecodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws -> (A, B, C, D, E) {
|
||||
let compA = try container.decode(A.self, forKey: strategy.codingKey(for: A.self))
|
||||
let compB = try container.decode(B.self, forKey: strategy.codingKey(for: B.self))
|
||||
let compC = try container.decode(C.self, forKey: strategy.codingKey(for: C.self))
|
||||
let compD = try container.decode(D.self, forKey: strategy.codingKey(for: D.self))
|
||||
let compE = try container.decode(E.self, forKey: strategy.codingKey(for: E.self))
|
||||
return Components(compA, compB, compC, compD, compE)
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// FamilyRequirementsManaging.swift
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
||||
public protocol FamilyRequirementsManaging {
|
||||
associatedtype Components
|
||||
associatedtype ComponentTypes
|
||||
associatedtype EntityAndComponents
|
||||
associatedtype RelativesDescending
|
||||
|
||||
init(_ types: ComponentTypes)
|
||||
|
||||
var componentTypes: [Component.Type] { get }
|
||||
|
||||
static func components(nexus: Nexus, entityId: EntityIdentifier) -> Components
|
||||
static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> EntityAndComponents
|
||||
static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) -> RelativesDescending
|
||||
|
||||
static func createMember(nexus: Nexus, components: Components) -> Entity
|
||||
}
|
||||
|
||||
public protocol FamilyEncoding: FamilyRequirementsManaging {
|
||||
static func encode(components: [Components], into container: inout UnkeyedEncodingContainer, using strategy: CodingStrategy) throws
|
||||
static func encode(components: Components, into container: inout KeyedEncodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws
|
||||
}
|
||||
|
||||
extension FamilyEncoding {
|
||||
public static func encode(components: [Components], into container: inout UnkeyedEncodingContainer, using strategy: CodingStrategy) throws {
|
||||
for comps in components {
|
||||
var container = container.nestedContainer(keyedBy: DynamicCodingKey.self)
|
||||
try Self.encode(components: comps, into: &container, using: strategy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol FamilyDecoding: FamilyRequirementsManaging {
|
||||
static func decode(componentsIn unkeyedContainer: inout UnkeyedDecodingContainer, using strategy: CodingStrategy) throws -> [Components]
|
||||
static func decode(componentsIn container: KeyedDecodingContainer<DynamicCodingKey>, using strategy: CodingStrategy) throws -> Components
|
||||
}
|
||||
|
||||
extension FamilyDecoding {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
public protocol CodingStrategy {
|
||||
func codingKey<C>(for componentType: C.Type) -> DynamicCodingKey where C: Component
|
||||
}
|
||||
|
||||
public struct DynamicCodingKey: CodingKey {
|
||||
public var intValue: Int?
|
||||
public var stringValue: String
|
||||
|
||||
public init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
|
||||
public init?(stringValue: String) { self.stringValue = stringValue }
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
//
|
||||
// FamilyRequirementsManaging.swift
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
||||
public protocol FamilyRequirementsManaging {
|
||||
associatedtype Components
|
||||
associatedtype ComponentTypes
|
||||
associatedtype EntityAndComponents
|
||||
associatedtype RelativesDescending
|
||||
|
||||
init(_ types: ComponentTypes)
|
||||
|
||||
var componentTypes: [Component.Type] { get }
|
||||
|
||||
static func components(nexus: Nexus, entityId: EntityIdentifier) -> Components
|
||||
static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> EntityAndComponents
|
||||
static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) -> RelativesDescending
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Foundation+Extensions.swift
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 22.07.20.
|
||||
//
|
||||
|
||||
#if canImport(Foundation)
|
||||
import class Foundation.JSONEncoder
|
||||
import class Foundation.JSONDecoder
|
||||
|
||||
import class Foundation.PropertyListEncoder
|
||||
import class Foundation.PropertyListDecoder
|
||||
|
||||
extension JSONEncoder: TopLevelEncoder { }
|
||||
extension JSONDecoder: TopLevelDecoder { }
|
||||
|
||||
extension PropertyListEncoder: TopLevelEncoder { }
|
||||
extension PropertyListDecoder: TopLevelDecoder { }
|
||||
#endif
|
||||
|
|
@ -31,6 +31,8 @@ public final class Nexus {
|
|||
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
|
||||
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>]
|
||||
|
||||
public final var codingStrategy: CodingStrategy
|
||||
|
||||
public final weak var delegate: NexusEventDelegate?
|
||||
|
||||
public convenience init() {
|
||||
|
|
@ -39,7 +41,8 @@ public final class Nexus {
|
|||
componentsByEntity: [:],
|
||||
entityIdGenerator: EntityIdentifierGenerator(),
|
||||
familyMembersByTraits: [:],
|
||||
childrenByParentEntity: [:])
|
||||
childrenByParentEntity: [:],
|
||||
codingStrategy: DefaultCodingStrategy())
|
||||
}
|
||||
|
||||
internal init(entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>,
|
||||
|
|
@ -47,13 +50,15 @@ public final class Nexus {
|
|||
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
|
||||
entityIdGenerator: EntityIdentifierGenerator,
|
||||
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>],
|
||||
childrenByParentEntity: [EntityIdentifier: Set<EntityIdentifier>]) {
|
||||
childrenByParentEntity: [EntityIdentifier: Set<EntityIdentifier>],
|
||||
codingStrategy: CodingStrategy) {
|
||||
self.entityStorage = entityStorage
|
||||
self.componentsByType = componentsByType
|
||||
self.componentIdsByEntity = componentsByEntity
|
||||
self.familyMembersByTraits = familyMembersByTraits
|
||||
self.childrenByParentEntity = childrenByParentEntity
|
||||
self.entityIdGenerator = entityIdGenerator
|
||||
self.codingStrategy = codingStrategy
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
|
@ -76,3 +81,12 @@ extension Nexus: CustomDebugStringConvertible {
|
|||
"<Nexus entities:\(numEntities) components:\(numComponents) families:\(numFamilies)>"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - default coding strategy
|
||||
public struct DefaultCodingStrategy: CodingStrategy {
|
||||
public init() { }
|
||||
|
||||
public func codingKey<C>(for componentType: C.Type) -> DynamicCodingKey where C: Component {
|
||||
DynamicCodingKey(stringValue: "\(C.self)")!
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,14 +18,16 @@ class Name: Component {
|
|||
}
|
||||
}
|
||||
|
||||
class Position: Component {
|
||||
final class Position: Component {
|
||||
var x: Int
|
||||
var y: Int
|
||||
|
||||
init(x: Int, y: Int) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
}
|
||||
}
|
||||
extension Position: Codable { }
|
||||
|
||||
class Velocity: Component {
|
||||
var a: Float
|
||||
|
|
@ -34,18 +36,27 @@ class Velocity: Component {
|
|||
}
|
||||
}
|
||||
|
||||
class Party: Component {
|
||||
final class Party: Component {
|
||||
var partying: Bool
|
||||
|
||||
init(partying: Bool) {
|
||||
self.partying = partying
|
||||
}
|
||||
}
|
||||
extension Party: Codable { }
|
||||
|
||||
class Color: Component {
|
||||
var r: UInt8 = 0
|
||||
var g: UInt8 = 0
|
||||
var b: UInt8 = 0
|
||||
final class Color: Component {
|
||||
var r: UInt8
|
||||
var g: UInt8
|
||||
var b: UInt8
|
||||
|
||||
init(r: UInt8 = 0, g: UInt8 = 0, b: UInt8 = 0) {
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
}
|
||||
}
|
||||
extension Color: Codable { }
|
||||
|
||||
class Index: Component {
|
||||
var index: Int
|
||||
|
|
@ -55,6 +66,28 @@ class Index: Component {
|
|||
}
|
||||
}
|
||||
|
||||
final class MyComponent: Component {
|
||||
var name: String
|
||||
var flag: Bool
|
||||
|
||||
init(name: String, flag: Bool) {
|
||||
self.name = name
|
||||
self.flag = flag
|
||||
}
|
||||
}
|
||||
extension MyComponent: Decodable { }
|
||||
extension MyComponent: Encodable { }
|
||||
|
||||
final class YourComponent: Component {
|
||||
var number: Float
|
||||
|
||||
init(number: Float) {
|
||||
self.number = number
|
||||
}
|
||||
}
|
||||
extension YourComponent: Decodable { }
|
||||
extension YourComponent: Encodable { }
|
||||
|
||||
final class SingleGameState: SingleComponent {
|
||||
var shouldQuit: Bool = false
|
||||
var playerHealth: Int = 67
|
||||
|
|
|
|||
|
|
@ -0,0 +1,351 @@
|
|||
//
|
||||
// FamilyCodingTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 22.07.20.
|
||||
//
|
||||
|
||||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
final class FamilyCodingTests: XCTestCase {
|
||||
|
||||
func testEncodingFamily1() throws {
|
||||
let nexus = Nexus()
|
||||
|
||||
let family = nexus.family(requires: MyComponent.self)
|
||||
family.createMember(with: MyComponent(name: "My Name", flag: true))
|
||||
family.createMember(with: MyComponent(name: "Your Name", flag: false))
|
||||
XCTAssertEqual(family.count, 2)
|
||||
|
||||
var jsonEncoder = JSONEncoder()
|
||||
let encodedData = try family.encodeMembers(using: &jsonEncoder)
|
||||
XCTAssertGreaterThanOrEqual(encodedData.count, 90)
|
||||
}
|
||||
|
||||
func testDecodingFamily1() throws {
|
||||
let jsonString = """
|
||||
[
|
||||
{
|
||||
"MyComponent": {
|
||||
"name": "My Name",
|
||||
"flag": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"MyComponent": {
|
||||
"name": "Your Name",
|
||||
"flag": false
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
let jsonData = jsonString.data(using: .utf8)!
|
||||
|
||||
let nexus = Nexus()
|
||||
let family = nexus.family(requires: MyComponent.self)
|
||||
XCTAssertTrue(family.isEmpty)
|
||||
var jsonDecoder = JSONDecoder()
|
||||
let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder)
|
||||
XCTAssertEqual(newEntities.count, 2)
|
||||
XCTAssertEqual(family.count, 2)
|
||||
|
||||
}
|
||||
|
||||
func testEncodeFamily2() throws {
|
||||
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)))
|
||||
XCTAssertEqual(family.count, 2)
|
||||
|
||||
var jsonEncoder = JSONEncoder()
|
||||
let encodedData = try family.encodeMembers(using: &jsonEncoder)
|
||||
XCTAssertGreaterThanOrEqual(encodedData.count, 91)
|
||||
}
|
||||
|
||||
func testDecodingFamily2() throws {
|
||||
let jsonString = """
|
||||
[
|
||||
{
|
||||
"MyComponent": {
|
||||
"name": "My Name",
|
||||
"flag": true
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 2.13
|
||||
}
|
||||
},
|
||||
{
|
||||
"MyComponent": {
|
||||
"name": "Your Name",
|
||||
"flag": false
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 3.1415
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
let jsonData = jsonString.data(using: .utf8)!
|
||||
|
||||
let nexus = Nexus()
|
||||
|
||||
let family = nexus.family(requiresAll: YourComponent.self, MyComponent.self)
|
||||
XCTAssertTrue(family.isEmpty)
|
||||
var jsonDecoder = JSONDecoder()
|
||||
let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder)
|
||||
XCTAssertEqual(newEntities.count, 2)
|
||||
XCTAssertEqual(family.count, 2)
|
||||
}
|
||||
|
||||
func testEncodeFamily3() throws {
|
||||
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)))
|
||||
XCTAssertEqual(family.count, 2)
|
||||
|
||||
var jsonEncoder = JSONEncoder()
|
||||
let encodedData = try family.encodeMembers(using: &jsonEncoder)
|
||||
XCTAssertGreaterThanOrEqual(encodedData.count, 200)
|
||||
}
|
||||
|
||||
func testDecodingFamily3() throws {
|
||||
let jsonString = """
|
||||
[
|
||||
{
|
||||
"MyComponent": {
|
||||
"name": "My Name",
|
||||
"flag": true
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 1.23
|
||||
},
|
||||
"Position": {
|
||||
"x": 1,
|
||||
"y": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"MyComponent": {
|
||||
"name": "Your Name",
|
||||
"flag": false
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 3.45
|
||||
},
|
||||
"Position": {
|
||||
"x": 3,
|
||||
"y": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
let jsonData = jsonString.data(using: .utf8)!
|
||||
|
||||
let nexus = Nexus()
|
||||
|
||||
let family = nexus.family(requiresAll: YourComponent.self, MyComponent.self, Position.self)
|
||||
let family2 = nexus.family(requiresAll: YourComponent.self, MyComponent.self, excludesAll: Index.self)
|
||||
XCTAssertTrue(family.isEmpty)
|
||||
var jsonDecoder = JSONDecoder()
|
||||
let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder)
|
||||
XCTAssertEqual(newEntities.count, 2)
|
||||
XCTAssertEqual(family.count, 2)
|
||||
XCTAssertEqual(family2.count, 2)
|
||||
}
|
||||
|
||||
func testEncodeFamily4() throws {
|
||||
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)))
|
||||
XCTAssertEqual(family.count, 2)
|
||||
|
||||
var jsonEncoder = JSONEncoder()
|
||||
let encodedData = try family.encodeMembers(using: &jsonEncoder)
|
||||
XCTAssertGreaterThanOrEqual(encodedData.count, 250)
|
||||
}
|
||||
|
||||
func testDecodeFamily4() throws {
|
||||
let jsonString = """
|
||||
[
|
||||
{
|
||||
"Color": {
|
||||
"r": 1,
|
||||
"g": 2,
|
||||
"b": 3
|
||||
},
|
||||
"Position": {
|
||||
"x": 1,
|
||||
"y": 2
|
||||
},
|
||||
"MyComponent": {
|
||||
"name": "My Name",
|
||||
"flag": true
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 1.2300000190734863
|
||||
}
|
||||
},
|
||||
{
|
||||
"Color": {
|
||||
"r": 4,
|
||||
"g": 5,
|
||||
"b": 6
|
||||
},
|
||||
"Position": {
|
||||
"x": 3,
|
||||
"y": 4
|
||||
},
|
||||
"MyComponent": {
|
||||
"name": "Your Name",
|
||||
"flag": false
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 3.4500000476837158
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
let jsonData = jsonString.data(using: .utf8)!
|
||||
|
||||
let nexus = Nexus()
|
||||
|
||||
let family = nexus.family(requiresAll: YourComponent.self, MyComponent.self, Position.self, Color.self)
|
||||
XCTAssertTrue(family.isEmpty)
|
||||
var jsonDecoder = JSONDecoder()
|
||||
let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder)
|
||||
XCTAssertEqual(newEntities.count, 2)
|
||||
XCTAssertEqual(family.count, 2)
|
||||
}
|
||||
|
||||
func testEncodeFamily5() throws {
|
||||
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)))
|
||||
XCTAssertEqual(family.count, 2)
|
||||
|
||||
var jsonEncoder = JSONEncoder()
|
||||
let encodedData = try family.encodeMembers(using: &jsonEncoder)
|
||||
XCTAssertGreaterThanOrEqual(encodedData.count, 320)
|
||||
}
|
||||
|
||||
func testDecodeFamily5() throws {
|
||||
let jsonString = """
|
||||
[
|
||||
{
|
||||
"Color": {
|
||||
"r": 1,
|
||||
"g": 2,
|
||||
"b": 3
|
||||
},
|
||||
"Position": {
|
||||
"x": 1,
|
||||
"y": 2
|
||||
},
|
||||
"MyComponent": {
|
||||
"name": "My Name",
|
||||
"flag": true
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 1.23
|
||||
},
|
||||
"Party": {
|
||||
"partying": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Color": {
|
||||
"r": 4,
|
||||
"g": 5,
|
||||
"b": 6
|
||||
},
|
||||
"Position": {
|
||||
"x": 3,
|
||||
"y": 4
|
||||
},
|
||||
"MyComponent": {
|
||||
"name": "Your Name",
|
||||
"flag": false
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 3.45
|
||||
},
|
||||
"Party": {
|
||||
"partying": false
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
let jsonData = jsonString.data(using: .utf8)!
|
||||
|
||||
let nexus = Nexus()
|
||||
|
||||
let family = nexus.family(requiresAll: YourComponent.self, MyComponent.self, Position.self, Color.self, Party.self)
|
||||
XCTAssertTrue(family.isEmpty)
|
||||
var jsonDecoder = JSONDecoder()
|
||||
let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder)
|
||||
XCTAssertEqual(newEntities.count, 2)
|
||||
XCTAssertEqual(family.count, 2)
|
||||
}
|
||||
|
||||
func testFailDecodingFamily() {
|
||||
|
||||
let jsonString = """
|
||||
[
|
||||
{
|
||||
"Color": {
|
||||
"r": 1,
|
||||
"g": 2,
|
||||
"b": 3
|
||||
},
|
||||
"Position": {
|
||||
"x": 1,
|
||||
"y": 2
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 1.23
|
||||
},
|
||||
"Party": {
|
||||
"partying": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Color": {
|
||||
"r": 4,
|
||||
"g": 5,
|
||||
"b": 6
|
||||
},
|
||||
"Position": {
|
||||
"x": 3,
|
||||
"y": 4
|
||||
},
|
||||
"YourComponent": {
|
||||
"number": 3.45
|
||||
},
|
||||
"Party": {
|
||||
"partying": false
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
let jsonData = jsonString.data(using: .utf8)!
|
||||
var jsonDecoder = JSONDecoder()
|
||||
|
||||
let nexus = Nexus()
|
||||
|
||||
let family = nexus.family(requiresAll: YourComponent.self, MyComponent.self)
|
||||
XCTAssertThrowsError(try family.decodeMembers(from: jsonData, using: &jsonDecoder))
|
||||
}
|
||||
}
|
||||
|
|
@ -184,4 +184,37 @@ class FamilyTests: XCTestCase {
|
|||
|
||||
XCTAssertEqual(family.memberIds.count, count + (count / 2))
|
||||
}
|
||||
|
||||
func testFamilyCreateMembers() {
|
||||
let position = Position(x: 0, y: 1)
|
||||
let name = Name(name: "SomeName")
|
||||
let velocity = Velocity(a: 123)
|
||||
let party = Party(partying: true)
|
||||
let color = Color()
|
||||
|
||||
let family1 = nexus.family(requires: Position.self, excludesAll: Name.self)
|
||||
XCTAssertTrue(family1.isEmpty)
|
||||
family1.createMember(with: position)
|
||||
XCTAssertEqual(family1.count, 1)
|
||||
|
||||
let family2 = nexus.family(requiresAll: Position.self, Name.self)
|
||||
XCTAssertTrue(family2.isEmpty)
|
||||
family2.createMember(with: (position, name))
|
||||
XCTAssertEqual(family2.count, 1)
|
||||
|
||||
let family3 = nexus.family(requiresAll: Position.self, Name.self, Velocity.self)
|
||||
XCTAssertTrue(family3.isEmpty)
|
||||
family3.createMember(with: (position, name, velocity))
|
||||
XCTAssertEqual(family3.count, 1)
|
||||
|
||||
let family4 = nexus.family(requiresAll: Position.self, Name.self, Velocity.self, Party.self)
|
||||
XCTAssertTrue(family4.isEmpty)
|
||||
family4.createMember(with: (position, name, velocity, party))
|
||||
XCTAssertEqual(family4.count, 1)
|
||||
|
||||
let family5 = nexus.family(requiresAll: Position.self, Name.self, Velocity.self, Party.self, Color.self)
|
||||
XCTAssertTrue(family5.isEmpty)
|
||||
family5.createMember(with: (position, name, velocity, party, color))
|
||||
XCTAssertEqual(family5.count, 1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,25 @@ extension EntityTests {
|
|||
]
|
||||
}
|
||||
|
||||
extension FamilyCodingTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__FamilyCodingTests = [
|
||||
("testDecodeFamily4", testDecodeFamily4),
|
||||
("testDecodeFamily5", testDecodeFamily5),
|
||||
("testDecodingFamily1", testDecodingFamily1),
|
||||
("testDecodingFamily2", testDecodingFamily2),
|
||||
("testDecodingFamily3", testDecodingFamily3),
|
||||
("testEncodeFamily2", testEncodeFamily2),
|
||||
("testEncodeFamily3", testEncodeFamily3),
|
||||
("testEncodeFamily4", testEncodeFamily4),
|
||||
("testEncodeFamily5", testEncodeFamily5),
|
||||
("testEncodingFamily1", testEncodingFamily1),
|
||||
("testFailDecodingFamily", testFailDecodingFamily)
|
||||
]
|
||||
}
|
||||
|
||||
extension FamilyTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
|
|
@ -41,6 +60,7 @@ extension FamilyTests {
|
|||
static let __allTests__FamilyTests = [
|
||||
("testFamilyAbandoned", testFamilyAbandoned),
|
||||
("testFamilyBulkDestroy", testFamilyBulkDestroy),
|
||||
("testFamilyCreateMembers", testFamilyCreateMembers),
|
||||
("testFamilyCreation", testFamilyCreation),
|
||||
("testFamilyExchange", testFamilyExchange),
|
||||
("testFamilyLateMember", testFamilyLateMember),
|
||||
|
|
@ -143,6 +163,7 @@ public func __allTests() -> [XCTestCaseEntry] {
|
|||
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
||||
testCase(ComponentTests.__allTests__ComponentTests),
|
||||
testCase(EntityTests.__allTests__EntityTests),
|
||||
testCase(FamilyCodingTests.__allTests__FamilyCodingTests),
|
||||
testCase(FamilyTests.__allTests__FamilyTests),
|
||||
testCase(FamilyTraitsTests.__allTests__FamilyTraitsTests),
|
||||
testCase(HashingTests.__allTests__HashingTests),
|
||||
|
|
|
|||
Loading…
Reference in New Issue