From da5bc22e16c1311519e780923f90173d05273d93 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 19 Aug 2026 09:56:36 +0200 Subject: [PATCH] Retry failed Eve presentation candidates --- .../EveUnityCultMeshLiveProviderTransport.cs | 36 ++++++++++++++++--- .../EveUnityCultMeshAssetLookupTests.cs | 24 +++++++++---- .../org.gamecult.eve.unity-scene/package.json | 2 +- 3 files changed, 49 insertions(+), 13 deletions(-) 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 1f38633..34320d7 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshLiveProviderTransport.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshLiveProviderTransport.cs @@ -73,6 +73,8 @@ namespace GameCult.Eve.UnityScene private bool _assetCatalogUpdateRunning; private long _surfaceAssetGeneration; private long _highestObservedBaseSurfaceVersion = -1; + private long _activeBaseSurfaceCandidateVersion = -1; + private long _activeBaseSurfaceCandidateGeneration = -1; private long _mountedBaseSurfaceVersion; private long _presentationBarrierVersion = -1; private long _lastQueuedEntityViewEpoch = -1; @@ -911,7 +913,7 @@ namespace GameCult.Eve.UnityScene private void PublishBaseSurface(EveSurfaceDocument surface) { if (surface == null) throw new ArgumentNullException(nameof(surface)); - if (!TryObserveBaseSurfaceVersion(surface.Version)) + if (!TryBeginBaseSurfaceCandidate(surface.Version, out var generation)) return; CancellationToken candidateToken; lock (_presentationFinalityGate) @@ -921,7 +923,6 @@ namespace GameCult.Eve.UnityScene _surfaceAssetCandidateLifetime = CancellationTokenSource.CreateLinkedTokenSource(_lifetime.Token); candidateToken = _surfaceAssetCandidateLifetime.Token; } - var generation = Interlocked.Increment(ref _surfaceAssetGeneration); if (!_bootstrapped) { _baseSurface = surface; @@ -946,17 +947,36 @@ namespace GameCult.Eve.UnityScene _ = RefreshAssetsForSurfaceAsync(surface, source, generation, candidateToken); } - private bool TryObserveBaseSurfaceVersion(long version) + private bool TryBeginBaseSurfaceCandidate(long version, out long generation) { lock (_presentationFinalityGate) { - if (version <= _highestObservedBaseSurfaceVersion) + generation = -1; + if (version < _highestObservedBaseSurfaceVersion || + version <= _mountedBaseSurfaceVersion || + (version == _highestObservedBaseSurfaceVersion && + _activeBaseSurfaceCandidateVersion == version)) return false; - _highestObservedBaseSurfaceVersion = version; + _highestObservedBaseSurfaceVersion = Math.Max(_highestObservedBaseSurfaceVersion, version); + generation = Interlocked.Increment(ref _surfaceAssetGeneration); + _activeBaseSurfaceCandidateVersion = version; + _activeBaseSurfaceCandidateGeneration = generation; return true; } } + private void RetireFailedBaseSurfaceCandidate(long version, long generation) + { + lock (_presentationFinalityGate) + { + if (_activeBaseSurfaceCandidateVersion != version || + _activeBaseSurfaceCandidateGeneration != generation) + return; + _activeBaseSurfaceCandidateVersion = -1; + _activeBaseSurfaceCandidateGeneration = -1; + } + } + private void PublishEmbeddedSurface(string recordKey, EveSurfaceDocument surface) { if (string.IsNullOrWhiteSpace(recordKey)) @@ -1349,6 +1369,7 @@ namespace GameCult.Eve.UnityScene } catch (Exception error) { + RetireFailedBaseSurfaceCandidate(surface.Version, generation); _liveDocuments.Enqueue(error); } } @@ -2110,6 +2131,11 @@ namespace GameCult.Eve.UnityScene lock (_presentationFinalityGate) { _mountedBaseSurfaceVersion = Math.Max(_mountedBaseSurfaceVersion, committedVersion); + if (_activeBaseSurfaceCandidateVersion == committedVersion) + { + _activeBaseSurfaceCandidateVersion = -1; + _activeBaseSurfaceCandidateGeneration = -1; + } ready = _deferredTerminalReceipts.Values .Where(receipt => receipt.PresentationSurfaceVersion <= _mountedBaseSurfaceVersion) .ToArray(); diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityCultMeshAssetLookupTests.cs b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityCultMeshAssetLookupTests.cs index 0d0d892..40cd2f3 100644 --- a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityCultMeshAssetLookupTests.cs +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityCultMeshAssetLookupTests.cs @@ -357,7 +357,7 @@ namespace GameCult.Eve.UnityScene.Tests presentationSurfaceVersion: presentationSurfaceVersion); [Test] - public void OlderObservedBaseVersionCannotSupersedeANewerCandidate() + public void FailedNewestCandidateCanRetryWithoutAllowingAnOlderSurface() { using var transport = new EveUnityCultMeshLiveProviderTransport( "test-cache", @@ -366,14 +366,24 @@ namespace GameCult.Eve.UnityScene.Tests "test.runtime", "test.provider", "test.surface"); - var observe = typeof(EveUnityCultMeshLiveProviderTransport).GetMethod( - "TryObserveBaseSurfaceVersion", + var begin = typeof(EveUnityCultMeshLiveProviderTransport).GetMethod( + "TryBeginBaseSurfaceCandidate", BindingFlags.NonPublic | BindingFlags.Instance)!; + var retire = typeof(EveUnityCultMeshLiveProviderTransport).GetMethod( + "RetireFailedBaseSurfaceCandidate", + BindingFlags.NonPublic | BindingFlags.Instance)!; + var first = new object[] { 10L, -1L }; + var older = new object[] { 7L, -1L }; + var duplicate = new object[] { 10L, -1L }; - Assert.That(observe.Invoke(transport, new object[] { 10L }), Is.True); - Assert.That(observe.Invoke(transport, new object[] { 7L }), Is.False); - Assert.That(observe.Invoke(transport, new object[] { 10L }), Is.False); - Assert.That(observe.Invoke(transport, new object[] { 11L }), Is.True); + Assert.That(begin.Invoke(transport, first), Is.True); + Assert.That(begin.Invoke(transport, older), Is.False); + Assert.That(begin.Invoke(transport, duplicate), Is.False); + retire.Invoke(transport, new[] { first[0], first[1] }); + var retry = new object[] { 10L, -1L }; + Assert.That(begin.Invoke(transport, retry), Is.True); + Assert.That((long)retry[1], Is.GreaterThan((long)first[1])); + Assert.That(begin.Invoke(transport, new object[] { 9L, -1L }), Is.False); } private static EveUnitySceneProviderSurfaceDocument SceneSurface(long version) diff --git a/packages/org.gamecult.eve.unity-scene/package.json b/packages/org.gamecult.eve.unity-scene/package.json index d4aed2e..12483e3 100644 --- a/packages/org.gamecult.eve.unity-scene/package.json +++ b/packages/org.gamecult.eve.unity-scene/package.json @@ -1,7 +1,7 @@ { "name": "org.gamecult.eve.unity-scene", "displayName": "Eve Unity Scene Lowering", - "version": "0.3.132", + "version": "0.3.133", "unity": "6000.0", "description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.", "dependencies": {