This commit is contained in:
Christian Treffs 2017-10-21 23:12:13 +02:00
parent 00bb739880
commit 41745a1fa7
14 changed files with 44 additions and 530 deletions

View File

@ -194,85 +194,3 @@ public extension Entity {
return (a, b, c, d, e, f)
}
}
/*
extension Entity: EventDispatcher {
public func dispatch<E>(_ event: E) where E: Event {
eventDispatcher.dispatch(event)
}
fileprivate func unowned(_ closure: @escaping (Entity) -> Void) {
#if DEBUG
let preActionCount: Int = retainCount(self)
#endif
let unownedClosure = { [unowned self] in
closure(self)
}
unownedClosure()
#if DEBUG
let postActionCount: Int = retainCount(self)
assert(postActionCount == preActionCount, "retain count missmatch [\(preActionCount)] -> [\(postActionCount)]")
#endif
}
private func notifyInit() {
unowned {
$0.dispatch(EntityCreated(entity: $0))
}
}
private func notify<C>(add component: C) {
unowned {
$0.dispatch(ComponentAdded(to: $0))
}
}
private func notify<C>(update newComponent: C, previous previousComponent: C) {
unowned {
$0.dispatch(ComponentUpdated(at: $0))
}
}
private func notify<C>(removed component: C) {
unowned {
$0.dispatch(ComponentRemoved(from: $0))
}
}
private func notify(removed component) {
//unowned { /* this keeps a reference since we need it */
dispatch(ComponentRemoved(from: self))
//}
}
private func notifyDestoryed() {
//unowned {
//$0.dispatch(event: EntityDestroyed(entity: $0))
//TODO: here entity is already dead
//}
}
}
// MARK: - debugging and string representation
extension Entity: CustomStringConvertible {
fileprivate var componentsString: String {
let compTypes: [String] = componentMap.map { String(describing: $0.value.uct.type) }
return compTypes.joined(separator: ",")
}
public var description: String {
return "Entity[\(uei)][\(numComponents):\(componentsString)]"
}
}
extension Entity: CustomPlaygroundQuickLookable {
public var customPlaygroundQuickLook: PlaygroundQuickLook {
return .text(self.description)
}
}
*/

View File

@ -1,76 +0,0 @@
//
// EventHandling.swift
// FirebladeECS
//
// Created by Christian Treffs on 08.10.17.
//
// MARK: - event
public protocol Event: UniqueEventIdentifiable { }
public extension Event {
static var uet: UET { return UET(Self.self) }
var uet: UET { return Self.uet }
}
// MARK: - event handler
public struct EventHandlerRef {
unowned var eventHandlerClassRefUnowned: EventHandler
let eventHandler: (Event) -> Void
}
public protocol EventHandler: class {
func subscribe<E: Event>(event eventHandler: @escaping (E) -> Void)
func unsubscribe<E: Event>(event eventHandler: @escaping (E) -> Void)
weak var delegate: EventHub? { get set }
}
public extension EventHandler {
fileprivate func unowned(_ closure: @escaping (EventHandler) -> Void) {
#if DEBUG
let preActionCount: Int = retainCount(self)
#endif
let unownedClosure = { [unowned self] in
closure(self)
}
unownedClosure()
#if DEBUG
let postActionCount: Int = retainCount(self)
assert(postActionCount == preActionCount, "retain count missmatch [\(preActionCount)] -> [\(postActionCount)]")
#endif
}
/// Subscribe with an event handler closure to receive events of type T
///
/// - Parameter eventHandler: event handler closure
public func subscribe<E: Event>(event eventHandler: @escaping (E) -> Void) {
unowned {
self.delegate?.subscribe(classRef: $0, eventHandler: eventHandler)
}
}
/// Unsubscribe from an event handler closure to stop receiving events of type T
///
/// - Parameter eventHandler: event handler closure
public func unsubscribe<E: Event>(event eventHandler: @escaping (E) -> Void) {
unowned {
self.delegate?.unsubscribe(classRef: $0, eventHandler: eventHandler)
}
}
}
public extension ContiguousArray where Element == EventHandlerRef {
public func index(is eventHandler: EventHandler) -> Int? { // FIXME: this my be costly
return index { (eventHandlerRef: EventHandlerRef) -> Bool in
return eventHandlerRef.eventHandlerClassRefUnowned === eventHandler
}
}
}
// MARK: - event dispatcher
public protocol EventDispatcher: class {
func dispatch<E: Event>(_ event: E)
}

View File

@ -1,80 +0,0 @@
//
// EventHub.swift
// FirebladeECS
//
// Created by Christian Treffs on 09.10.17.
//
public protocol EventHub: class {
func subscribe<E: Event>(classRef eventHandlerRefUnowned: EventHandler, eventHandler: @escaping (E) -> Void)
func unsubscribe<E: Event>(classRef eventHandlerRefUnowned: EventHandler, eventHandler: @escaping (E) -> Void)
weak var sniffer: EventSniffer? { get set }
}
public class DefaultEventHub: EventHub, EventDispatcher {
public var sniffer: EventSniffer?
public init() { }
private var listeners: [UET: ContiguousArray<EventHandlerRef>] = [:]
private func typeEvent<E: Event>(opaqueEvent: Event, eventHandler: @escaping (E) -> Void ) {
guard let typedEvent: E = opaqueEvent as? E else {
fatalError() // TODO: meaningful message
}
eventHandler(typedEvent)
}
private func push(listener newListener: EventHandlerRef, `for` uet: UET) {
if listeners[uet] == nil {
listeners[uet] = []
listeners.reserveCapacity(1)
}
listeners[uet]?.append(newListener)
}
}
// MARK: - event center
extension DefaultEventHub {
final public func subscribe<E: Event>(classRef unownedListenerRef: EventHandler, eventHandler handler: @escaping (E) -> Void) {
let eventListener: EventHandlerRef = EventHandlerRef(eventHandlerClassRefUnowned: unownedListenerRef,
eventHandler: { self.typeEvent(opaqueEvent: $0, eventHandler: handler) })
push(listener: eventListener, for: E.uet)
sniffer?.subscriber(subsribed: E.uet)
}
final public func unsubscribe<E: Event>(classRef listenerRef: EventHandler, eventHandler handler: @escaping (E) -> Void) {
let uet: UET = E.uet
if let removeIdx: Int = listeners[uet]?.index(is: listenerRef) {
let removed = listeners[uet]?.remove(at: removeIdx)
assert(removed != nil)
sniffer?.subscriber(unsubsribed: uet)
}
}
}
// MARK: - event dispatcher delegate
extension DefaultEventHub {
final public func dispatch<E: Event>(_ event: E) {
let uet: UET = E.uet
listeners[uet]?.forEach {
$0.eventHandler(event)
}
sniffer?.dispatched(event: event)
}
}
// MARK: - event dumper
public protocol EventSniffer: class {
func subscriber(subsribed to: UET)
func subscriber(unsubsribed from: UET)
func dispatched<E: Event>(event: E)
}

View File

@ -4,7 +4,7 @@
//
// Created by Christian Treffs on 08.10.17.
//
protocol Event { }
public struct EntityCreated: Event {
let entityId: EntityIdentifier
}

View File

