// // Nexus+Serialization.swift // // // Created by Christian Treffs on 26.06.20. // #if canImport(Foundation) import struct Foundation.Data extension Nexus { final func serialize() throws -> SNexus { var componentInstances: [ComponentIdentifier.StableId: SComponent] = [:] var entityComponentsMap: [EntityIdentifier: Set] = [:] for entitId in componentIdsByEntity.keys { entityComponentsMap[entitId] = [] let componentIds = self.get(components: entitId) ?? [] for componentId in componentIds { let component = self.get(unsafe: componentId, for: entitId) let componentStableInstanceHash = ComponentIdentifier.makeStableInstanceHash(component: component, entityId: entitId) componentInstances[componentStableInstanceHash] = SComponent.component(component) entityComponentsMap[entitId]?.insert(componentStableInstanceHash) } } return SNexus(version: version, entities: entityComponentsMap, components: componentInstances) } final func deserialize(from sNexus: SNexus, into nexus: Nexus) throws { for freeId in sNexus.entities.map(\.key).reversed() { nexus.entityIdGenerator.markUnused(entityId: freeId) } for componentSet in sNexus.entities.values { let entity = createEntity() for sCompId in componentSet { guard let sComp = sNexus.components[sCompId] else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "Could not find component instance for \(sCompId).")) } switch sComp { case let .component(comp): entity.assign(comp) } } } } } extension EntityIdentifier: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(id) } } extension EntityIdentifier: Decodable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let id = try container.decode(UInt32.self) self.init(id) } } struct SNexus { let version: Version let entities: [EntityIdentifier: Set] let components: [ComponentIdentifier.StableId: SComponent] } extension SNexus: Encodable {} extension SNexus: Decodable {} protocol ComponentEncoding { static func encode(component: Component, to encoder: Encoder) throws } protocol ComponentDecoding { static func decode(from decoder: Decoder) throws -> Component } typealias ComponentCodable = ComponentDecoding & ComponentEncoding extension SNexus: ComponentEncoding { static func encode(component: Component, to encoder: Encoder) throws { var container = encoder.singleValueContainer() let bytes = withUnsafeBytes(of: component) { Data(bytes: $0.baseAddress!, count: MemoryLayout.stride(ofValue: component)) } try container.encode(bytes) } } extension SNexus: ComponentDecoding { static func decode(from decoder: Decoder) throws -> Component { let container = try decoder.singleValueContainer() let instanceData = try container.decode(Data.self) return instanceData.withUnsafeBytes { $0.baseAddress!.load(as: Component.self) } } } enum SComponent { case component(Component) } extension SComponent: Encodable { public func encode(to encoder: Encoder) throws { switch self { case let .component(comp): try CodingStrategy.encode(component: comp, to: encoder) } } } extension SComponent: Decodable { public init(from decoder: Decoder) throws { self = try .component(CodingStrategy.decode(from: decoder)) } } #endif