Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/christophhagen/BinaryCodable", from: "3.0.0"),
//.package(url: "https://github.com/apple/swift-protobuf.git", .upToNextMajor(from: "1.25.1")),
.package(url: "https://github.com/outfoxx/PotentCodables.git", from: "3.5.3"),

.package(url: "https://github.com/livekit/webrtc-xcframework.git", exact: "137.7151.07"),
.package(url: "https://github.com/swhitty/FlyingFox.git", .upToNextMajor(from: "0.25.0")),
.package(url: "https://github.com/swhitty/FlyingFoxMacros.git", .upToNextMajor(from: "0.2.0")),

.package(url: "https://github.com/Flight-School/AnyCodable", from: "0.6.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
.package(url: "https://github.com/alloverse/OpenCombine.git", branch: "fix/vision-support"), // So we can use Combine on Linux.
.package(url: "https://github.com/keyvariable/kvSIMD.swift.git", from: "1.1.0"), // So we can use simd on Linux
Expand All @@ -57,8 +56,7 @@ let package = Package(
.target(
name: "allonet2",
dependencies: [
"BinaryCodable",
"AnyCodable",
"PotentCodables",
"FlyingFox",
"FlyingFoxMacros",
"Version",
Expand Down
7 changes: 4 additions & 3 deletions Sources/allonet2/AlloSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
//

import Foundation
import BinaryCodable
import PotentCodables
import PotentCBOR
import OpenCombineShim
import Logging

Expand Down Expand Up @@ -91,7 +92,7 @@ public class AlloSession : NSObject, TransportDelegate
// TODO: this unsafe is going to bite me... store it threadsafely so logging can use it?
nonisolated(unsafe) public var clientId: ClientId? { transport.clientId }

let encoder = BinaryEncoder()
let encoder = CBOREncoder()

public func send(interaction: Interaction)
{
Expand Down Expand Up @@ -200,7 +201,7 @@ public class AlloSession : NSObject, TransportDelegate
}
}

let decoder = BinaryDecoder()
let decoder = CBORDecoder()
nonisolated public func transport(_ transport: Transport, didReceiveData data: Data, on channel: DataChannel)
{
switch channel.alloLabel {
Expand Down
4 changes: 2 additions & 2 deletions Sources/allonet2/Interaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

import Foundation
import AnyCodable
import PotentCodables

@MainActor
public struct Interaction : Codable
Expand Down Expand Up @@ -66,7 +66,7 @@ public enum InteractionBody : Codable
case tap(at: SIMD3<Float>) // oneway

// - Other
case custom(value: [String: AnyCodable])
case custom(value: [String: AnyValue])

// - Generic responses
case error(domain: String, code: Int, description: String)
Expand Down
4 changes: 2 additions & 2 deletions Sources/allonet2/Place.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public struct ComponentSet: CustomStringConvertible
{
public subscript<T>(componentType: T.Type) -> T? where T : Component
{
return state.current.components[componentType.componentTypeId]?[id] as! T?
return state.current.components[componentType.componentTypeId]?[id]?.decoded() as! T?
}
public func set<T>(_ newValue: T) async throws(AlloverseError) where T: Component
{
Expand All @@ -145,7 +145,7 @@ public struct ComponentSet: CustomStringConvertible
}
public subscript(componentTypeID: ComponentTypeID) -> (any Component)?
{
return state.current.components[componentTypeID]?[id]
return state.current.components[componentTypeID]?[id]?.decoded()
}

private let state: PlaceState
Expand Down
10 changes: 5 additions & 5 deletions Sources/allonet2/PlaceContents+Changes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ extension PlaceContents
{
added.append(.componentAdded(entityId, component))
}
else if !component.isEqualTo(prev!)
else if component != prev!
{
updated.append(.componentUpdated(entityId, component))
}
Expand Down Expand Up @@ -135,15 +135,15 @@ extension PlaceContents
case .entityRemoved(let e):
entities[e.id] = nil
case .componentAdded(let eid, let component):
let key = type(of:component).componentTypeId
let key = component.componentTypeId
lists[key, default: [:]][eid] = component
case .componentUpdated(let eid, let component):
let key = type(of:component).componentTypeId
let key = component.componentTypeId
guard let _ = lists[key]?[eid] else { return nil }
lists[key]![eid]! = component
case .componentRemoved(let edata, let component):
guard let _ = lists[type(of:component).componentTypeId] else { return nil }
lists[type(of:component).componentTypeId]![edata.id] = nil
guard let _ = lists[component.componentTypeId] else { return nil }
lists[component.componentTypeId]![edata.id] = nil
}
}
return PlaceContents(revision: changeSet.toRevision, entities: entities, components: ComponentLists(lists: lists), logger: self.logger)
Expand Down
89 changes: 52 additions & 37 deletions Sources/allonet2/PlaceContents+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Logging
import PotentCodables

extension PlaceContents: Equatable
{
Expand All @@ -25,7 +26,7 @@ extension PlaceContents: Equatable

// Compare each component using the helper method.
for (l, r) in zip(lhsComponents, rhsComponents) {
if !l.value.isEqualTo(r.value) {
if l.value != r.value {
return false
}
}
Expand Down Expand Up @@ -59,50 +60,64 @@ public final class ComponentRegistry
}
}

/// `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.
@MainActor
public struct AnyComponent: Component {
public static func == (lhs: AnyComponent, rhs: AnyComponent) -> Bool {
return lhs.base.isEqualTo(rhs.base)
public struct AnyComponent: Codable, Equatable
{
public static func == (lhs: AnyComponent, rhs: AnyComponent) -> Bool
{
return lhs.anyValue == rhs.anyValue
}

public var base: any Component
// The concrete Component type we use
public func decoded() -> any Component
{
return decodedIfAvailable()!
}
// ... or nil, if the type is not compiled into this binary and registered with the ComponentRegistry.
public func decodedIfAvailable() -> (any Component)?
{
guard let type = ComponentRegistry.shared.component(for: componentTypeId)
else { return nil }
return try! AnyValueDecoder.default.decode(type, from: anyValue) // if the type is registered, it should decode
}
public func decodeCustom() -> CustomComponent
{
return CustomComponent(typeId: componentTypeId, fields: anyValue)
}

public var componentTypeId: String { type(of:base).componentTypeId }
// The type-erased content, available whether the concrete type is available or not
public var anyValue: AnyValue
public var componentTypeId: String

public init(_ base: some Component) {
self.base = base
func indentedDescription(_ prefix: String) -> String
{
return decodedIfAvailable()?.indentedDescription(prefix) ?? "\(prefix)AnyComponent<\(componentTypeId)>: \(anyValue.description)"
}

// MARK: - Codable
private enum CodingKeys: String, CodingKey {
case type
case payload
// MARK: Codable
public init(_ base: some Component)
{
componentTypeId = type(of: base).componentTypeId
anyValue = try! AnyValueEncoder().encode(base)
}

// optimization idea: store CBOR treeValue instead of AnyValue to avoid two tree walks
private enum CodingKeys: String, CodingKey
{
case componentTypeId
case value
}
public init(from decoder: Decoder) throws {
// First, decode the type discriminator.
let container = try decoder.container(keyedBy: CodingKeys.self)
let typeId = try container.decode(String.self, forKey: .type)

// Ask the registry for the correct concrete type.
guard let componentType = ComponentRegistry.shared.component(for: typeId) else {
throw DecodingError.dataCorruptedError(forKey: .type,
in: container,
debugDescription: "Unknown component type: \(typeId)")
}

// Decode the actual component.
self.base = try componentType.init(from: container.superDecoder(forKey: .payload))
componentTypeId = try container.decode(String.self, forKey: .componentTypeId)
anyValue = try container.decode(AnyValue.self, forKey: .value)
}

public func encode(to encoder: Encoder) throws {
// Create a container for both the type discriminator and the payload.
var container = encoder.container(keyedBy: CodingKeys.self)
// Write out the type identifier. We use the static property from the concrete type.
try container.encode(String(describing: type(of: base)), forKey: .type)

// Encode the underlying component.
try base.encode(to: container.superEncoder(forKey: .payload))
try container.encode(componentTypeId, forKey: .componentTypeId)
try container.encode(anyValue, forKey: .value)
}
}

Expand Down Expand Up @@ -134,15 +149,15 @@ extension PlaceChange: Codable
try container.encode(ChangeKind.componentAdded, forKey: .kind)
try container.encode(eid, forKey: .entityID)
// Wrap the component so we can encode it generically.
try container.encode(AnyComponent(component), forKey: .component)
try container.encode(component, forKey: .component)
case .componentUpdated(let eid, let component):
try container.encode(ChangeKind.componentUpdated, forKey: .kind)
try container.encode(eid, forKey: .entityID)
try container.encode(AnyComponent(component), forKey: .component)
try container.encode(component, forKey: .component)
case .componentRemoved(let edata, let component):
try container.encode(ChangeKind.componentRemoved, forKey: .kind)
try container.encode(edata, forKey: .entity)
try container.encode(AnyComponent(component), forKey: .component)
try container.encode(component, forKey: .component)
}
}

Expand All @@ -161,15 +176,15 @@ extension PlaceChange: Codable
case .componentAdded:
let eid = try container.decode(EntityID.self, forKey: .entityID)
let anyComp = try container.decode(AnyComponent.self, forKey: .component)
self = .componentAdded(eid, anyComp.base)
self = .componentAdded(eid, anyComp)
case .componentUpdated:
let eid = try container.decode(EntityID.self, forKey: .entityID)
let anyComp = try container.decode(AnyComponent.self, forKey: .component)
self = .componentUpdated(eid, anyComp.base)
self = .componentUpdated(eid, anyComp)
case .componentRemoved:
let edata = try container.decode(EntityData.self, forKey: .entity)
let anyComp = try container.decode(AnyComponent.self, forKey: .component)
self = .componentRemoved(edata, anyComp.base)
self = .componentRemoved(edata, anyComp)
}
}
}
Loading