@ -130,7 +130,8 @@ extension Family {
}
}
public func iterate<A, B, C, D, E, F>(_ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?, () -> F?) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component {
public func iterate<A, B, C, D, E, F>(_ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?, () -> F?) -> Void)
where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component {
iterateMembers { (entityId, getEntityInstance) in
func getComponent<Z>() -> Z? where Z: Component {
return self.nexus.get(component: Z.identifier, for: entityId) as? Z

View File

@ -30,6 +30,10 @@ extension Family {
return nexus.isMember(entity, in: self)
}
public final func isMember(_ entityId: EntityIdentifier) -> Bool {
return nexus.isMember(entityId, in: self)
}
internal var members: LazyMapCollection<LazyFilterCollection<LazyMapCollection<EntityIdSet, Entity?>>, Entity> {
return nexus.members(of: self)
}
@ -37,98 +41,3 @@ extension Family {
return nexus.members(of: self)
}
}
// MARK: - member iterator
extension Family {
/*
func makeIterator<A>() -> AnyIterator<(Entity, A)> where A: Component {
var members = self.members.makeIterator()
return AnyIterator<(Entity, A)> { [unowned self] in
let entityId: EntityIdentifier = members.next()!
let entity: Entity = self.nexus.get(entity: entityId)!
let a: A = entity.get()!
return (entity, a)
}
}
func makeIterator<A, B>() -> AnyIterator<(Entity, A, B)> where A: Component, B: Component {
var members = self.members.makeIterator()
return AnyIterator<(Entity, A, B)> { [unowned self] in
let entityId: EntityIdentifier = members.next()!
let entity: Entity = self.nexus.get(entity: entityId)!
let a: A = entity.get()!
let b: B = entity.get()!
return (entity, a, b)
}
}
func makeIterator<A, B, C>() -> AnyIterator<(Entity, A, B, C)> where A: Component, B: Component, C: Component {
var members = self.members.makeIterator()
return AnyIterator<(Entity, A, B, C)> { [unowned self] in
let entityId: EntityIdentifier = members.next()!
let entity: Entity = self.nexus.get(entity: entityId)!
let a: A = entity.get()!
let b: B = entity.get()!
let c: C = entity.get()!
return (entity, a, b, c)
}
}
*/
}
/*
// MARK: - Equatable
extension Family: Equatable {
public static func ==(lhs: Family, rhs: Family) -> Bool {
return lhs.traits == rhs.traits
}
}
// MARK: - Hashable
extension Family: Hashable {
public var hashValue: Int {
return traits.hashValue
}
}
*/
/*
// MARK: - event dispatcher
extension Family: EventDispatcher {
public func dispatch<E>(_ event: E) where E: Event {
dispatcher.dispatch(event)
}
fileprivate func notifyCreated() {
dispatch(FamilyCreated(family: traits))
}
fileprivate func notify(added newEntity: EntityIdentifier) {
dispatch(FamilyMemberAdded(member: newEntity, to: traits))
}
fileprivate func notify(removed removedEntity: EntityIdentifier) {
dispatch(FamilyMemberRemoved(member: removedEntity, from: traits))
}
fileprivate func notifyDestroyed() {
dispatch(FamilyDestroyed(family: traits))
}
}
// MARK: - event handler
extension Family: EventHandler {
fileprivate final func handleComponentAddedToEntity(event: ComponentAdded) {
update(membership: event.to)
}
fileprivate final func handleComponentUpdatedAtEntity(event: ComponentUpdated) {
update(membership: event.at)
}
fileprivate final func handleComponentRemovedFromEntity(event: ComponentRemoved) {
update(membership: event.from)
}
}
*/

View File

@ -70,47 +70,6 @@ extension FamilyTraitSet {
}
extension FamilyTraitSet {
/*/// needs to have all of the given components
fileprivate func matches(all entity: Entity) -> Bool {
var all = requiresAll
while let compId: ComponentIdentifier = all.next() {
guard entity.has(compId) else { return false }
}
return true
}
/// needs to have none of the given (excluded) components
fileprivate func matches(none entity: Entity) -> Bool {
var none = excludesAll
while let compId: ComponentIdentifier = none.next() {
guard !entity.has(compId) else { return false }
}
return true
}
/// needs to have at lease one of the given components
fileprivate func matches(any entity: Entity) -> Bool {
guard !isEmptyAny else { return true }
var any = needsAtLeastOne
while let compId: ComponentIdentifier = any.next() {
if entity.has(compId) {
return true
}
}
return false
}
func isMatch(_ entity: Entity) -> Bool {
guard matches(all: entity) else { return false }
guard matches(none: entity) else { return false }
guard matches(any: entity) else { return false }
return true
}*/
}
// MARK: - Equatable
extension FamilyTraitSet: Equatable {
public static func ==(lhs: FamilyTraitSet, rhs: FamilyTraitSet) -> Bool {

View File

@ -1,49 +0,0 @@
//
// Logging.swift
// FirebladeECS
//
// Created by Christian Treffs on 08.10.17.
//
struct Log {
private init() { }
enum Level {
case info, debug, warn, error
var toString: String {
switch self {
case .info: return "INFO"
case .debug: return "DEBUG"
case .warn: return "WARN"
case .error: return "ERROR"
}
}
}
static func info(_ args: Any?...) { Log.log(level: .info, args: args) }
static func debug(_ args: Any?...) { Log.log(level: .debug, args: args) }
static func warn(_ args: Any?...) { Log.log(level: .warn, args: args) }
static func error(_ args: Any?...) { Log.log(level: .error, args: args) }
private static func log(level: Log.Level, args: [Any?]) {
let entry: String = Log.serialize(level: level, args: args)
Swift.print(entry)
}
private static func serialize(level: Log.Level, args: [Any?]) -> String {
let argStrings: [String] = args.flatMap { arg in
if let arg = arg {
return String(describing: arg)
}
return nil
}
let argString: String = argStrings.joined(separator: " ")
let levelString: String = level.toString
return ["[", levelString, "]\t", argString].joined()
}
}

View File

@ -59,6 +59,12 @@ extension Nexus {
// assign entity / component to index
componentIndexByEntityComponentHash[hash] = newComponentIndex
// FIXME: this is costly for many families
let entityId: EntityIdentifier = entity.identifier
familiyByTraitHash.forEach { (_, family) in
update(membership: family, for: entityId)
}
notify(ComponentAdded(component: componentId, to: entity.identifier))
}
@ -79,7 +85,6 @@ extension Nexus {
}
fileprivate func get<C>(_ hash: EntityComponentHash) -> C? where C: Component {
Log.info("GETTING: \(C.self)")
let componentId: ComponentIdentifier = C.identifier
guard let componentIdx: ComponentIndex = componentIndexByEntityComponentHash[hash] else { return nil }
guard let uniformComponents: UniformComponents = componentsByType[componentId] else { return nil }
@ -136,6 +141,11 @@ extension Nexus {
}
}
// FIXME: this is costly for many families
familiyByTraitHash.forEach { (_, family) in
update(membership: family, for: entityId)
}
notify(ComponentRemoved(component: componentId, from: entityId))
return true
}

View File

@ -43,8 +43,12 @@ extension Nexus {
}
public func isMember(_ entity: Entity, in family: Family) -> Bool {
return isMember(entity.identifier, in: family)
}
public func isMember(_ entityId: EntityIdentifier, in family: Family) -> Bool {
let traitHash: FamilyTraitSetHash = family.traits.hashValue
let entityId = entity.identifier
// FIXME: this may be costly for many entities in family
return familyMembersByTraitHash[traitHash]?.contains(entityId) ?? false
}
@ -58,8 +62,13 @@ extension Nexus {
let family = Family(self, traits: traits)
let replaced = familiyByTraitHash.updateValue(family, forKey: traitHash)
assert(replaced == nil, "Family with exact trait hash already exists: \(traitHash)")
// FIXME: this is costly for many entities
entities.forEach {
update(membership: family, for: $0.identifier)
}
notify(FamilyCreated(family: traits))
// FIXME: update memberships for prior entites
return family
}
@ -67,21 +76,23 @@ extension Nexus {
// MARK: - update family membership
func update(membership family: Family, for entity: Entity) {
let entityIdx: EntityIndex = entity.identifier.index
func update(membership family: Family, for entityId: EntityIdentifier) {
let entityIdx: EntityIndex = entityId.index
guard let componentsSet: ComponentSet = componentIdsSetByEntity[entityIdx] else { return }
let isMember: Bool = family.isMember(entityId)
let isMatch: Bool = family.traits.isMatch(components: componentsSet)
switch isMatch {
case true:
add(to: family, entity: entity)
case false:
remove(from: family, entity: entity)
switch (isMatch, isMember) {
case (true, false):
add(to: family, entityId: entityId)
case (false, true):
remove(from: family, entityId: entityId)
default:
break
}
}
fileprivate func add(to family: Family, entity: Entity) {
fileprivate func add(to family: Family, entityId: EntityIdentifier) {
let traitHash: FamilyTraitSetHash = family.traits.hashValue
let entityId: EntityIdentifier = entity.identifier
if familyMembersByTraitHash[traitHash] != nil {
let (inserted, _) = familyMembersByTraitHash[traitHash]!.insert(entityId)
assert(inserted, "entity with id \(entityId) already in family")
@ -93,9 +104,8 @@ extension Nexus {
notify(FamilyMemberAdded(member: entityId, to: family.traits))
}
fileprivate func remove(from family: Family, entity: Entity) {
fileprivate func remove(from family: Family, entityId: EntityIdentifier) {
let traitHash: FamilyTraitSetHash = family.traits.hashValue
let entityId: EntityIdentifier = entity.identifier
guard let removed = familyMembersByTraitHash[traitHash]?.remove(entityId) else {
assert(false, "removing entity id \(entityId) that is not in family \(family)")

View File

@ -69,32 +69,10 @@ extension Nexus {
func notify(_ event: Event) {
//Log.debug(event)
// TODO: implement
}
func report(_ message: String) {
Log.warn(message)
// TODO: implement
}
}
/*
// MARK: - event handler
extension Nexus {
func handleEntityCreated(_ e: EntityCreated) { print(e) }
func handleEntityDestroyed(_ e: EntityDestroyed) { print(e) }
func handleComponentAdded(_ e: ComponentAdded) { print(e) }
func handleComponentUpdated(_ e: ComponentUpdated) { print(e) }
func handleComponentRemoved(_ e: ComponentRemoved) { print(e) }
func handleFamilyCreated(_ e: FamilyCreated) {
print(e)
let newFamily: Family = e.family
onFamilyCreated(newFamily)
}
func handleFamilyMemberAdded(_ e: FamilyMemberAdded) { print(e) }
func handleFamilyMemberRemoved(_ e: FamilyMemberRemoved) { print(e) }
func handleFamilyDestroyed(_ e: FamilyDestroyed) { print(e) }
}
*/

View File

@ -1,16 +0,0 @@
//
// System.swift
// FirebladeECS
//
// Created by Christian Treffs on 08.10.17.
//
public enum SystemState {
case running, paused, inactive
}
public protocol System: class {
//var state: SystemState { set get }
func startup()
func shutdown()
}

View File

@ -1,41 +0,0 @@
//
// UET.swift
// FirebladeECS
//
// Created by Christian Treffs on 09.10.17.
//
// MARK: Unique Event Type
/// Unique Event Type
public struct UET {
let objectIdentifier: ObjectIdentifier
let type: Event.Type
init(_ eventType: Event.Type) {
objectIdentifier = ObjectIdentifier(eventType)
type = eventType
}
init(_ event: Event) {
let eventType: Event.Type = event.uet.type
self.init(eventType)
}
}
extension UET: Equatable {
public static func ==(lhs: UET, rhs: UET) -> Bool {
return lhs.objectIdentifier == rhs.objectIdentifier
}
}
extension UET: Hashable {
public var hashValue: Int {
return objectIdentifier.hashValue
}
}
// MARK: Unique Event Identifiable
public protocol UniqueEventIdentifiable {
static var uet: UET { get }
var uet: UET { get }
}

View File

@ -67,14 +67,11 @@ class FamilyTests: XCTestCase {
let nexus = Nexus()
let a = nexus.create(entity: "a").assign(Position(x: 1, y: 2), Name(name: "myName"), Velocity(a: 3.14), EmptyComponent())
let b = nexus.create(entity: "b").assign(Position(x: 3, y: 4), Velocity(a: 5.23), EmptyComponent())
nexus.create(entity: "a").assign(Position(x: 1, y: 2), Name(name: "myName"), Velocity(a: 3.14), EmptyComponent())
nexus.create(entity: "b").assign(Position(x: 3, y: 4), Velocity(a: 5.23), EmptyComponent())
let family = nexus.family(requiresAll: [Position.self, Velocity.self], excludesAll: [Party.self], needsAtLeastOne: [Name.self, EmptyComponent.self])
nexus.update(membership: family, for: a)
nexus.update(membership: family, for: b)
var index: Int = 0
family.iterate { (e: () -> Entity, p: () -> Position!, v: () -> Velocity!, n: () -> Name?) in
@ -122,8 +119,6 @@ class FamilyTests: XCTestCase {
let family = nexus.family(requiresAll: [Position.self, Velocity.self], excludesAll: [Party.self], needsAtLeastOne: [Name.self, EmptyComponent.self])
nexus.entities.forEach { nexus.update(membership: family, for: $0) }
XCTAssert(family.members.count == number)
XCTAssert(family.memberIds.count == number)
XCTAssert(nexus.entities.count == number)
@ -152,8 +147,6 @@ class FamilyTests: XCTestCase {
let family = nexus.family(requiresAll: [Position.self, Velocity.self], excludesAll: [Party.self], needsAtLeastOne: [Name.self, EmptyComponent.self])
nexus.entities.forEach { nexus.update(membership: family, for: $0) }
XCTAssert(family.members.count == number)
XCTAssert(family.memberIds.count == number)
XCTAssert(nexus.entities.count == number)
@ -182,8 +175,6 @@ class FamilyTests: XCTestCase {
let family = nexus.family(requiresAll: [Position.self, Velocity.self], excludesAll: [Party.self], needsAtLeastOne: [Name.self, EmptyComponent.self])
nexus.entities.forEach { nexus.update(membership: family, for: $0) }
measure {
family.memberIds.forEach { (e) in
_ = e