feat(runtime): define borrowed views commands and ordered events
Establish the J1 presentation-independent contract with instance-scoped clocks and generations, immutable borrowed views, typed generation-gated commands, normalized ordered diagnostics, and teardown acknowledgements. Route graphical startup plus press-time selection, movement, and combat through focused App adapters over the exact existing owners without adding a queue or mirrored world. Validated by the Release solution build, 13 Runtime tests, 3,838 App tests with three existing skips, and the complete 8,424-test Release suite with five existing skips. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
afebbe3eca
commit
854d9e9cd1
21 changed files with 2594 additions and 44 deletions
45
src/AcDream.Runtime/GameRuntimeClock.cs
Normal file
45
src/AcDream.Runtime/GameRuntimeClock.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
namespace AcDream.Runtime;
|
||||
|
||||
public readonly record struct RuntimeFrameTime(
|
||||
ulong FrameNumber,
|
||||
double DeltaSeconds,
|
||||
double SimulationTimeSeconds);
|
||||
|
||||
public interface IGameRuntimeClock
|
||||
{
|
||||
ulong FrameNumber { get; }
|
||||
|
||||
double SimulationTimeSeconds { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instance-scoped simulation clock shared by graphical and future headless
|
||||
/// hosts. It owns no wall-clock/global state.
|
||||
/// </summary>
|
||||
public sealed class GameRuntimeClock : IGameRuntimeClock
|
||||
{
|
||||
public ulong FrameNumber { get; private set; }
|
||||
|
||||
public double SimulationTimeSeconds { get; private set; }
|
||||
|
||||
public RuntimeFrameTime Advance(
|
||||
double hostDeltaSeconds,
|
||||
bool advanceSimulationTime = true)
|
||||
{
|
||||
double deltaSeconds = NormalizeDeltaSeconds(hostDeltaSeconds);
|
||||
FrameNumber = checked(FrameNumber + 1UL);
|
||||
if (advanceSimulationTime)
|
||||
SimulationTimeSeconds += deltaSeconds;
|
||||
return new RuntimeFrameTime(
|
||||
FrameNumber,
|
||||
deltaSeconds,
|
||||
SimulationTimeSeconds);
|
||||
}
|
||||
|
||||
public static double NormalizeDeltaSeconds(double deltaSeconds) =>
|
||||
double.IsFinite(deltaSeconds)
|
||||
&& deltaSeconds > 0.0
|
||||
&& deltaSeconds <= float.MaxValue
|
||||
? deltaSeconds
|
||||
: 0.0;
|
||||
}
|
||||
151
src/AcDream.Runtime/GameRuntimeCommands.cs
Normal file
151
src/AcDream.Runtime/GameRuntimeCommands.cs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
namespace AcDream.Runtime;
|
||||
|
||||
public enum RuntimeCommandStatus
|
||||
{
|
||||
Accepted,
|
||||
Inactive,
|
||||
StaleGeneration,
|
||||
Unsupported,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeCommandResult(
|
||||
RuntimeCommandStatus Status,
|
||||
RuntimeGenerationToken Generation,
|
||||
uint ResultObjectId = 0u)
|
||||
{
|
||||
public bool Accepted => Status == RuntimeCommandStatus.Accepted;
|
||||
}
|
||||
|
||||
public enum RuntimeSessionStartStatus
|
||||
{
|
||||
Disabled,
|
||||
MissingCredentials,
|
||||
NoCharacters,
|
||||
Connected,
|
||||
Deferred,
|
||||
Failed,
|
||||
Inactive,
|
||||
StaleGeneration,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeSessionStartResult(
|
||||
RuntimeSessionStartStatus Status,
|
||||
RuntimeGenerationToken Generation,
|
||||
uint CharacterId = 0u,
|
||||
string CharacterName = "",
|
||||
Exception? Error = null);
|
||||
|
||||
public enum RuntimeSelectionCommand
|
||||
{
|
||||
SelectClosestHostile,
|
||||
SelectPrevious,
|
||||
ExamineSelected,
|
||||
UseSelected,
|
||||
PickUpSelected,
|
||||
}
|
||||
|
||||
public enum RuntimeCombatCommand
|
||||
{
|
||||
ToggleMode,
|
||||
}
|
||||
|
||||
public enum RuntimeMovementCommand
|
||||
{
|
||||
ToggleRunLock,
|
||||
Stop,
|
||||
Ready,
|
||||
Sit,
|
||||
Crouch,
|
||||
Sleep,
|
||||
}
|
||||
|
||||
public enum RuntimeChatChannel
|
||||
{
|
||||
Say,
|
||||
Tell,
|
||||
Fellowship,
|
||||
Allegiance,
|
||||
Vassals,
|
||||
Patron,
|
||||
Monarch,
|
||||
CoVassals,
|
||||
General,
|
||||
Trade,
|
||||
LookingForGroup,
|
||||
Roleplay,
|
||||
Society,
|
||||
Olthoi,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeChatCommand(
|
||||
RuntimeChatChannel Channel,
|
||||
string Text,
|
||||
string? TargetName = null);
|
||||
|
||||
public enum RuntimePortalCommand
|
||||
{
|
||||
RecallLifestone,
|
||||
RecallMarketplace,
|
||||
RecallHouse,
|
||||
RecallMansion,
|
||||
}
|
||||
|
||||
public interface IRuntimeSessionCommands
|
||||
{
|
||||
RuntimeSessionStartResult Start(RuntimeGenerationToken expectedGeneration);
|
||||
|
||||
RuntimeSessionStartResult Reconnect(RuntimeGenerationToken expectedGeneration);
|
||||
|
||||
RuntimeTeardownAcknowledgement Stop(RuntimeGenerationToken expectedGeneration);
|
||||
}
|
||||
|
||||
public interface IRuntimeSelectionCommands
|
||||
{
|
||||
RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
RuntimeSelectionCommand command);
|
||||
}
|
||||
|
||||
public interface IRuntimeCombatCommands
|
||||
{
|
||||
RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
RuntimeCombatCommand command);
|
||||
}
|
||||
|
||||
public interface IRuntimeMovementCommands
|
||||
{
|
||||
RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
RuntimeMovementCommand command);
|
||||
}
|
||||
|
||||
public interface IRuntimeChatCommands
|
||||
{
|
||||
RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeChatCommand command);
|
||||
}
|
||||
|
||||
public interface IRuntimePortalCommands
|
||||
{
|
||||
RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
RuntimePortalCommand command);
|
||||
}
|
||||
|
||||
public interface IGameRuntimeCommands
|
||||
{
|
||||
IRuntimeSessionCommands Session { get; }
|
||||
|
||||
IRuntimeSelectionCommands Selection { get; }
|
||||
|
||||
IRuntimeCombatCommands Combat { get; }
|
||||
|
||||
IRuntimeMovementCommands Movement { get; }
|
||||
|
||||
IRuntimeChatCommands Chat { get; }
|
||||
|
||||
IRuntimePortalCommands Portal { get; }
|
||||
}
|
||||
236
src/AcDream.Runtime/GameRuntimeEvents.cs
Normal file
236
src/AcDream.Runtime/GameRuntimeEvents.cs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
namespace AcDream.Runtime;
|
||||
|
||||
public readonly record struct RuntimeEventStamp(
|
||||
RuntimeGenerationToken Generation,
|
||||
ulong Sequence,
|
||||
ulong FrameNumber);
|
||||
|
||||
public enum RuntimeCommandDomain
|
||||
{
|
||||
Session,
|
||||
Selection,
|
||||
Combat,
|
||||
Movement,
|
||||
Chat,
|
||||
Portal,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeLifecycleDelta(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimeLifecycleState Previous,
|
||||
RuntimeLifecycleState Current);
|
||||
|
||||
public readonly record struct RuntimeCommandDelta(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimeCommandDomain Domain,
|
||||
int Operation,
|
||||
RuntimeCommandStatus Status,
|
||||
uint PrimaryObjectId = 0u,
|
||||
string? Text = null);
|
||||
|
||||
public enum RuntimeEntityChange
|
||||
{
|
||||
Registered,
|
||||
Updated,
|
||||
Rebucketed,
|
||||
Hidden,
|
||||
Withdrawn,
|
||||
Deleted,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeEntityDelta(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimeEntityChange Change,
|
||||
RuntimeEntitySnapshot Entity);
|
||||
|
||||
public enum RuntimeInventoryChange
|
||||
{
|
||||
Added,
|
||||
Updated,
|
||||
Moved,
|
||||
Removed,
|
||||
Cleared,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeInventoryDelta(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimeInventoryChange Change,
|
||||
RuntimeInventoryItemSnapshot Item);
|
||||
|
||||
public readonly record struct RuntimeChatEntry(
|
||||
long Revision,
|
||||
uint SenderGuid,
|
||||
int Kind,
|
||||
string Sender,
|
||||
string Text,
|
||||
string ChannelName);
|
||||
|
||||
public readonly record struct RuntimeChatDelta(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimeChatEntry Entry);
|
||||
|
||||
public readonly record struct RuntimeMovementDelta(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimeMovementSnapshot Movement);
|
||||
|
||||
public readonly record struct RuntimePortalDelta(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimePortalSnapshot Portal);
|
||||
|
||||
public interface IRuntimeEventObserver
|
||||
{
|
||||
void OnLifecycle(in RuntimeLifecycleDelta delta);
|
||||
|
||||
void OnCommand(in RuntimeCommandDelta delta);
|
||||
|
||||
void OnEntity(in RuntimeEntityDelta delta);
|
||||
|
||||
void OnInventory(in RuntimeInventoryDelta delta);
|
||||
|
||||
void OnChat(in RuntimeChatDelta delta);
|
||||
|
||||
void OnMovement(in RuntimeMovementDelta delta);
|
||||
|
||||
void OnPortal(in RuntimePortalDelta delta);
|
||||
}
|
||||
|
||||
public interface IRuntimeEventSource
|
||||
{
|
||||
IDisposable Subscribe(IRuntimeEventObserver observer);
|
||||
}
|
||||
|
||||
public enum RuntimeTraceKind
|
||||
{
|
||||
Lifecycle,
|
||||
Command,
|
||||
Entity,
|
||||
Inventory,
|
||||
Chat,
|
||||
Movement,
|
||||
Portal,
|
||||
Checkpoint,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stable normalized trace row used to compare a direct host with an adapter
|
||||
/// host without retaining either owner's object graph.
|
||||
/// </summary>
|
||||
public readonly record struct RuntimeTraceEntry(
|
||||
RuntimeEventStamp Stamp,
|
||||
RuntimeTraceKind Kind,
|
||||
int Code,
|
||||
long Value,
|
||||
uint PrimaryObjectId,
|
||||
uint SecondaryObjectId,
|
||||
string Text);
|
||||
|
||||
public sealed class RuntimeTraceRecorder : IRuntimeEventObserver
|
||||
{
|
||||
private readonly List<RuntimeTraceEntry> _entries = [];
|
||||
|
||||
public IReadOnlyList<RuntimeTraceEntry> Entries => _entries;
|
||||
|
||||
public void AddCheckpoint(
|
||||
RuntimeEventStamp stamp,
|
||||
in RuntimeStateCheckpoint checkpoint)
|
||||
{
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
stamp,
|
||||
RuntimeTraceKind.Checkpoint,
|
||||
(int)checkpoint.Lifecycle,
|
||||
checkpoint.ChatRevision,
|
||||
(uint)checkpoint.EntityCount,
|
||||
(uint)checkpoint.InventoryObjectCount,
|
||||
string.Create(
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
$"frame={checkpoint.FrameNumber};materialized={checkpoint.MaterializedEntityCount};" +
|
||||
$"containers={checkpoint.InventoryContainerCount};chat={checkpoint.ChatCount};" +
|
||||
$"portal={checkpoint.Portal.Generation}:{(int)checkpoint.Portal.Kind}:" +
|
||||
$"{checkpoint.Portal.DestinationCell:X8}:{checkpoint.Portal.IsReady}")));
|
||||
}
|
||||
|
||||
public void OnLifecycle(in RuntimeLifecycleDelta delta) =>
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
delta.Stamp,
|
||||
RuntimeTraceKind.Lifecycle,
|
||||
(int)delta.Current,
|
||||
(int)delta.Previous,
|
||||
0u,
|
||||
0u,
|
||||
string.Empty));
|
||||
|
||||
public void OnCommand(in RuntimeCommandDelta delta) =>
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
delta.Stamp,
|
||||
RuntimeTraceKind.Command,
|
||||
((int)delta.Domain << 16) | (delta.Operation & 0xFFFF),
|
||||
(int)delta.Status,
|
||||
delta.PrimaryObjectId,
|
||||
0u,
|
||||
delta.Text ?? string.Empty));
|
||||
|
||||
public void OnEntity(in RuntimeEntityDelta delta) =>
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
delta.Stamp,
|
||||
RuntimeTraceKind.Entity,
|
||||
(int)delta.Change,
|
||||
delta.Entity.Identity.Incarnation,
|
||||
delta.Entity.Identity.ServerGuid,
|
||||
delta.Entity.CellId,
|
||||
string.Empty));
|
||||
|
||||
public void OnInventory(in RuntimeInventoryDelta delta) =>
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
delta.Stamp,
|
||||
RuntimeTraceKind.Inventory,
|
||||
(int)delta.Change,
|
||||
delta.Item.StackSize,
|
||||
delta.Item.ObjectId,
|
||||
delta.Item.ContainerId,
|
||||
delta.Item.Name ?? string.Empty));
|
||||
|
||||
public void OnChat(in RuntimeChatDelta delta) =>
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
delta.Stamp,
|
||||
RuntimeTraceKind.Chat,
|
||||
delta.Entry.Kind,
|
||||
delta.Entry.Revision,
|
||||
delta.Entry.SenderGuid,
|
||||
0u,
|
||||
$"{delta.Entry.ChannelName}|{delta.Entry.Sender}|{delta.Entry.Text}"));
|
||||
|
||||
public void OnMovement(in RuntimeMovementDelta delta) =>
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
delta.Stamp,
|
||||
RuntimeTraceKind.Movement,
|
||||
delta.Movement.IsAirborne ? 1 : 0,
|
||||
BitConverter.DoubleToInt64Bits(delta.Movement.SimulationTimeSeconds),
|
||||
delta.Movement.LocalEntityId,
|
||||
delta.Movement.Position.ObjCellId,
|
||||
string.Empty));
|
||||
|
||||
public void OnPortal(in RuntimePortalDelta delta) =>
|
||||
_entries.Add(new RuntimeTraceEntry(
|
||||
delta.Stamp,
|
||||
RuntimeTraceKind.Portal,
|
||||
(int)delta.Portal.Kind,
|
||||
delta.Portal.Generation,
|
||||
delta.Portal.DestinationCell,
|
||||
0u,
|
||||
$"{delta.Portal.IsReady}:{delta.Portal.IsMaterialized}:" +
|
||||
$"{delta.Portal.IsCompleted}:{delta.Portal.IsCancelled}:" +
|
||||
$"{delta.Portal.IsWorldVisible}"));
|
||||
}
|
||||
|
||||
/// <summary>Monotonic sequence owner for one runtime instance.</summary>
|
||||
public sealed class RuntimeEventSequencer
|
||||
{
|
||||
private ulong _sequence;
|
||||
|
||||
public RuntimeEventStamp Next(
|
||||
RuntimeGenerationToken generation,
|
||||
ulong frameNumber) =>
|
||||
new(generation, checked(++_sequence), frameNumber);
|
||||
|
||||
public ulong LastSequence => _sequence;
|
||||
}
|
||||
142
src/AcDream.Runtime/GameRuntimeViews.cs
Normal file
142
src/AcDream.Runtime/GameRuntimeViews.cs
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
using AcDream.Core.Physics;
|
||||
|
||||
namespace AcDream.Runtime;
|
||||
|
||||
public readonly record struct RuntimeEntityIdentity(
|
||||
uint ServerGuid,
|
||||
uint LocalEntityId,
|
||||
ushort Incarnation);
|
||||
|
||||
public readonly record struct RuntimeEntitySnapshot(
|
||||
RuntimeEntityIdentity Identity,
|
||||
uint CellId,
|
||||
uint PhysicsState,
|
||||
Position? Position,
|
||||
bool IsMaterialized,
|
||||
bool IsSpatiallyVisible,
|
||||
bool IsHydrated);
|
||||
|
||||
public interface IRuntimeEntityVisitor
|
||||
{
|
||||
void Visit(in RuntimeEntitySnapshot entity);
|
||||
}
|
||||
|
||||
public interface IRuntimeEntityView
|
||||
{
|
||||
int Count { get; }
|
||||
|
||||
int MaterializedCount { get; }
|
||||
|
||||
bool TryGet(uint serverGuid, out RuntimeEntitySnapshot entity);
|
||||
|
||||
/// <summary>
|
||||
/// Visits the canonical owner synchronously without copying its mutable
|
||||
/// collection. The visitor must not retain references or mutate runtime
|
||||
/// ownership during the call.
|
||||
/// </summary>
|
||||
void Visit(IRuntimeEntityVisitor visitor);
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeInventoryItemSnapshot(
|
||||
uint ObjectId,
|
||||
ushort Incarnation,
|
||||
string Name,
|
||||
uint ContainerId,
|
||||
int ContainerSlot,
|
||||
uint WielderId,
|
||||
uint EquipLocation,
|
||||
int StackSize,
|
||||
int Value);
|
||||
|
||||
public interface IRuntimeInventoryVisitor
|
||||
{
|
||||
void Visit(in RuntimeInventoryItemSnapshot item);
|
||||
}
|
||||
|
||||
public interface IRuntimeInventoryView
|
||||
{
|
||||
int ObjectCount { get; }
|
||||
|
||||
int ContainerCount { get; }
|
||||
|
||||
bool TryGet(uint objectId, out RuntimeInventoryItemSnapshot item);
|
||||
|
||||
/// <inheritdoc cref="IRuntimeEntityView.Visit"/>
|
||||
void Visit(IRuntimeInventoryVisitor visitor);
|
||||
}
|
||||
|
||||
public interface IRuntimeChatView
|
||||
{
|
||||
long Revision { get; }
|
||||
|
||||
int Count { get; }
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeMovementSnapshot(
|
||||
bool HasController,
|
||||
uint LocalEntityId,
|
||||
Position Position,
|
||||
System.Numerics.Vector3 Velocity,
|
||||
bool IsAirborne,
|
||||
double SimulationTimeSeconds);
|
||||
|
||||
public interface IRuntimeMovementView
|
||||
{
|
||||
RuntimeMovementSnapshot Snapshot { get; }
|
||||
}
|
||||
|
||||
public enum RuntimePortalKind
|
||||
{
|
||||
None,
|
||||
Login,
|
||||
Portal,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimePortalSnapshot(
|
||||
long Generation,
|
||||
RuntimePortalKind Kind,
|
||||
uint DestinationCell,
|
||||
bool IsReady,
|
||||
bool IsMaterialized,
|
||||
bool IsCompleted,
|
||||
bool IsCancelled,
|
||||
bool IsWorldVisible);
|
||||
|
||||
public interface IRuntimePortalView
|
||||
{
|
||||
RuntimePortalSnapshot Snapshot { get; }
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeStateCheckpoint(
|
||||
RuntimeGenerationToken Generation,
|
||||
RuntimeLifecycleState Lifecycle,
|
||||
ulong FrameNumber,
|
||||
int EntityCount,
|
||||
int MaterializedEntityCount,
|
||||
int InventoryObjectCount,
|
||||
int InventoryContainerCount,
|
||||
long ChatRevision,
|
||||
int ChatCount,
|
||||
RuntimeMovementSnapshot Movement,
|
||||
RuntimePortalSnapshot Portal);
|
||||
|
||||
public interface IGameRuntimeView
|
||||
{
|
||||
RuntimeGenerationToken Generation { get; }
|
||||
|
||||
RuntimeLifecycleSnapshot Lifecycle { get; }
|
||||
|
||||
IGameRuntimeClock Clock { get; }
|
||||
|
||||
IRuntimeEntityView Entities { get; }
|
||||
|
||||
IRuntimeInventoryView Inventory { get; }
|
||||
|
||||
IRuntimeChatView Chat { get; }
|
||||
|
||||
IRuntimeMovementView Movement { get; }
|
||||
|
||||
IRuntimePortalView Portal { get; }
|
||||
|
||||
RuntimeStateCheckpoint CaptureCheckpoint();
|
||||
}
|
||||
62
src/AcDream.Runtime/RuntimeGeneration.cs
Normal file
62
src/AcDream.Runtime/RuntimeGeneration.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
namespace AcDream.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Identifies one exact client-session generation. Default is the
|
||||
/// pre-session generation and remains a valid compare token.
|
||||
/// </summary>
|
||||
public readonly record struct RuntimeGenerationToken(ulong Value)
|
||||
{
|
||||
public static RuntimeGenerationToken Initial => default;
|
||||
|
||||
public RuntimeGenerationToken Next() => new(checked(Value + 1UL));
|
||||
|
||||
public override string ToString() => Value.ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public enum RuntimeLifecycleState
|
||||
{
|
||||
Constructed,
|
||||
Stopped,
|
||||
Starting,
|
||||
InWorld,
|
||||
Stopping,
|
||||
Faulted,
|
||||
Disposed,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeLifecycleSnapshot(
|
||||
RuntimeGenerationToken Generation,
|
||||
RuntimeLifecycleState State,
|
||||
uint PlayerGuid,
|
||||
bool HasTransport);
|
||||
|
||||
[Flags]
|
||||
public enum RuntimeTeardownStage
|
||||
{
|
||||
None = 0,
|
||||
CommandsInert = 1 << 0,
|
||||
InboundDetached = 1 << 1,
|
||||
TransportDisposed = 1 << 2,
|
||||
HostReset = 1 << 3,
|
||||
Complete = CommandsInert | InboundDetached | TransportDisposed | HostReset,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A generation-scoped acknowledgement. A caller may retry only the missing
|
||||
/// suffix; completed teardown stages never replay.
|
||||
/// </summary>
|
||||
public readonly record struct RuntimeTeardownAcknowledgement(
|
||||
RuntimeGenerationToken RetiredGeneration,
|
||||
RuntimeGenerationToken CurrentGeneration,
|
||||
RuntimeCommandStatus Status,
|
||||
RuntimeTeardownStage CompletedStages,
|
||||
Exception? Error = null)
|
||||
{
|
||||
public bool IsComplete =>
|
||||
Status == RuntimeCommandStatus.Accepted
|
||||
&&
|
||||
(CompletedStages & RuntimeTeardownStage.Complete)
|
||||
== RuntimeTeardownStage.Complete
|
||||
&& Error is null;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue