From e836884f164de3d730ea8edeb5b7147f644dc58a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 12 Jul 2026 21:28:39 +0100 Subject: [PATCH] Lower EveUnity through the CultMesh client --- .../EveUnityCultMeshConnectionContext.cs | 84 ------- .../EveUnityCultMeshConnectionContext.cs.meta | 2 - .../EveUnityCultMeshLiveProviderTransport.cs | 230 ++++-------------- .../EveUnityCultMeshPlayableWorldProvider.cs | 9 +- .../EveUnityCultMeshProviderDiscovery.cs | 165 +++---------- 5 files changed, 86 insertions(+), 404 deletions(-) delete mode 100644 packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs delete mode 100644 packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs.meta diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs deleted file mode 100644 index d6f7abf..0000000 --- a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using GameCult.Mesh; - -#nullable enable - -namespace GameCult.Eve.UnityScene -{ - public sealed class EveUnityCultMeshConnectionContext : IDisposable - { - private readonly CultMeshDiscoveryService _discovery; - - internal EveUnityCultMeshConnectionContext( - CultMeshEndpointId endpointId, - CultMeshDiscoveryService discovery, - CultMeshSessionManager sessions) - { - EndpointId = endpointId ?? throw new ArgumentNullException(nameof(endpointId)); - _discovery = discovery ?? throw new ArgumentNullException(nameof(discovery)); - Sessions = sessions ?? throw new ArgumentNullException(nameof(sessions)); - } - - public CultMeshEndpointId EndpointId { get; } - public CultMeshSessionManager Sessions { get; } - - public Task OpenDocumentsAsync(CancellationToken cancellationToken = default) => - Sessions.ConnectAsync(EndpointId, CultMeshProtocols.Documents, cancellationToken); - - public void Dispose() - { - Sessions.Dispose(); - _discovery.Dispose(); - } - - internal static EveUnityCultMeshConnectionContext Create( - string endpointId, - IEnumerable routes) - { - var identity = CultMeshEndpointId.Parse(endpointId); - var discovery = new CultMeshDiscoveryService(new[] { new FixedRouteSource(routes) }); - return new EveUnityCultMeshConnectionContext( - identity, - discovery, - new CultMeshSessionManager(discovery, new ICultMeshTransportConnector[] - { - new CultMeshSchemaTransportConnector() - })); - } - - private sealed class FixedRouteSource : ICultMeshLookupSource - { - private readonly CultMeshVerseDescriptor[] _routes; - - public FixedRouteSource(IEnumerable routes) - { - _routes = routes?.ToArray() ?? throw new ArgumentNullException(nameof(routes)); - } - - public string SourceId => "eve-unity-rendezvous"; - - public Task> LookupAsync( - CultMeshDiscoveryQuery query, - CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - var now = DateTimeOffset.UtcNow; - IReadOnlyList observations = _routes - .Where(route => string.Equals(route.VerseId, query.EndpointId, StringComparison.Ordinal)) - .Select(route => new CultMeshDiscoveryObservation( - route, - SourceId, - now, - now.AddMinutes(5), - CultMeshDiscoveryTrust.Unsigned, - "CultMesh rendezvous catalog")) - .ToArray(); - return Task.FromResult(observations); - } - } - } -} diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs.meta b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs.meta deleted file mode 100644 index 7d74b1b..0000000 --- a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshConnectionContext.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e2ece888321b9854fb4424a65448f703 \ No newline at end of file diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshLiveProviderTransport.cs b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshLiveProviderTransport.cs index 0cae7e9..be4862b 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshLiveProviderTransport.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshLiveProviderTransport.cs @@ -34,12 +34,12 @@ namespace GameCult.Eve.UnityScene typeof(CultMeshCdnArtifactChunk) }; - private readonly string _replicaPath; - private readonly string _endpoint; + private readonly EveUnityCultMeshProviderSelection _selection; + private readonly CultMeshClient _mesh; + private readonly string _endpointId; private readonly string _providerId; private readonly string _surfaceId; private readonly string _runtimeId; - private readonly EveUnityCultMeshConnectionContext? _connection; private CultMeshSession? _documentSession; private readonly HashSet _pendingCommandIds = new HashSet(StringComparer.Ordinal); private readonly HashSet _publishedReceiptIds = new HashSet(StringComparer.Ordinal); @@ -48,36 +48,24 @@ namespace GameCult.Eve.UnityScene private readonly List _assetBundles = new List(); private readonly Dictionary _cameraCullingMasks = new Dictionary(StringComparer.Ordinal); private readonly ConcurrentQueue _liveDocuments = new ConcurrentQueue(); - private CultMeshNode? _node; - private CultMeshSnapshotSession? _snapshot; private CultNetDocumentRegistry? _networkRegistry; - private CultNetShardDescriptor? _replicaShard; - private CultNetDatabaseSubscriptionClient? _subscriptions; + private readonly List _liveWatches = new List(); + private bool _liveSubscriptionsOpen; private EveProviderAdvertisementDocument? _advertisement; private EveAdvertisedSurface? _advertisedSurface; public EveUnityCultMeshLiveProviderTransport( string replicaPath, - string endpoint, - string providerId, - string surfaceId, - string runtimeId = "eve-unity", - EveUnityCultMeshConnectionContext? connection = null) + EveUnityCultMeshProviderSelection selection, + string runtimeId = "eve-unity") { - _replicaPath = string.IsNullOrWhiteSpace(replicaPath) - ? throw new ArgumentException("Replica path must be non-empty.", nameof(replicaPath)) - : Path.GetFullPath(replicaPath); - _endpoint = string.IsNullOrWhiteSpace(endpoint) - ? throw new ArgumentException("CultMesh endpoint must be non-empty.", nameof(endpoint)) - : endpoint.Trim(); - _providerId = string.IsNullOrWhiteSpace(providerId) - ? throw new ArgumentException("Provider id must be non-empty.", nameof(providerId)) - : providerId.Trim(); - _surfaceId = string.IsNullOrWhiteSpace(surfaceId) - ? throw new ArgumentException("Surface id must be non-empty.", nameof(surfaceId)) - : surfaceId.Trim(); + _ = replicaPath; + _selection = selection ?? throw new ArgumentNullException(nameof(selection)); + _mesh = selection.Mesh; + _endpointId = selection.EndpointId; + _providerId = selection.ProviderId; + _surfaceId = selection.SurfaceId; _runtimeId = string.IsNullOrWhiteSpace(runtimeId) ? "eve-unity" : runtimeId.Trim(); - _connection = connection; CurrentSurfaceDocument = EmptySurfaceDocument(); CurrentAssetManifestDocument = EmptyAssetManifest(); } @@ -159,10 +147,9 @@ namespace GameCult.Eve.UnityScene SourceRuntimeId = _runtimeId, SourceRole = "eve-unity" }); - new CultNetSchemaWriteForwarder(new CultNetSchemaWriteForwarderOptions()) - .ForwardPutAsync(_replicaShard!, message) - .GetAwaiter() - .GetResult(); + message.ShardId = RemoteShardId; + message.ShardEpoch = 1; + _documentSession!.SendCultNet(message); _pendingCommandIds.Add(commandId); } @@ -172,15 +159,11 @@ namespace GameCult.Eve.UnityScene _assetBundles.Clear(); _prefabs.Clear(); _nativeAssets.Clear(); - _node?.Dispose(); - _snapshot?.Dispose(); - _subscriptions?.Dispose(); - _connection?.Dispose(); - _node = null; - _snapshot = null; - _subscriptions = null; + foreach (var watch in _liveWatches) watch.Dispose(); + _liveWatches.Clear(); + _mesh.Dispose(); + _documentSession = null; _networkRegistry = null; - _replicaShard = null; _pendingCommandIds.Clear(); _publishedReceiptIds.Clear(); } @@ -206,103 +189,24 @@ namespace GameCult.Eve.UnityScene private void EnsureOpen() { - if (_node != null) + if (_documentSession != null) return; var cacheRegistry = CultMesh.CreateCultCacheDocumentRegistry(WireDocumentTypes); _networkRegistry = CultMesh.CreateCultNetDocumentRegistry(WireDocumentTypes, cacheRegistry); - _replicaShard = new CultNetShardDescriptor( - RemoteShardId, - _providerId, - epoch: 1, - isPrimary: false, - schemaIds: WireDocumentTypes.Select(type => cacheRegistry.GetRequired(type).SchemaId), - primaryEndpoints: new[] { _endpoint }); - _node = CultMesh.CreateNodeAsync( - _replicaPath, - new CultMeshNodeOptions - { - StartServer = false, - EnableDurableShardLogs = true, - CacheOptions = new CultCacheOpenOptions - { - Registry = cacheRegistry, - PullOnOpen = false, - StoreFlushOnDispose = true, - UseDirectoryStore = true - }, - DatabaseOptions = new CultNetDatabaseOptions - { - RuntimeId = _runtimeId, - Shards = new[] { _replicaShard }, - DocumentRegistry = _networkRegistry - } - }) - .GetAwaiter() - .GetResult(); - if (_connection != null) - { - _documentSession = _connection.OpenDocumentsAsync().GetAwaiter().GetResult(); - _snapshot = CultMeshSnapshotSession.ConnectAsync( - _connection.Sessions, - _connection.EndpointId, - new CultMeshSnapshotRequestOptions - { - ShardId = RemoteShardId, - ShardEpoch = 1, - ConnectTimeout = TimeSpan.FromSeconds(5), - ResponseTimeout = TimeSpan.FromSeconds(10), - MessageIdPrefix = "eve-unity", - RudpRuntimeId = _runtimeId, - RudpMaxFragmentBytes = 1024 - }, - _networkRegistry) - .GetAwaiter().GetResult(); - return; - } - _snapshot = CultMesh.SnapshotSession( - _endpoint, - new CultMeshSnapshotRequestOptions - { - ShardId = RemoteShardId, - ShardEpoch = 1, - ConnectTimeout = TimeSpan.FromSeconds(5), - ResponseTimeout = TimeSpan.FromSeconds(10), - MessageIdPrefix = "eve-unity", - RudpRuntimeId = _runtimeId, - RudpMaxFragmentBytes = 1024 - }, - _networkRegistry); + _documentSession = _mesh.ConnectAsync(_endpointId, CultMeshProtocols.Documents) + .GetAwaiter().GetResult(); } private void ResolveAdvertisement() { - var documents = _snapshot! - .FetchDocumentsAsync() - .GetAwaiter() - .GetResult(); - _advertisement = documents.FirstOrDefault(document => - string.Equals(document.ProviderId, _providerId, StringComparison.Ordinal)) - ?? throw new InvalidOperationException($"Provider '{_providerId}' did not publish an Eve advertisement."); - _advertisedSurface = _advertisement.Surfaces.FirstOrDefault(surface => - string.Equals(surface.SurfaceId, _surfaceId, StringComparison.Ordinal)) - ?? throw new InvalidOperationException($"Provider '{_providerId}' did not advertise surface '{_surfaceId}'."); - if (!string.Equals(_advertisedSurface.Transport, "cultmesh-record", StringComparison.Ordinal)) - throw new InvalidOperationException("EveUnity CultMesh transport requires a cultmesh-record surface advertisement."); - if (string.IsNullOrWhiteSpace(_advertisedSurface.RecordRef)) - throw new InvalidOperationException("The provider surface advertisement does not publish a record reference."); + _advertisement = _selection.Surface.Provider; + _advertisedSurface = _selection.Surface.Advertisement; } private void RefreshSurface() { - var surface = _snapshot! - .FetchDocumentsAsync( - recordKeys: new[] { _advertisedSurface!.RecordRef }, - schemaIds: new[] { EveSurfaceDocument.SchemaId }) - .GetAwaiter() - .GetResult() - .FirstOrDefault() - ?? throw new InvalidOperationException("The advertised Eve surface record was not available."); + var surface = _selection.Surface.Surface.LatestAsync().GetAwaiter().GetResult(); PublishSurface(surface); } @@ -327,25 +231,9 @@ namespace GameCult.Eve.UnityScene private void EnsureLiveSubscriptions() { - if (_subscriptions != null) return; - var client = _documentSession?.OpenSchemaClient() - ?? CultNetSchemaClients.CreateForEndpoint(_endpoint); - if (_documentSession == null) - { - var endpoint = new Uri(_endpoint); - client.Connect(endpoint.Host, endpoint.Port); - var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(5); - while (!client.Connected && DateTime.UtcNow < deadline) - System.Threading.Thread.Sleep(1); - if (!client.Connected) - { - client.Dispose(); - throw new TimeoutException($"Timed out connecting live Eve state subscription to '{_endpoint}'."); - } - } - - _subscriptions = new CultNetDatabaseSubscriptionClient(client, _node!.Database.Cache, _networkRegistry!); - _subscriptions.Changed += OnReplicatedDocumentChanged; + if (_liveSubscriptionsOpen) return; + _liveSubscriptionsOpen = true; + _liveWatches.Add(_selection.Surface.Surface.Watch(surface => _liveDocuments.Enqueue(surface))); var lowered = new EveUnitySceneSurfaceLowerer() .Lower(CurrentSurfaceDocument.SurfaceDocument, CurrentSurfaceDocument.AdvertisedSurface); string entityViewPointer = lowered.PlayableWorld == null @@ -353,31 +241,16 @@ namespace GameCult.Eve.UnityScene : lowered.PlayableWorld.EntityViewPointerId; if (!string.IsNullOrWhiteSpace(entityViewPointer)) { - _subscriptions.SubscribeAsync( - "eve-unity-entity-view", - recordKeys: new[] { entityViewPointer }, - schemaIds: new[] { EveEntitySoaViewDocument.SchemaId }) + var entities = _mesh.DocumentAsync(_endpointId, entityViewPointer) .GetAwaiter().GetResult(); - if (_node.Database.Cache.Get(new CultRecordKey(entityViewPointer)) is EveEntitySoaViewDocument current) - _liveDocuments.Enqueue(current); + _liveDocuments.Enqueue(entities.LatestAsync().GetAwaiter().GetResult()); + _liveWatches.Add(entities.Watch(view => _liveDocuments.Enqueue(view))); } - _subscriptions.SubscribeAsync( - "eve-unity-surface", - recordKeys: new[] { _advertisedSurface!.RecordRef }, - schemaIds: new[] { EveSurfaceDocument.SchemaId }, - includeSnapshot: false) - .GetAwaiter().GetResult(); - _subscriptions.SubscribeAsync( - "eve-unity-receipts", - schemaIds: new[] { EveCommandReceiptDocument.SchemaId }, - includeSnapshot: false) - .GetAwaiter().GetResult(); - } - - private void OnReplicatedDocumentChanged(CultNetReplicatedDocumentChange change) - { - if (change.Document is EveEntitySoaViewDocument or EveSurfaceDocument or EveCommandReceiptDocument) - _liveDocuments.Enqueue(change.Document); + var receipts = _mesh.CollectionAsync(_endpointId).GetAwaiter().GetResult(); + _liveWatches.Add(receipts.WatchChanges(change => + { + if (change.Document != null) _liveDocuments.Enqueue(change.Document); + })); } private void RefreshAssetCatalog() @@ -386,13 +259,8 @@ namespace GameCult.Eve.UnityScene if (string.IsNullOrWhiteSpace(interaction.AssetManifestRecordRef)) return; - var catalog = _snapshot! - .FetchDocumentsAsync( - recordKeys: new[] { interaction.AssetManifestRecordRef }, - schemaIds: new[] { EveAssetCatalogDocument.SchemaId }) - .GetAwaiter() - .GetResult() - .FirstOrDefault(); + var catalog = _mesh.ReadAsync(_endpointId, interaction.AssetManifestRecordRef) + .GetAwaiter().GetResult(); if (catalog == null || catalog.Version == CurrentAssetCatalogVersion) return; @@ -415,14 +283,8 @@ namespace GameCult.Eve.UnityScene foreach (var group in selected.GroupBy(selection => selection.Variant!.Uri, StringComparer.Ordinal)) { var variant = group.First().Variant!; - var manifest = _snapshot!.FetchDocumentsAsync( - recordKeys: new[] { variant.Uri }, - schemaIds: new[] { CultMeshCdnSchemaVersions.ArtifactManifest }) - .GetAwaiter() - .GetResult(); - var descriptor = manifest.FirstOrDefault(); - if (descriptor == null) - throw new InvalidOperationException($"Provider asset bundle manifest '{variant.Uri}' was not available."); + var descriptor = _mesh.ReadAsync(_endpointId, variant.Uri) + .GetAwaiter().GetResult(); var bytes = ReadCachedBundle(descriptor, variant); VerifyBundle(bytes, variant); var bundle = AssetBundle.LoadFromMemory(bytes); @@ -489,12 +351,10 @@ namespace GameCult.Eve.UnityScene for (var index = 0; index < references.Length; index += 2) { var batch = references.Skip(index).Take(2).ToArray(); - var chunks = _snapshot! - .FetchDocumentsAsync( - recordKeys: batch.Select(reference => reference.RecordKey).ToArray(), - schemaIds: new[] { CultMeshCdnSchemaVersions.ArtifactChunk }) - .GetAwaiter() - .GetResult(); + var chunks = batch.Select(reference => _mesh + .ReadAsync(_endpointId, reference.RecordKey) + .GetAwaiter().GetResult()) + .ToArray(); foreach (var reference in batch) { var chunk = chunks.FirstOrDefault(candidate => diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshPlayableWorldProvider.cs b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshPlayableWorldProvider.cs index 884c818..c606e19 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshPlayableWorldProvider.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshPlayableWorldProvider.cs @@ -126,7 +126,7 @@ namespace GameCult.Eve.UnityScene _transport = null; Selection = null; if (!transportOwnedConnection && _discovery?.Status == TaskStatus.RanToCompletion) - _discovery.Result.Connection.Dispose(); + _discovery.Result.Mesh.Dispose(); _discovery = null; } @@ -185,11 +185,8 @@ namespace GameCult.Eve.UnityScene : replicaPath; _transport = new EveUnityCultMeshLiveProviderTransport( resolvedReplicaPath, - Selection.Endpoint, - Selection.ProviderId, - Selection.SurfaceId, - runtimeId, - Selection.Connection); + Selection, + runtimeId); _transport.EntityViewAvailable += view => _pendingEntityViews.Enqueue(view); _bridge = new EveUnitySceneLiveProviderBridge(_transport); } diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshProviderDiscovery.cs b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshProviderDiscovery.cs index cb6a641..c08809e 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshProviderDiscovery.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshProviderDiscovery.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using GameCult.Eve.Surface; using GameCult.Mesh; -using GameCult.Networking; #nullable enable @@ -13,46 +10,27 @@ namespace GameCult.Eve.UnityScene { public sealed class EveUnityCultMeshProviderSelection { - public EveUnityCultMeshProviderSelection( - string endpoint, - string verseId, - string providerId, - string surfaceId, - string surfaceKind, - EveUnityCultMeshConnectionContext connection) + internal EveUnityCultMeshProviderSelection( + string endpointId, + CultMeshClient mesh, + EveSurfaceSelection surface) { - Endpoint = endpoint ?? ""; - VerseId = verseId ?? ""; - ProviderId = providerId ?? ""; - SurfaceId = surfaceId ?? ""; - SurfaceKind = surfaceKind ?? ""; - Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + EndpointId = endpointId; + Mesh = mesh; + Surface = surface; } - public string Endpoint { get; } - public string VerseId { get; } - public string ProviderId { get; } - public string SurfaceId { get; } - public string SurfaceKind { get; } - public EveUnityCultMeshConnectionContext Connection { get; } + public string EndpointId { get; } + public string VerseId => Surface.Provider.VerseId; + public string ProviderId => Surface.Provider.ProviderId; + public string SurfaceId => Surface.Advertisement.SurfaceId; + public string SurfaceKind => Surface.Advertisement.SurfaceKind; + public CultMeshClient Mesh { get; } + public EveSurfaceSelection Surface { get; } } public sealed class EveUnityCultMeshProviderDiscovery { - private static readonly Type[] AdvertisementDocumentTypes = - { - typeof(EveProviderAdvertisementDocument) - }; - - public EveUnityCultMeshProviderSelection Discover( - string rendezvousEndpoint, - string providerId = "", - string surfaceId = "", - string surfaceKind = "interactive-world", - string verseId = "") - => DiscoverAsync(rendezvousEndpoint, providerId, surfaceId, surfaceKind, verseId) - .GetAwaiter().GetResult(); - public async Task DiscoverAsync( string rendezvousEndpoint, string providerId = "", @@ -63,100 +41,33 @@ namespace GameCult.Eve.UnityScene { if (string.IsNullOrWhiteSpace(rendezvousEndpoint)) throw new ArgumentException("Rendezvous endpoint must be non-empty.", nameof(rendezvousEndpoint)); + var endpointId = !string.IsNullOrWhiteSpace(verseId) ? verseId : providerId; + if (string.IsNullOrWhiteSpace(endpointId)) + throw new ArgumentException("Provider or Verse identity must be supplied for CultMesh discovery.", nameof(providerId)); - var response = await CultMesh.CreateVerseDiscoveryClient().FetchAsync( - rendezvousEndpoint, - new CultMeshVerseCatalogRequestMessage - { - VerseIds = string.IsNullOrWhiteSpace(verseId) ? null : new[] { verseId }, - TransportVersion = "cultmesh.v0" - }).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); - - var candidates = response.Verses - .Where(verse => string.IsNullOrWhiteSpace(verseId) || - string.Equals(verse.VerseId, verseId, StringComparison.Ordinal)) - .SelectMany(verse => (verse.DiscoveryEndpoints ?? Array.Empty()) - .Select(endpoint => new { Verse = verse, Endpoint = endpoint })) - .Where(candidate => !string.IsNullOrWhiteSpace(candidate.Endpoint)) - .ToArray(); - if (candidates.Length == 0) - throw new InvalidOperationException("The rendezvous endpoint advertised no compatible Verse endpoints."); - - var failures = new List(); - foreach (var candidate in candidates) + var mesh = new CultMeshClient(new CultMeshClientOptions { - EveUnityCultMeshConnectionContext? connection = null; - try - { - connection = EveUnityCultMeshConnectionContext.Create( - candidate.Verse.VerseId, - new[] { candidate.Verse.ToVerseDescriptor() }); - var advertisement = (await FetchAdvertisementsAsync(connection, cancellationToken).ConfigureAwait(false)) - .Where(document => string.IsNullOrWhiteSpace(providerId) || - string.Equals(document.ProviderId, providerId, StringComparison.Ordinal)) - .Select(document => new - { - Document = document, - Surface = document.Surfaces.FirstOrDefault(surface => - (string.IsNullOrWhiteSpace(surfaceId) || - string.Equals(surface.SurfaceId, surfaceId, StringComparison.Ordinal)) && - (string.IsNullOrWhiteSpace(surfaceKind) || - string.Equals(surface.SurfaceKind, surfaceKind, StringComparison.Ordinal))) - }) - .FirstOrDefault(match => match.Surface != null); - if (advertisement?.Surface == null) - continue; - - return new EveUnityCultMeshProviderSelection( - candidate.Endpoint, - candidate.Verse.VerseId, - advertisement.Document.ProviderId, - advertisement.Surface.SurfaceId, - advertisement.Surface.SurfaceKind, - connection); - } - catch (Exception error) - { - connection?.Dispose(); - failures.Add($"{candidate.Endpoint}: {error.Message}"); - } - } - - var filter = $"provider='{providerId}', surface='{surfaceId}', kind='{surfaceKind}'"; - var detail = failures.Count == 0 ? "" : $" Endpoint failures: {string.Join(" | ", failures)}"; - throw new InvalidOperationException($"No advertised Eve surface matched {filter}.{detail}"); - } - - private static async Task FetchAdvertisementsAsync( - EveUnityCultMeshConnectionContext connection, - CancellationToken cancellationToken) - { - var cacheRegistry = CultMesh.CreateCultCacheDocumentRegistry(AdvertisementDocumentTypes); - var networkRegistry = CultMesh.CreateCultNetDocumentRegistry(AdvertisementDocumentTypes, cacheRegistry); - using var snapshot = await CultMeshSnapshotSession.ConnectAsync( - connection.Sessions, - connection.EndpointId, - new CultMeshSnapshotEndpointOptions - { - Context = CultMesh.Verse("eve.discovery", "eve-unity").Context, - DocumentRegistry = networkRegistry, - Request = new CultMeshSnapshotRequestOptions + RendezvousEndpoints = new[] { rendezvousEndpoint } + }); + try + { + var surface = await mesh.SurfaceAsync( + endpointId, + new EveSurfaceRequest { - ShardId = "provider", - ShardEpoch = 1, - ConnectTimeout = TimeSpan.FromSeconds(5), - ResponseTimeout = TimeSpan.FromSeconds(10), - MessageIdPrefix = "eve-unity-discovery", - RudpRuntimeId = "eve-unity.discovery", - RudpMaxFragmentBytes = 1024 - } - }.Request, - networkRegistry, - cancellationToken).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); - return (await snapshot.FetchDocumentsAsync() - .ConfigureAwait(false)).ToArray(); + ProviderId = providerId, + SurfaceId = surfaceId, + SurfaceKind = surfaceKind, + LoweringTarget = "unity-scene" + }, + cancellationToken).ConfigureAwait(false); + return new EveUnityCultMeshProviderSelection(endpointId, mesh, surface); + } + catch + { + mesh.Dispose(); + throw; + } } } }