refactor(runtime): acknowledge exact world host projections
This commit is contained in:
parent
73d0b54e38
commit
18d17d8bb1
17 changed files with 1677 additions and 72 deletions
|
|
@ -453,6 +453,8 @@ internal sealed class FrameRootCompositionPhase
|
|||
lifecycleAutomation =
|
||||
new WorldLifecycleAutomationController(
|
||||
() => session.WorldReveal.Snapshot,
|
||||
() => d.WorldEnvironment.Runtime.Ownership,
|
||||
() => live.WorldTransit.Ownership,
|
||||
() => session.WorldReveal.PortalMaterializationCount,
|
||||
resourceSnapshots.Capture,
|
||||
screenshots,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using AcDream.App.Streaming;
|
|||
using AcDream.App.UI.Testing;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.World;
|
||||
|
||||
namespace AcDream.App.Diagnostics;
|
||||
|
||||
|
|
@ -103,6 +104,8 @@ internal sealed record WorldLifecycleCheckpoint(
|
|||
DateTime TimestampUtc,
|
||||
int ProcessId,
|
||||
RuntimePortalSnapshot Reveal,
|
||||
RuntimeWorldEnvironmentOwnershipSnapshot EnvironmentOwnership,
|
||||
RuntimeWorldTransitOwnershipSnapshot TransitOwnership,
|
||||
RenderFrameOutcome Render,
|
||||
WorldLifecycleResourceSnapshot Resources);
|
||||
|
||||
|
|
@ -177,6 +180,10 @@ internal sealed class WorldLifecycleAutomationController :
|
|||
};
|
||||
|
||||
private readonly Func<RuntimePortalSnapshot> _getReveal;
|
||||
private readonly Func<RuntimeWorldEnvironmentOwnershipSnapshot>
|
||||
_getEnvironmentOwnership;
|
||||
private readonly Func<RuntimeWorldTransitOwnershipSnapshot>
|
||||
_getTransitOwnership;
|
||||
private readonly Func<int> _getPortalMaterializationCount;
|
||||
private readonly Func<RenderFrameOutcome, WorldLifecycleResourceSnapshot>
|
||||
_captureResources;
|
||||
|
|
@ -191,6 +198,9 @@ internal sealed class WorldLifecycleAutomationController :
|
|||
|
||||
public WorldLifecycleAutomationController(
|
||||
Func<RuntimePortalSnapshot> getReveal,
|
||||
Func<RuntimeWorldEnvironmentOwnershipSnapshot>
|
||||
getEnvironmentOwnership,
|
||||
Func<RuntimeWorldTransitOwnershipSnapshot> getTransitOwnership,
|
||||
Func<int> getPortalMaterializationCount,
|
||||
Func<RenderFrameOutcome, WorldLifecycleResourceSnapshot> captureResources,
|
||||
FrameScreenshotController screenshots,
|
||||
|
|
@ -198,6 +208,11 @@ internal sealed class WorldLifecycleAutomationController :
|
|||
Action<string>? log = null)
|
||||
{
|
||||
_getReveal = getReveal ?? throw new ArgumentNullException(nameof(getReveal));
|
||||
_getEnvironmentOwnership = getEnvironmentOwnership
|
||||
?? throw new ArgumentNullException(
|
||||
nameof(getEnvironmentOwnership));
|
||||
_getTransitOwnership = getTransitOwnership
|
||||
?? throw new ArgumentNullException(nameof(getTransitOwnership));
|
||||
_getPortalMaterializationCount = getPortalMaterializationCount
|
||||
?? throw new ArgumentNullException(nameof(getPortalMaterializationCount));
|
||||
_captureResources = captureResources ?? throw new ArgumentNullException(nameof(captureResources));
|
||||
|
|
@ -289,6 +304,8 @@ internal sealed class WorldLifecycleAutomationController :
|
|||
TimestampUtc: DateTime.UtcNow,
|
||||
ProcessId: Environment.ProcessId,
|
||||
Reveal: _getReveal(),
|
||||
EnvironmentOwnership: _getEnvironmentOwnership(),
|
||||
TransitOwnership: _getTransitOwnership(),
|
||||
Render: outcome,
|
||||
Resources: _captureResources(outcome));
|
||||
string json = JsonSerializer.Serialize(checkpoint, JsonOptions);
|
||||
|
|
|
|||
|
|
@ -126,7 +126,9 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
_actionView.Snapshot,
|
||||
_movementView.Snapshot,
|
||||
_environmentView.Snapshot,
|
||||
_portalView.Snapshot);
|
||||
_environmentView.Ownership,
|
||||
_portalView.Snapshot,
|
||||
_portalView.Ownership);
|
||||
|
||||
internal void Deactivate() => _active = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ internal sealed class WorldGenerationQuiescence
|
|||
private readonly Func<uint, RuntimeEntityKey?> _resolveProjectionKey;
|
||||
private readonly IWorldAudioQuiescence? _audio;
|
||||
private bool _effectsQuiesced;
|
||||
private bool _selectionEdgeCommitted;
|
||||
private bool _audioSuspended;
|
||||
private bool _audioTransitionActive;
|
||||
|
||||
public WorldGenerationQuiescence(
|
||||
SelectionState selection,
|
||||
|
|
@ -99,26 +102,62 @@ internal sealed class WorldGenerationQuiescence
|
|||
|
||||
public void CommitBegin(in WorldGenerationQuiescenceEdge edge)
|
||||
{
|
||||
if (!edge.ShouldApply || _effectsQuiesced)
|
||||
if (!edge.ShouldApply)
|
||||
return;
|
||||
|
||||
_effectsQuiesced = true;
|
||||
if (edge.ClearWorldSelection)
|
||||
if (!_selectionEdgeCommitted)
|
||||
{
|
||||
_selection.Clear(
|
||||
SelectionChangeSource.System,
|
||||
SelectionChangeReason.Cleared);
|
||||
// SelectionState commits its value before notifying observers.
|
||||
// Record the edge first so a re-entrant reveal cannot replay it.
|
||||
_selectionEdgeCommitted = true;
|
||||
if (edge.ClearWorldSelection)
|
||||
{
|
||||
_selection.Clear(
|
||||
SelectionChangeSource.System,
|
||||
SelectionChangeReason.Cleared);
|
||||
}
|
||||
}
|
||||
|
||||
_audio?.SuspendWorldAudio();
|
||||
ReconcileAudio();
|
||||
}
|
||||
|
||||
public void ObserveReleased()
|
||||
{
|
||||
if (!_effectsQuiesced)
|
||||
if (!_effectsQuiesced
|
||||
&& !_selectionEdgeCommitted
|
||||
&& !_audioSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_effectsQuiesced = false;
|
||||
_audio?.ResumeWorldAudio();
|
||||
ReconcileAudio();
|
||||
if (!_effectsQuiesced && !_audioSuspended)
|
||||
_selectionEdgeCommitted = false;
|
||||
}
|
||||
|
||||
private void ReconcileAudio()
|
||||
{
|
||||
if (_audio is null || _audioTransitionActive)
|
||||
return;
|
||||
|
||||
_audioTransitionActive = true;
|
||||
try
|
||||
{
|
||||
while (_audioSuspended != _effectsQuiesced)
|
||||
{
|
||||
bool suspend = _effectsQuiesced;
|
||||
if (suspend)
|
||||
_audio.SuspendWorldAudio();
|
||||
else
|
||||
_audio.ResumeWorldAudio();
|
||||
_audioSuspended = suspend;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_audioTransitionActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,13 +37,31 @@ internal interface IWorldRevealRenderResourceScheduler
|
|||
/// </summary>
|
||||
internal sealed class WorldRevealCoordinator
|
||||
{
|
||||
private sealed class HostProjection(
|
||||
RuntimeWorldHostProjectionToken token,
|
||||
int requiredRenderRadius,
|
||||
in WorldGenerationQuiescenceEdge quiescenceEdge)
|
||||
{
|
||||
public RuntimeWorldHostProjectionToken Token { get; } = token;
|
||||
public int RequiredRenderRadius { get; } = requiredRenderRadius;
|
||||
public WorldGenerationQuiescenceEdge QuiescenceEdge { get; } =
|
||||
quiescenceEdge;
|
||||
public bool QuiescenceCommitted { get; set; }
|
||||
public bool StreamingRegistered { get; set; }
|
||||
public bool RenderResourcesRegistered { get; set; }
|
||||
public bool SimulationReleaseProjected { get; set; }
|
||||
public bool StreamingReleased { get; set; }
|
||||
public bool RenderResourcesReleased { get; set; }
|
||||
}
|
||||
|
||||
private readonly RuntimeWorldTransitState _transit;
|
||||
private readonly WorldRevealReadinessBarrier _readiness;
|
||||
private readonly WorldGenerationQuiescence? _quiescence;
|
||||
private readonly IWorldRevealStreamingScheduler? _streaming;
|
||||
private readonly IWorldRevealRenderResourceScheduler? _renderResources;
|
||||
private long _reservationGeneration;
|
||||
private bool _worldViewportReleased;
|
||||
private readonly List<HostProjection> _hostProjections = [];
|
||||
private bool _hostRetryActive;
|
||||
private bool _hostRetryRequested;
|
||||
|
||||
public WorldRevealCoordinator(
|
||||
RuntimeWorldTransitState transit,
|
||||
|
|
@ -76,12 +94,15 @@ internal sealed class WorldRevealCoordinator
|
|||
public int PortalMaterializationCount =>
|
||||
_transit.Snapshot.PortalMaterializationCount;
|
||||
public bool WaitCueShown => _transit.Snapshot.WaitCueShown;
|
||||
public RuntimeWorldTransitOwnershipSnapshot Ownership =>
|
||||
_transit.Ownership;
|
||||
|
||||
public long BeginLogin(uint destinationCell)
|
||||
{
|
||||
if (destinationCell == 0u)
|
||||
throw new ArgumentOutOfRangeException(nameof(destinationCell));
|
||||
|
||||
WithdrawHostForReplacement();
|
||||
WorldGenerationQuiescenceEdge quiescenceEdge =
|
||||
_quiescence?.CaptureBegin() ?? default;
|
||||
_readiness.Begin();
|
||||
|
|
@ -100,7 +121,15 @@ internal sealed class WorldRevealCoordinator
|
|||
{
|
||||
if (destinationCell == 0u)
|
||||
throw new ArgumentOutOfRangeException(nameof(destinationCell));
|
||||
if (!_transit.CanBeginPortalReveal(
|
||||
teleportSequence,
|
||||
destinationCell))
|
||||
{
|
||||
generation = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
WithdrawHostForReplacement();
|
||||
WorldGenerationQuiescenceEdge quiescenceEdge =
|
||||
_quiescence?.CaptureBegin() ?? default;
|
||||
if (!_transit.TryBeginPortalReveal(
|
||||
|
|
@ -124,14 +153,21 @@ internal sealed class WorldRevealCoordinator
|
|||
uint destinationCell,
|
||||
in WorldGenerationQuiescenceEdge quiescenceEdge)
|
||||
{
|
||||
_quiescence?.CommitBegin(quiescenceEdge);
|
||||
_reservationGeneration = generation;
|
||||
_worldViewportReleased = false;
|
||||
_streaming?.BeginDestinationReservation(
|
||||
generation,
|
||||
destinationCell,
|
||||
WorldRevealReadinessBarrier.RequiredRenderRadius(destinationCell));
|
||||
_renderResources?.BeginDestinationReveal(generation);
|
||||
if (!_transit.TryRegisterHostProjection(
|
||||
generation,
|
||||
destinationCell,
|
||||
out RuntimeWorldHostProjectionToken token))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Runtime rejected the exact reveal host projection.");
|
||||
}
|
||||
|
||||
_hostProjections.Add(new HostProjection(
|
||||
token,
|
||||
WorldRevealReadinessBarrier.RequiredRenderRadius(
|
||||
destinationCell),
|
||||
quiescenceEdge));
|
||||
RetryPendingHostWork();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -146,6 +182,7 @@ internal sealed class WorldRevealCoordinator
|
|||
|
||||
public WorldRevealReadinessSnapshot Evaluate(uint destinationCell)
|
||||
{
|
||||
RetryPendingHostWork();
|
||||
WorldRevealReadinessSnapshot snapshot = _readiness.Evaluate(destinationCell);
|
||||
RuntimePortalSnapshot portal = _transit.Snapshot;
|
||||
if (portal.Generation != 0)
|
||||
|
|
@ -189,17 +226,17 @@ internal sealed class WorldRevealCoordinator
|
|||
ushort teleportSequence,
|
||||
uint destinationCell)
|
||||
{
|
||||
bool wasAvailable = _transit.IsWorldSimulationAvailable;
|
||||
bool acknowledged = _transit.AcknowledgePortalMaterialized(
|
||||
generation,
|
||||
teleportSequence,
|
||||
destinationCell);
|
||||
ObserveSimulationRelease(wasAvailable);
|
||||
RetryPendingHostWork();
|
||||
return acknowledged;
|
||||
}
|
||||
|
||||
public void ObserveWorldViewportVisible()
|
||||
{
|
||||
RetryPendingHostWork();
|
||||
long generation = _transit.Snapshot.Generation;
|
||||
if (generation != 0)
|
||||
_transit.AcknowledgeWorldViewportVisible(generation);
|
||||
|
|
@ -220,13 +257,12 @@ internal sealed class WorldRevealCoordinator
|
|||
/// </summary>
|
||||
public void RevealWorldViewport()
|
||||
{
|
||||
long generation = _reservationGeneration;
|
||||
if (generation == 0 || _worldViewportReleased)
|
||||
HostProjection? host = FindCurrentHostProjection();
|
||||
if (host is null)
|
||||
return;
|
||||
|
||||
_streaming?.EndDestinationReservation(generation);
|
||||
_renderResources?.EndDestinationReveal(generation);
|
||||
_worldViewportReleased = true;
|
||||
_transit.RequireDestinationReservationRelease(host.Token);
|
||||
RetryPendingHostWork();
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
|
|
@ -235,10 +271,8 @@ internal sealed class WorldRevealCoordinator
|
|||
if (generation == 0)
|
||||
return;
|
||||
|
||||
bool wasAvailable = _transit.IsWorldSimulationAvailable;
|
||||
_transit.Complete(generation);
|
||||
ObserveSimulationRelease(wasAvailable);
|
||||
EndHostLifetime();
|
||||
RetryPendingHostWork();
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
|
|
@ -247,37 +281,284 @@ internal sealed class WorldRevealCoordinator
|
|||
if (generation == 0)
|
||||
return;
|
||||
|
||||
bool wasAvailable = _transit.IsWorldSimulationAvailable;
|
||||
_transit.Cancel(generation);
|
||||
ObserveSimulationRelease(wasAvailable);
|
||||
EndHostLifetime();
|
||||
RetryPendingHostWork();
|
||||
}
|
||||
|
||||
public void ResetSession()
|
||||
{
|
||||
bool wasAvailable = _transit.IsWorldSimulationAvailable;
|
||||
long generation = _transit.Snapshot.Generation;
|
||||
if (generation != 0)
|
||||
_transit.Cancel(generation);
|
||||
ObserveSimulationRelease(wasAvailable);
|
||||
EndHostLifetime();
|
||||
RetryPendingHostWork();
|
||||
_transit.ResetSession();
|
||||
}
|
||||
|
||||
private void EndHostLifetime()
|
||||
/// <summary>
|
||||
/// Resumes the exact unfinished graphical-host suffix. Completed callback
|
||||
/// stages never replay; a thrown callback leaves its Runtime
|
||||
/// acknowledgement pending for the next call.
|
||||
/// </summary>
|
||||
internal void RetryPendingHostWork()
|
||||
{
|
||||
long generation = _reservationGeneration;
|
||||
if (generation == 0)
|
||||
_hostRetryRequested = true;
|
||||
if (_hostRetryActive)
|
||||
return;
|
||||
|
||||
RevealWorldViewport();
|
||||
_reservationGeneration = 0;
|
||||
_worldViewportReleased = false;
|
||||
_hostRetryActive = true;
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
_hostRetryRequested = false;
|
||||
int index = 0;
|
||||
while (index < _hostProjections.Count)
|
||||
{
|
||||
HostProjection host = _hostProjections[index];
|
||||
DrainHostProjection(host);
|
||||
if (index < _hostProjections.Count
|
||||
&& ReferenceEquals(
|
||||
_hostProjections[index],
|
||||
host))
|
||||
{
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (_hostRetryRequested);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_hostRetryActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ObserveSimulationRelease(bool wasAvailable)
|
||||
private void DrainHostProjection(HostProjection host)
|
||||
{
|
||||
if (!wasAvailable && _transit.IsWorldSimulationAvailable)
|
||||
if (!TryGetHostProjection(host, out var projection))
|
||||
return;
|
||||
|
||||
RuntimeWorldHostAcknowledgementStage pending =
|
||||
projection.PendingAcknowledgements;
|
||||
if ((pending & RuntimeWorldHostAcknowledgementStage
|
||||
.ProjectionRegistered) != 0)
|
||||
{
|
||||
CompleteHostRegistration(host);
|
||||
}
|
||||
|
||||
if (!TryGetHostProjection(host, out projection))
|
||||
return;
|
||||
pending = projection.PendingAcknowledgements;
|
||||
if ((pending & RuntimeWorldHostAcknowledgementStage
|
||||
.SimulationReleaseProjected) != 0)
|
||||
{
|
||||
CompleteSimulationRelease(host);
|
||||
}
|
||||
|
||||
if (!TryGetHostProjection(host, out projection))
|
||||
return;
|
||||
pending = projection.PendingAcknowledgements;
|
||||
if ((pending & RuntimeWorldHostAcknowledgementStage
|
||||
.DestinationReservationReleased) != 0)
|
||||
{
|
||||
CompleteDestinationReservationRelease(host);
|
||||
}
|
||||
|
||||
if (!TryGetHostProjection(host, out projection))
|
||||
return;
|
||||
if ((projection.PendingAcknowledgements
|
||||
& RuntimeWorldHostAcknowledgementStage.TerminalProjected)
|
||||
== 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Acknowledge(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage.TerminalProjected);
|
||||
RemoveHostProjection(host);
|
||||
}
|
||||
|
||||
private void CompleteHostRegistration(HostProjection host)
|
||||
{
|
||||
if (!host.QuiescenceCommitted)
|
||||
{
|
||||
_quiescence?.CommitBegin(host.QuiescenceEdge);
|
||||
host.QuiescenceCommitted = true;
|
||||
}
|
||||
if (!IsStagePending(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.ProjectionRegistered))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!host.StreamingRegistered)
|
||||
{
|
||||
_streaming?.BeginDestinationReservation(
|
||||
host.Token.Generation,
|
||||
host.Token.DestinationCell,
|
||||
host.RequiredRenderRadius);
|
||||
host.StreamingRegistered = true;
|
||||
}
|
||||
if (!IsStagePending(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.ProjectionRegistered))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!host.RenderResourcesRegistered)
|
||||
{
|
||||
_renderResources?.BeginDestinationReveal(
|
||||
host.Token.Generation);
|
||||
host.RenderResourcesRegistered = true;
|
||||
}
|
||||
if (!IsStagePending(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.ProjectionRegistered))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Acknowledge(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage.ProjectionRegistered);
|
||||
}
|
||||
|
||||
private void CompleteSimulationRelease(HostProjection host)
|
||||
{
|
||||
if (!host.SimulationReleaseProjected)
|
||||
{
|
||||
_quiescence?.ObserveReleased();
|
||||
host.SimulationReleaseProjected = true;
|
||||
}
|
||||
if (!IsStagePending(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.SimulationReleaseProjected))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Acknowledge(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.SimulationReleaseProjected);
|
||||
}
|
||||
|
||||
private void CompleteDestinationReservationRelease(HostProjection host)
|
||||
{
|
||||
if (!host.StreamingReleased)
|
||||
{
|
||||
if (host.StreamingRegistered)
|
||||
{
|
||||
_streaming?.EndDestinationReservation(
|
||||
host.Token.Generation);
|
||||
}
|
||||
host.StreamingReleased = true;
|
||||
}
|
||||
if (!IsStagePending(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.DestinationReservationReleased))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!host.RenderResourcesReleased)
|
||||
{
|
||||
if (host.RenderResourcesRegistered)
|
||||
{
|
||||
_renderResources?.EndDestinationReveal(
|
||||
host.Token.Generation);
|
||||
}
|
||||
host.RenderResourcesReleased = true;
|
||||
}
|
||||
if (!IsStagePending(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.DestinationReservationReleased))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Acknowledge(
|
||||
host,
|
||||
RuntimeWorldHostAcknowledgementStage
|
||||
.DestinationReservationReleased);
|
||||
}
|
||||
|
||||
private void WithdrawHostForReplacement()
|
||||
{
|
||||
HostProjection? host = FindCurrentHostProjection();
|
||||
if (host is null)
|
||||
return;
|
||||
|
||||
if (!_transit.BeginHostProjectionSupersession(host.Token))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Runtime rejected reveal-host supersession.");
|
||||
}
|
||||
RetryPendingHostWork();
|
||||
}
|
||||
|
||||
private HostProjection? FindCurrentHostProjection()
|
||||
{
|
||||
long generation = _transit.Snapshot.Generation;
|
||||
for (int i = 0; i < _hostProjections.Count; i++)
|
||||
{
|
||||
HostProjection candidate = _hostProjections[i];
|
||||
if (candidate.Token.Generation == generation)
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool TryGetHostProjection(
|
||||
HostProjection host,
|
||||
out RuntimeWorldHostProjectionSnapshot projection)
|
||||
{
|
||||
if (_transit.TryGetHostProjection(host.Token, out projection))
|
||||
return true;
|
||||
|
||||
RemoveHostProjection(host);
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsStagePending(
|
||||
HostProjection host,
|
||||
RuntimeWorldHostAcknowledgementStage stage) =>
|
||||
_transit.TryGetHostProjection(
|
||||
host.Token,
|
||||
out RuntimeWorldHostProjectionSnapshot projection)
|
||||
&& (projection.PendingAcknowledgements & stage) != 0;
|
||||
|
||||
private void RemoveHostProjection(HostProjection host)
|
||||
{
|
||||
for (int i = 0; i < _hostProjections.Count; i++)
|
||||
{
|
||||
if (!ReferenceEquals(_hostProjections[i], host))
|
||||
continue;
|
||||
|
||||
_hostProjections.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void Acknowledge(
|
||||
HostProjection host,
|
||||
RuntimeWorldHostAcknowledgementStage stage)
|
||||
{
|
||||
if (!_transit.AcknowledgeHostProjection(
|
||||
new RuntimeWorldHostAcknowledgement(
|
||||
host.Token,
|
||||
stage)))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Runtime rejected reveal host acknowledgement {stage} "
|
||||
+ $"for generation {host.Token.Generation}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue