Stream live Eve surfaces and command receipts

This commit is contained in:
Meta Vens 2026-07-12 13:10:43 +01:00
commit 6253649349
4 changed files with 56 additions and 41 deletions

View file

@ -5,7 +5,7 @@
"unity": "2021.3",
"description": "Unity-compatible DTOs for gamecult.eve.surface.v1 documents.",
"dependencies": {
"org.gamecult.cultlib": "1.0.5"
"org.gamecult.cultlib": "1.0.6"
},
"keywords": [
"Eve",

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Collections.Concurrent;
using GameCult.Caching;
using GameCult.Caching.MessagePack;
using GameCult.Eve.Surface;
@ -44,6 +45,7 @@ namespace GameCult.Eve.UnityScene
private readonly Dictionary<string, UnityEngine.Object> _nativeAssets = new Dictionary<string, UnityEngine.Object>(StringComparer.Ordinal);
private readonly List<AssetBundle> _assetBundles = new List<AssetBundle>();
private readonly Dictionary<string, int> _cameraCullingMasks = new Dictionary<string, int>(StringComparer.Ordinal);
private readonly ConcurrentQueue<object> _liveDocuments = new ConcurrentQueue<object>();
private CultMeshNode? _node;
private CultMeshSnapshotSession? _snapshot;
private CultNetDocumentRegistry? _networkRegistry;
@ -110,7 +112,19 @@ namespace GameCult.Eve.UnityScene
ResolveAdvertisement();
RefreshSurface();
RefreshAssetCatalog();
RefreshReceipts();
}
public void PumpLiveEvents()
{
while (_liveDocuments.TryDequeue(out var document))
{
if (document is EveEntitySoaViewDocument entityView)
EntityViewAvailable?.Invoke(entityView);
else if (document is EveSurfaceDocument surface)
PublishSurface(surface);
else if (document is EveCommandReceiptDocument receipt)
PublishReceipt(receipt);
}
}
public void SubmitCommand(EveSurfaceCommandRequest request)
@ -264,6 +278,11 @@ namespace GameCult.Eve.UnityScene
.GetResult()
.FirstOrDefault()
?? throw new InvalidOperationException("The advertised Eve surface record was not available.");
PublishSurface(surface);
}
private void PublishSurface(EveSurfaceDocument surface)
{
var interaction = RequireWorldInteraction();
CurrentSurfaceDocument = new EveUnitySceneProviderSurfaceDocument(
surface,
@ -278,7 +297,6 @@ namespace GameCult.Eve.UnityScene
_advertisedSurface.RecordRef,
surface.Version);
SurfaceDocumentAvailable?.Invoke(CurrentSurfaceDocument);
AssetManifestDocumentAvailable?.Invoke(CurrentAssetManifestDocument);
EnsureLiveSubscriptions();
}
@ -312,14 +330,25 @@ namespace GameCult.Eve.UnityScene
schemaIds: new[] { EveEntitySoaViewDocument.SchemaId })
.GetAwaiter().GetResult();
if (_node.Database.Cache.Get(new CultRecordKey(entityViewPointer)) is EveEntitySoaViewDocument current)
EntityViewAvailable?.Invoke(current);
_liveDocuments.Enqueue(current);
}
_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 entityView)
EntityViewAvailable?.Invoke(entityView);
if (change.Document is EveEntitySoaViewDocument or EveSurfaceDocument or EveCommandReceiptDocument)
_liveDocuments.Enqueue(change.Document);
}
private void RefreshAssetCatalog()
@ -489,42 +518,27 @@ namespace GameCult.Eve.UnityScene
throw new InvalidOperationException($"Provider asset bundle '{variant.Uri}' hash did not match its catalog.");
}
private void RefreshReceipts()
private void PublishReceipt(EveCommandReceiptDocument receipt)
{
if (_pendingCommandIds.Count == 0)
if (!_pendingCommandIds.Contains(receipt.CommandId) || !_publishedReceiptIds.Add(receipt.ReceiptId))
return;
var receipts = _snapshot!
.FetchDocumentsAsync<EveCommandReceiptDocument>()
.GetAwaiter()
.GetResult();
foreach (var receipt in receipts
.Where(receipt => _pendingCommandIds.Contains(receipt.CommandId))
.OrderBy(receipt => receipt.SourceVersion))
{
if (!_publishedReceiptIds.Add(receipt.ReceiptId))
continue;
CommandReceiptAvailable?.Invoke(new EveUnitySceneCommandReceipt(
receipt.ReceiptId,
receipt.Command,
receipt.CommandId,
receipt.State,
receipt.OwnerRepo,
receipt.Authority,
receipt.Schema,
receipt.ProviderId,
receipt.SurfaceId,
receipt.Message,
DateTimeOffset.TryParse(receipt.IssuedAtUtc, out var issuedAt) ? issuedAt : null,
receipt.SourceVersion));
if (string.Equals(receipt.State, "accepted", StringComparison.OrdinalIgnoreCase) ||
string.Equals(receipt.State, "denied", StringComparison.OrdinalIgnoreCase) ||
string.Equals(receipt.State, "reconciled", StringComparison.OrdinalIgnoreCase))
{
_pendingCommandIds.Remove(receipt.CommandId);
}
}
CommandReceiptAvailable?.Invoke(new EveUnitySceneCommandReceipt(
receipt.ReceiptId,
receipt.Command,
receipt.CommandId,
receipt.State,
receipt.OwnerRepo,
receipt.Authority,
receipt.Schema,
receipt.ProviderId,
receipt.SurfaceId,
receipt.Message,
DateTimeOffset.TryParse(receipt.IssuedAtUtc, out var issuedAt) ? issuedAt : null,
receipt.SourceVersion));
if (string.Equals(receipt.State, "accepted", StringComparison.OrdinalIgnoreCase) ||
string.Equals(receipt.State, "denied", StringComparison.OrdinalIgnoreCase) ||
string.Equals(receipt.State, "reconciled", StringComparison.OrdinalIgnoreCase))
_pendingCommandIds.Remove(receipt.CommandId);
}
private EveWorldInteractionAdvertisement RequireWorldInteraction()

View file

@ -126,6 +126,7 @@ namespace GameCult.Eve.UnityScene
private void Update()
{
_transport?.PumpLiveEvents();
EveEntitySoaViewDocument? latest = null;
while (_pendingEntityViews.TryDequeue(out var next)) latest = next;
if (latest == null) return;

View file

@ -5,7 +5,7 @@
"unity": "2021.3",
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
"dependencies": {
"org.gamecult.cultlib": "1.0.5",
"org.gamecult.cultlib": "1.0.6",
"org.gamecult.eve.plugin-fields": "0.1.1",
"org.gamecult.eve.surface": "0.1.0",
"com.unity.mathematics": "1.3.2"