Retry failed Eve presentation candidates

This commit is contained in:
Meta Vens 2026-08-19 09:56:36 +02:00
commit da5bc22e16
3 changed files with 50 additions and 14 deletions

View file

@ -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();

View file

@ -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)

View file

@ -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": {