66//
77
88import Logging
9+ import PotentCodables
910
1011extension PlaceContents : Equatable
1112{
@@ -25,7 +26,7 @@ extension PlaceContents: Equatable
2526
2627 // Compare each component using the helper method.
2728 for (l, r) in zip ( lhsComponents, rhsComponents) {
28- if ! l. value. isEqualTo ( r. value) {
29+ if l. value != r. value {
2930 return false
3031 }
3132 }
@@ -59,50 +60,64 @@ public final class ComponentRegistry
5960 }
6061}
6162
63+ /// `AnyComponent` lets AlloPlace only store and forward type-erased value trees of Components, while client code can use `decoded()` to receive the real concrete Component type.
6264@MainActor
63- public struct AnyComponent : Component {
64- public static func == ( lhs: AnyComponent , rhs: AnyComponent ) -> Bool {
65- return lhs. base. isEqualTo ( rhs. base)
65+ public struct AnyComponent : Codable , Equatable
66+ {
67+ public static func == ( lhs: AnyComponent , rhs: AnyComponent ) -> Bool
68+ {
69+ return lhs. anyValue == rhs. anyValue
6670 }
6771
68- public var base : any Component
72+ // The concrete Component type we use
73+ public func decoded( ) -> any Component
74+ {
75+ return decodedIfAvailable ( ) !
76+ }
77+ // ... or nil, if the type is not compiled into this binary and registered with the ComponentRegistry.
78+ public func decodedIfAvailable( ) -> ( any Component ) ?
79+ {
80+ guard let type = ComponentRegistry . shared. component ( for: componentTypeId)
81+ else { return nil }
82+ return try ! AnyValueDecoder . default. decode ( type, from: anyValue) // if the type is registered, it should decode
83+ }
84+ public func decodeCustom( ) -> CustomComponent
85+ {
86+ return CustomComponent ( typeId: componentTypeId, fields: anyValue)
87+ }
6988
70- public var componentTypeId : String { type ( of: base) . componentTypeId }
89+ // The type-erased content, available whether the concrete type is available or not
90+ public var anyValue : AnyValue
91+ public var componentTypeId : String
7192
72- public init ( _ base: some Component ) {
73- self . base = base
93+ func indentedDescription( _ prefix: String ) -> String
94+ {
95+ return decodedIfAvailable ( ) ? . indentedDescription ( prefix) ?? " \( prefix) AnyComponent< \( componentTypeId) >: \( anyValue. description) "
7496 }
7597
76- // MARK: - Codable
77- private enum CodingKeys : String , CodingKey {
78- case type
79- case payload
98+ // MARK: Codable
99+ public init ( _ base: some Component )
100+ {
101+ componentTypeId = type ( of: base) . componentTypeId
102+ anyValue = try ! AnyValueEncoder ( ) . encode ( base)
80103 }
81104
105+ // optimization idea: store CBOR treeValue instead of AnyValue to avoid two tree walks
106+ private enum CodingKeys : String , CodingKey
107+ {
108+ case componentTypeId
109+ case value
110+ }
82111 public init ( from decoder: Decoder ) throws {
83- // First, decode the type discriminator.
84112 let container = try decoder. container ( keyedBy: CodingKeys . self)
85- let typeId = try container. decode ( String . self, forKey: . type)
86-
87- // Ask the registry for the correct concrete type.
88- guard let componentType = ComponentRegistry . shared. component ( for: typeId) else {
89- throw DecodingError . dataCorruptedError ( forKey: . type,
90- in: container,
91- debugDescription: " Unknown component type: \( typeId) " )
92- }
93-
94- // Decode the actual component.
95- self . base = try componentType. init ( from: container. superDecoder ( forKey: . payload) )
113+ componentTypeId = try container. decode ( String . self, forKey: . componentTypeId)
114+ anyValue = try container. decode ( AnyValue . self, forKey: . value)
96115 }
97-
116+
98117 public func encode( to encoder: Encoder ) throws {
99- // Create a container for both the type discriminator and the payload.
100118 var container = encoder. container ( keyedBy: CodingKeys . self)
101- // Write out the type identifier. We use the static property from the concrete type.
102- try container. encode ( String ( describing: type ( of: base) ) , forKey: . type)
103-
104- // Encode the underlying component.
105- try base. encode ( to: container. superEncoder ( forKey: . payload) )
119+ try container. encode ( componentTypeId, forKey: . componentTypeId)
120+ try container. encode ( anyValue, forKey: . value)
106121 }
107122}
108123
@@ -134,15 +149,15 @@ extension PlaceChange: Codable
134149 try container. encode ( ChangeKind . componentAdded, forKey: . kind)
135150 try container. encode ( eid, forKey: . entityID)
136151 // Wrap the component so we can encode it generically.
137- try container. encode ( AnyComponent ( component) , forKey: . component)
152+ try container. encode ( component, forKey: . component)
138153 case . componentUpdated( let eid, let component) :
139154 try container. encode ( ChangeKind . componentUpdated, forKey: . kind)
140155 try container. encode ( eid, forKey: . entityID)
141- try container. encode ( AnyComponent ( component) , forKey: . component)
156+ try container. encode ( component, forKey: . component)
142157 case . componentRemoved( let edata, let component) :
143158 try container. encode ( ChangeKind . componentRemoved, forKey: . kind)
144159 try container. encode ( edata, forKey: . entity)
145- try container. encode ( AnyComponent ( component) , forKey: . component)
160+ try container. encode ( component, forKey: . component)
146161 }
147162 }
148163
@@ -161,15 +176,15 @@ extension PlaceChange: Codable
161176 case . componentAdded:
162177 let eid = try container. decode ( EntityID . self, forKey: . entityID)
163178 let anyComp = try container. decode ( AnyComponent . self, forKey: . component)
164- self = . componentAdded( eid, anyComp. base )
179+ self = . componentAdded( eid, anyComp)
165180 case . componentUpdated:
166181 let eid = try container. decode ( EntityID . self, forKey: . entityID)
167182 let anyComp = try container. decode ( AnyComponent . self, forKey: . component)
168- self = . componentUpdated( eid, anyComp. base )
183+ self = . componentUpdated( eid, anyComp)
169184 case . componentRemoved:
170185 let edata = try container. decode ( EntityData . self, forKey: . entity)
171186 let anyComp = try container. decode ( AnyComponent . self, forKey: . component)
172- self = . componentRemoved( edata, anyComp. base )
187+ self = . componentRemoved( edata, anyComp)
173188 }
174189 }
175190}
0 commit comments