using System.Collections.Immutable;
using System.Numerics;
using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Runtime.Physics;
namespace AcDream.Runtime.Entities;
internal enum RuntimeInitialCreateExecutionStatus : byte
{
/// Initial tail + entire FIFO revision applied + residence consumed.
Completed,
/// Initial authored placement not yet acknowledged; retry later.
PendingPlacement,
///
/// Yielded mid-drain: a Position continuation began an authored placement
/// that is not yet acknowledged; retry later. Two distinct flavors share
/// this one status (Round 4 R4-14): (1) the ORDINARY flavor, where
///
/// returns the exact token to drive prepare/submit/acknowledge on; and
/// (2) transient operation-slot CONTENTION (Round 3 B1) - another
/// operation already occupies this entity's SetPosition slot at the
/// moment the continuation's own merge committed. In flavor (2),
/// TryGetPendingContinuationPlacement returns false (no
/// token was ever begun) even though the overall status is still
/// AwaitingContinuationPlacement; the caller's only correct action is to
/// retry Execute again later with no placement work of its own -
/// the retry re-attempts ONLY the placement begin against the
/// already-committed merge, never re-running the merge or re-publishing.
///
AwaitingContinuationPlacement,
RejectedToken,
/// Residence retired/superseded - abandoned, ledgers converged.
RejectedAuthority,
}
///
/// Executor-time inputs sampled at the retail decision point. These cannot be
/// retained at admission time because they describe LIVE state (the local
/// player, the current physics simulation) rather than the accepted wire
/// packet itself. Round 3 A3: contact is NOT one of these - it comes solely
/// from the retained wire packet's own IsGrounded bit (PositionPack
/// bit 0x4, server-asserted contact at admission time), never from a live
/// body query or a caller-supplied fallback.
///
internal readonly record struct RuntimeInitialCreateExecutionInputs(
bool UsePositionFromServer,
float PlayerDistance);
internal enum RuntimeInitialCreateExecutedActionKind : byte
{
InitialAdoption,
TeleportHookRequest,
DeferredChildReplay,
/// Round 5 R5-1: one queued accepted parent relation replayed in this parent's own initial tail.
ParentRelationReplay,
PreTailDescriptionAdaptation,
ObjDesc,
CreateParent,
Parent,
Pickup,
Position,
Movement,
State,
Vector,
WeenieDescription,
ResidentCellCleanup,
}
///
/// Round 4 R4-6: outcome of one deferred child's replay registration
/// ().
/// Replaces the previous bool DeferredChildRegistered field, which
/// collapsed a genuine re-defer (the grandparent is ALSO still missing)
/// into the same "true" value as an outright successful registration.
///
internal enum RuntimeDeferredChildReplayOutcome : byte
{
/// was non-null.
Registered,
/// was true - the replayed child itself still has a missing (grand)parent.
ReDeferred,
/// Neither Canonical nor DeferredForParent - registration was rejected outright, or the registration callback threw (Round 4 R4-1).
Rejected,
}
///
/// Round 4 R4-6: distinct outcome for a Parent/CreateParent relation
/// applied at execution time. Replaces the Round 3 B9 dead-letter
/// re-Enqueue with retail-faithful discard (Round 4 R4-5).
///
internal enum RuntimeParentRelationOutcome : byte
{
/// The parent was addressable and current (or, at replay, named the exact live incarnation); the attach commit ran.
Applied,
///
/// Round 5 R5-1: the LIVE parent incarnation is newer than the one this
/// relation named - retail-faithful discard, mirroring
/// 's own "current parent
/// newer than the packet" branch. The already-accepted position-
/// timestamp merge already ran (at the relation's original drain, not
/// repeated here); no leave-world, no placement forget.
///
DiscardedStaleParent,
///
/// Round 5 R5-1: the parent is unaddressable, or (standalone Parent
/// only) names a parent incarnation that has not yet arrived - queued
/// under the parent's guid exactly like retail's QueueBlobForObject
/// (pseudo-C 92326), replayed when that guid is created.
///
DeferredAwaitingParent,
///
/// Round 5 R5-3: the queued relation's child is no longer valid at
/// replay time (not current, or a different incarnation than when
/// queued) - a contained failure, not an exception; recorded and
/// skipped, never resurrected.
///
Rejected,
}
///
/// Retail's exact three-way ResidentCellCleanup disposition (retail-notes.md
/// function 1, SmartBox::HandleCreateObject 0x00454c80, lines ~788-801).
///
internal enum RuntimeResidentCellCleanupDisposition : byte
{
///
/// objcell_id != 0 && cell != 0: already resident -
/// un-mark (RemoveObjectToBeDestroyed).
///
ResidentUnmarked,
///
/// objcell_id != 0 && cell == 0 while an existing
/// lost-cell/deferred SetPosition operation already owns this exact
/// entity: the destruction mark belongs to that existing lifetime, not
/// to this tail action.
///
DeferredUnderLostCellOwnership,
///
/// No cell claimed at all (objcell_id == 0). Retail's own third
/// case (HandleCreateObject, retail-notes.md function 1, lines
/// ~93942-93943) additionally requires NO weenie description before
/// marking for destruction. That second half is structurally
/// UNREACHABLE through this exact envelope path: every
/// SameIncarnationCreate continuation this codebase constructs
/// carries a WeenieDescription action immediately before
/// ResidentCellCleanup, never optionally
/// (,
/// RuntimeInitialCreateResidenceState.cs:277-284, enforces
/// Actions[^2].Kind is WeenieDescription for every admitted
/// envelope). This value records the conservative claimed-but-celless
/// fact for that case without asserting it matches retail's documented
/// no-weenie destruction mark, and without building a second destruction
/// mechanism ahead of the object-table wiring that would let the
/// executor distinguish the two.
///
CelllessNoWeenieMarkUnreachable,
}
///
/// One immutable trace entry. is the owning
/// continuation's FIFO sequence (0 for initial-tail-only facts that precede
/// the FIFO entirely). is the same-incarnation envelope
/// action index, or -1 outside an envelope.
/// and are only meaningful for Position/hook-request
/// entries; only for
/// .
///
internal readonly record struct RuntimeInitialCreateExecutedAction(
RuntimeInitialCreateExecutedActionKind Kind,
ulong Sequence,
int Stage,
RuntimeAuthoritativePositionDisposition? PositionDisposition,
RuntimeTeleportHookPhase HookPhase,
RuntimeDeferredChildReplayOutcome? DeferredChildOutcome = null,
RuntimeResidentCellCleanupDisposition? ResidentCellCleanupDisposition = null,
RuntimePositionConstrainPhase ConstrainPhase = RuntimePositionConstrainPhase.None,
bool StopInterpolating = false,
bool ZeroVelocity = false,
bool PreserveHeading = false,
bool SendPositionImmediately = false,
bool UnparentBeforeRouting = false,
RuntimeParentRelationOutcome? ParentRelationOutcome = null);
///
/// Host-independent immutable execution result. Hosts/tests consume this; the
/// executor never calls presentation.
///
internal readonly record struct RuntimeInitialCreateExecutionReceipt(
RuntimeEntityKey Entity,
uint FullCellId,
RuntimeTeleportHookPhase TeleportHookPhase,
ImmutableArray Trace,
int ReplayedDeferredChildCount);
///
/// C3-1: public projection of . A
/// separate public enum (rather than widening the internal one's
/// accessibility) keeps the classifier/executor's internal vocabulary free to
/// evolve without becoming a host-facing contract; values map 1:1 today.
///
public enum RuntimeInitialCreateTeleportHookPhase : byte
{
None,
BeforePositionOperation,
AfterPositionOperation,
AfterEnterWorld,
}
/// C3-1: public projection of .
public enum RuntimeInitialCreatePositionDisposition : byte
{
RejectedAuthority,
RejectedData,
AwaitFreshPosition,
NoPositionOperation,
Interpolate,
SetPosition,
SetPositionSimple,
}
/// C3-1: public projection of .
public enum RuntimeInitialCreatePositionConstrainPhase : byte
{
None,
BeforePositionOperation,
AfterPositionOperation,
}
///
/// C3-1: one Position continuation's route facts from the executor's trace,
/// projected to a public shape so a host can bind constrain/interpolation
/// presentation (retail's ConstrainTo placement, stop-interpolate,
/// zero-velocity, preserve-heading, send-position-immediately) without
/// reaching into internal Runtime route-classifier types.
/// Review addendum (2026-08-02): the route's UnparentBeforeRouting
/// ("unset_parent") and ApplyPlacementFrameBeforeRouting
/// ("SetPlacementFrame") facts are deliberately NOT projected here - the
/// executor's own merge (ApplyAcceptedPositionSnapshot's
/// clearParent/installPlacementFrame parameters) already
/// applies both directly to the canonical snapshot, synchronously, before
/// the trace entry carrying this fact is even built. A host reading this
/// record must NOT re-apply either one - the facts this type DOES carry
/// (, ,
/// , ,
/// , ) are
/// exactly the bindings still DEFERRED to the host at presentation time;
/// everything already-applied is intentionally excluded.
///
public readonly record struct RuntimeInitialCreatePositionRouteFact(
ulong Sequence,
RuntimeInitialCreatePositionDisposition Disposition,
RuntimeInitialCreateTeleportHookPhase HookPhase,
RuntimeInitialCreatePositionConstrainPhase ConstrainPhase,
bool StopInterpolating,
bool ZeroVelocity,
bool PreserveHeading,
bool SendPositionImmediately);
///
/// C3-1: the public host consumption shape for one executor drain's
/// completion, reached via
///
/// using the correlated
/// receipt's own Entity/Sequence identity. Built exactly once, at completion
/// time, and cached alongside the internal receipt it projects (see
/// ) -
/// a host retrying TryGetInitialCreateCompletion across multiple polls
/// never triggers a second allocation.
/// Review addendum (2026-08-02): 's ARRAY
/// ORDER is the authoritative ordering, not
/// alone - Position facts drained from the SAME same-incarnation envelope
/// ( distinguishes
/// them internally, a field this projection does not carry) share one
/// continuation Sequence, so two entries can legitimately have equal
/// Sequence values.
/// walks the executor's trace strictly in construction order (itself the
/// exact FIFO drain order) and appends without reordering or deduplicating,
/// so array index - never a sort or group-by on Sequence - is the
/// only reliable way to recover drain order from this array.
///
public readonly record struct RuntimeInitialCreatePlacementCompletion(
RuntimeEntityKey Entity,
uint FullCellId,
RuntimeInitialCreateTeleportHookPhase TeleportHookPhase,
ImmutableArray PositionRouteFacts,
int ReplayedDeferredChildCount);
///
/// Applies one entity's completed initial-Create residence: adopts the
/// initial placement exactly once, emits the AfterEnterWorld teleport-hook
/// request, replays raw missing-parent child Creates in FIFO order, drains
/// every retained continuation strictly by sequence (classifying Position
/// continuations at execution time against LIVE inputs), and releases the
/// residence once the drained prefix matches the lease's current length.
/// Execute is synchronous and retry-idempotent: a caller re-invokes it
/// after or
///
/// once the placement token in the returned trace has been prepared,
/// submitted, and acknowledged by whatever drives
/// (a test harness today; a host at
/// cutover). This type never references App/UI/Silk.NET/OpenGL/OpenAL/
/// Headless and is reached only by tests in this slice - no production
/// caller exists yet.
///
internal sealed class RuntimeInitialCreateContinuationExecutor
{
private enum InitialTailPhase : byte
{
NotStarted,
Adopted,
HookRecorded,
DeferredReplayed,
/// Round 5 R5-1: the accepted-relation queue for this guid has been drained.
RelationsReplayed,
}
private readonly record struct PendingPublish(
RuntimeEntityChange Change,
Func Matches,
RuntimePlacementCancellationReceipt Cancellation);
private sealed class Progress
{
internal required ulong LeaseId { get; init; }
internal ulong AppliedThroughSequence { get; set; }
internal InitialTailPhase TailPhase { get; set; }
internal int EnvelopeStageIndex { get; set; } = -1;
internal RuntimeEntityPlacementToken PendingContinuationPlacement { get; set; }
internal ulong PendingContinuationSequence { get; set; }
internal RuntimeAuthoritativePositionRoute PendingContinuationRoute { get; set; }
///
/// Round 3 B1: true once a Position action's merge+publish has
/// committed but TryBeginExclusiveAuthoredPlacement failed on
/// transient operation-slot contention (another operation currently
/// owns this entity's SetPosition slot) rather than genuine
/// staleness. While true, a re-entry into ApplyPositionAction
/// for the SAME continuation/stage skips the merge/publish entirely
/// and retries only the placement begin - closing the "duplicate
/// publish on every retry" hole a naive full re-apply would open.
///
internal bool PositionMergeCommittedForRetry { get; set; }
internal ulong PositionMergeCommittedVersion { get; set; }
internal int ReplayedDeferredChildCount { get; set; }
internal List EnvelopeBuffer { get; } = [];
internal ImmutableArray.Builder Trace { get; } =
ImmutableArray.CreateBuilder();
}
private readonly RuntimeEntityDirectory _entities;
private readonly RuntimeInitialCreateResidenceState _residences;
private readonly RuntimePhysicsState _physics;
private readonly RuntimeEntityObjectEventStream _events;
private readonly Func
_registerDeferredChild;
///
/// Round 3 B12: mirrors
/// for the
/// residence path's WeenieDescription tail action. The executor holds no
/// direct reference to RuntimeEntityObjectLifetime (it is
/// constructed BY that owner) or its ClientObjectTable, so the
/// lifetime binds this delegate at construction the same way it binds
/// .
///
private readonly Func
_applyAcceptedSpawn;
private readonly Dictionary _progress = [];
private readonly HashSet _executing = [];
///
/// C0-1: correlates a published
///
/// receipt back to the full execution receipt/trace, keyed by the SAME
/// public Entity/Sequence identity every other Kind uses (the receipt's
/// own Token.Entity/Token.Sequence). Overwritten (never
/// accumulated) per entity key - an entity cannot have two drains
/// completing concurrently ('s own
/// reentrancy guard), so only the most recent completion for a key is
/// ever meaningful; the exact-sequence check in
/// rejects a stale lookup against a
/// superseded completion under a reused key. C3-1: the tuple's third
/// slot is the SAME receipt already projected once to the public
/// shape (see
/// ) - stored here, not recomputed per
/// read, so a host polling TryGetInitialCreateCompletion across
/// retries never allocates a second time for the same completion.
///
private readonly Dictionary
_completionReceipts = [];
private Func? _generation;
private Func? _usePositionFromServer;
private Func? _localPlayerPosition;
private bool _liveInputsBound;
internal RuntimeInitialCreateContinuationExecutor(
RuntimeEntityDirectory entities,
RuntimeInitialCreateResidenceState residences,
RuntimePhysicsState physics,
RuntimeEntityObjectEventStream events,
Func
registerDeferredChild,
Func
applyAcceptedSpawn)
{
_entities = entities ?? throw new ArgumentNullException(nameof(entities));
_residences = residences
?? throw new ArgumentNullException(nameof(residences));
_physics = physics ?? throw new ArgumentNullException(nameof(physics));
_events = events ?? throw new ArgumentNullException(nameof(events));
_registerDeferredChild = registerDeferredChild
?? throw new ArgumentNullException(nameof(registerDeferredChild));
_applyAcceptedSpawn = applyAcceptedSpawn
?? throw new ArgumentNullException(nameof(applyAcceptedSpawn));
}
internal void BindGeneration(Func generation)
{
ArgumentNullException.ThrowIfNull(generation);
if (_generation is not null)
{
throw new InvalidOperationException(
"The initial-create continuation executor's generation source is already bound.");
}
_generation = generation;
}
///
/// C0-2: binds Runtime's own live-input sources so no host ever computes
/// /
///
/// itself. Optional/nullable exactly like is
/// NOT (that one throws when unbound) - here an unbound source is a
/// legitimate, permanent state for bare-lifetime tests, which keep
/// constructing the executor without a and
/// keep driving with an explicit caller-supplied
/// override (see
/// ). binds the real
/// owners - RuntimeCharacterState.UsePositionFromServer and the
/// live RuntimeLocalPlayerMovementState.Controller position -
/// once both exist (they are constructed AFTER
/// /this executor, so this bind
/// cannot happen at the executor's own constructor time the way
/// does; it happens alongside
/// BindEventContext in GameRuntime's construction
/// sequence). Throws if called twice, matching every other Bind* seam on
/// this class/its siblings (,
/// RuntimeEntityObjectEventStream.BindContext"/>,
/// RuntimePlacementProjectionChannel.BindGeneration).
/// F3: itself returns
/// Vector3?, not Vector3 - a BOUND source with no live
/// controller yet (the login-window drain, before
/// RuntimeLocalPlayerMovementState.Controller exists) must yield
/// null, not a fabricated Vector3.Zero.
/// falls back to the caller-supplied struct's PlayerDistance whenever
/// this source is unbound OR returns null - the SAME fallback rule
/// either way, never a synthetic origin-point distance that could
/// misclassify a remote entity as implausibly far (>96 m) during that
/// window.
///
internal void BindLiveInputs(
Func usePositionFromServer,
Func localPlayerPosition)
{
ArgumentNullException.ThrowIfNull(usePositionFromServer);
ArgumentNullException.ThrowIfNull(localPlayerPosition);
if (_liveInputsBound)
{
throw new InvalidOperationException(
"The initial-create continuation executor's live-input sources are already bound.");
}
_usePositionFromServer = usePositionFromServer;
_localPlayerPosition = localPlayerPosition;
_liveInputsBound = true;
}
///
/// C0-2: resolves the EFFECTIVE inputs for one
/// call. A bound source always wins; the caller-supplied
/// struct is the test-override shape (its own
/// doc comment still describes production usage now that this method
/// exists) and is used verbatim only for whichever field has no bound
/// source - a bare-lifetime test that never calls
/// gets EXACTLY the caller-supplied values,
/// preserving every existing test's behavior unchanged.
/// uses
/// the SAME world-space basis as today's legacy remote path
/// (LiveEntityNetworkUpdateController.cs's
/// MaxPhysicsDistance/dist computation, cutover-routes.md
/// route 4: Vector3.Distance(worldPos, localPlayerPos) where
/// localPlayerPos is the live physics-CONTROLLER position, never a
/// record snapshot) - here, Vector3.Distance between THIS
/// entity's own currently-accepted position (the exact field
/// BeginAcceptedPlacementCore/CanonicalSetupTableId already
/// trust: Snapshot.Physics?.Position ?? Snapshot.Position) and the
/// bound local-player controller position. Computed ONCE per
/// call, matching the one-shot-per-call granularity
/// already had before this slice (retail
/// recomputes player_distance per wire packet; refining this
/// executor to per-continuation freshness is out of C0-2's scope).
///
private RuntimeInitialCreateExecutionInputs ResolveInputs(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateExecutionInputs inputs)
{
bool usePositionFromServer = _usePositionFromServer is { } source
? source()
: inputs.UsePositionFromServer;
float playerDistance = inputs.PlayerDistance;
// F3: an unbound source AND a bound-but-null live position (no
// controller yet) both fall back to the caller-supplied struct
// identically - never fabricate Vector3.Zero as a stand-in.
if (_localPlayerPosition?.Invoke() is { } localPlayerPosition
&& (canonical.Snapshot.Physics?.Position
?? canonical.Snapshot.Position) is { } accepted)
{
var target = new Vector3(
accepted.PositionX, accepted.PositionY, accepted.PositionZ);
playerDistance = Vector3.Distance(target, localPlayerPosition);
}
return new RuntimeInitialCreateExecutionInputs(
usePositionFromServer, playerDistance);
}
///
/// C0-1: reaches the full execution receipt/trace correlated with an
/// observed
/// receipt, purely via that receipt's own public
/// Token.Entity/Token.Sequence identity - the same identity
/// every other placement Kind is acknowledged by. Returns false for a
/// superseded/stale sequence under a reused entity key.
///
internal bool TryGetCompletionReceipt(
in RuntimePlacementProjectionToken token,
out RuntimeInitialCreateExecutionReceipt receipt)
{
if (_completionReceipts.TryGetValue(
token.Entity,
out (ulong Sequence, RuntimeInitialCreateExecutionReceipt Receipt,
RuntimeInitialCreatePlacementCompletion Public) entry)
&& entry.Sequence == token.Sequence)
{
receipt = entry.Receipt;
return true;
}
receipt = default;
return false;
}
///
/// C3-1: the public host consumption surface for
/// - same exact-sequence
/// correlation rule, but returns the cached public projection instead of
/// the internal receipt/trace. Reached via
/// .
///
internal bool TryGetCompletion(
in RuntimePlacementProjectionToken token,
out RuntimeInitialCreatePlacementCompletion completion)
{
if (_completionReceipts.TryGetValue(
token.Entity,
out (ulong Sequence, RuntimeInitialCreateExecutionReceipt Receipt,
RuntimeInitialCreatePlacementCompletion Public) entry)
&& entry.Sequence == token.Sequence)
{
completion = entry.Public;
return true;
}
completion = default;
return false;
}
///
/// Review fix (2026-08-02, architecture pass): every arm is now listed
/// explicitly and the catch-all throws instead of silently folding an
/// unmapped future internal value into None. A value this method
/// cannot map must never reach a host disguised as "nothing to bind" -
/// that would silently drop presentation behavior (e.g. a real
/// teleport-hook phase host code never runs). See
/// 's
/// reflection-based completeness test, which walks every declared
/// value through this exact
/// method and fails if a new internal value is ever added without a
/// matching arm here.
///
private static RuntimeInitialCreateTeleportHookPhase MapHookPhase(
RuntimeTeleportHookPhase phase) => phase switch
{
RuntimeTeleportHookPhase.None =>
RuntimeInitialCreateTeleportHookPhase.None,
RuntimeTeleportHookPhase.BeforePositionOperation =>
RuntimeInitialCreateTeleportHookPhase.BeforePositionOperation,
RuntimeTeleportHookPhase.AfterPositionOperation =>
RuntimeInitialCreateTeleportHookPhase.AfterPositionOperation,
RuntimeTeleportHookPhase.AfterEnterWorld =>
RuntimeInitialCreateTeleportHookPhase.AfterEnterWorld,
_ => throw new ArgumentOutOfRangeException(
nameof(phase),
phase,
$"Unmapped {nameof(RuntimeTeleportHookPhase)} value - add an explicit arm to {nameof(MapHookPhase)} and to the public {nameof(RuntimeInitialCreateTeleportHookPhase)} projection."),
};
///
/// Review fix (2026-08-02): see 's remarks -
/// same explicit-arms-plus-throwing-catch-all discipline.
///
private static RuntimeInitialCreatePositionDisposition MapDisposition(
RuntimeAuthoritativePositionDisposition disposition) => disposition switch
{
RuntimeAuthoritativePositionDisposition.RejectedAuthority =>
RuntimeInitialCreatePositionDisposition.RejectedAuthority,
RuntimeAuthoritativePositionDisposition.RejectedData =>
RuntimeInitialCreatePositionDisposition.RejectedData,
RuntimeAuthoritativePositionDisposition.AwaitFreshPosition =>
RuntimeInitialCreatePositionDisposition.AwaitFreshPosition,
RuntimeAuthoritativePositionDisposition.NoPositionOperation =>
RuntimeInitialCreatePositionDisposition.NoPositionOperation,
RuntimeAuthoritativePositionDisposition.Interpolate =>
RuntimeInitialCreatePositionDisposition.Interpolate,
RuntimeAuthoritativePositionDisposition.SetPosition =>
RuntimeInitialCreatePositionDisposition.SetPosition,
RuntimeAuthoritativePositionDisposition.SetPositionSimple =>
RuntimeInitialCreatePositionDisposition.SetPositionSimple,
_ => throw new ArgumentOutOfRangeException(
nameof(disposition),
disposition,
$"Unmapped {nameof(RuntimeAuthoritativePositionDisposition)} value - add an explicit arm to {nameof(MapDisposition)} and to the public {nameof(RuntimeInitialCreatePositionDisposition)} projection."),
};
///
/// Review fix (2026-08-02): see 's remarks -
/// same explicit-arms-plus-throwing-catch-all discipline.
///
private static RuntimeInitialCreatePositionConstrainPhase MapConstrainPhase(
RuntimePositionConstrainPhase phase) => phase switch
{
RuntimePositionConstrainPhase.None =>
RuntimeInitialCreatePositionConstrainPhase.None,
RuntimePositionConstrainPhase.BeforePositionOperation =>
RuntimeInitialCreatePositionConstrainPhase.BeforePositionOperation,
RuntimePositionConstrainPhase.AfterPositionOperation =>
RuntimeInitialCreatePositionConstrainPhase.AfterPositionOperation,
_ => throw new ArgumentOutOfRangeException(
nameof(phase),
phase,
$"Unmapped {nameof(RuntimePositionConstrainPhase)} value - add an explicit arm to {nameof(MapConstrainPhase)} and to the public {nameof(RuntimeInitialCreatePositionConstrainPhase)} projection."),
};
///
/// C3-1: projects an internal
/// to the public
/// shape exactly once, at completion time (see the
/// _completionReceipts assignment in ).
/// Only Position-kind trace entries carry route facts meaningful for
/// constrain/interpolation binding; every other action kind
/// (InitialAdoption, TeleportHookRequest, replay, envelope stages, ...)
/// is intentionally excluded from -
/// widening this to every trace entry would require making the whole
/// internal action-kind vocabulary public, which the pinned contract
/// explicitly prefers to avoid.
///
private static RuntimeInitialCreatePlacementCompletion ProjectCompletion(
in RuntimeInitialCreateExecutionReceipt receipt)
{
ImmutableArray trace = receipt.Trace;
int positionCount = 0;
for (int i = 0; i < trace.Length; i++)
{
if (trace[i].Kind == RuntimeInitialCreateExecutedActionKind.Position)
positionCount++;
}
ImmutableArray positionFacts;
if (positionCount == 0)
{
positionFacts = ImmutableArray.Empty;
}
else
{
var builder = ImmutableArray.CreateBuilder(
positionCount);
for (int i = 0; i < trace.Length; i++)
{
RuntimeInitialCreateExecutedAction action = trace[i];
if (action.Kind != RuntimeInitialCreateExecutedActionKind.Position)
continue;
// Review fix (2026-08-02): PositionDisposition is nullable
// on RuntimeInitialCreateExecutedAction because it is only
// meaningful for Position/hook-request entries in general -
// but BuildPositionTrace is the SOLE constructor of
// Kind.Position entries (grep-confirmed, 5 call sites, all
// through BuildPositionTrace) and it always passes
// route.Disposition, a non-nullable enum, into this slot.
// Null is therefore NOT a legitimate state for a Position-
// kind entry specifically - a silent `?? NoPositionOperation`
// fallback here would have hidden a real bug (a future
// Position-trace producer that forgot to set it) behind a
// plausible-looking default. Fail loudly instead.
if (action.PositionDisposition is not { } disposition)
{
throw new InvalidOperationException(
"A Position-kind executor trace entry must always " +
"carry a non-null PositionDisposition - " +
"BuildPositionTrace (the sole producer of Kind.Position " +
"entries) always supplies route.Disposition.");
}
builder.Add(new RuntimeInitialCreatePositionRouteFact(
action.Sequence,
MapDisposition(disposition),
MapHookPhase(action.HookPhase),
MapConstrainPhase(action.ConstrainPhase),
action.StopInterpolating,
action.ZeroVelocity,
action.PreserveHeading,
action.SendPositionImmediately));
}
positionFacts = builder.MoveToImmutable();
}
return new RuntimeInitialCreatePlacementCompletion(
receipt.Entity,
receipt.FullCellId,
MapHookPhase(receipt.TeleportHookPhase),
positionFacts,
receipt.ReplayedDeferredChildCount);
}
///
/// F2: reaps exactly one completion-receipt correlation entry, bound as
/// 's
/// notification callback - fired the moment a host acknowledges the
/// Kind ExecutorCompleted receipt this entry correlates, never before.
/// The exact-sequence check rejects removing a NEWER completion's entry
/// under a reused key (mirrors 's
/// own currency check).
///
internal void ForgetCompletionReceipt(RuntimeEntityKey key, ulong sequence)
{
if (_completionReceipts.TryGetValue(
key,
out (ulong Sequence, RuntimeInitialCreateExecutionReceipt Receipt,
RuntimeInitialCreatePlacementCompletion Public) entry)
&& entry.Sequence == sequence)
{
_completionReceipts.Remove(key);
}
}
///
/// F2: folded into /
/// IsConverged - an unacknowledged completion receipt is
/// outstanding host debt, mirroring
/// 's
/// existing "must be zero to converge" shape for the SAME underlying
/// receipt stream.
///
internal int PendingCompletionReceiptCount => _completionReceipts.Count;
internal int ProgressCount => _progress.Count;
///
/// Round 5 R5-3: mirrors the
/// DispatchFailureCount/LastDispatchFailure precedent for the deferred-
/// replay containment introduced by Round 4 R4-1 and extended by this
/// round's deferred-relation replay. A contained catch never silently
/// swallows - it increments this counter and records the exception,
/// then keeps draining the remaining entries.
///
internal long ReplayFailureCount { get; private set; }
internal Exception? LastReplayFailure { get; private set; }
private void RecordReplayFailure(Exception error)
{
ReplayFailureCount++;
LastReplayFailure = error;
}
///
/// Exposes the exact placement token a
///
/// yield is waiting on, so a caller (a test harness today; a host at
/// cutover) can drive 's ordinary
/// prepare/submit/acknowledge cycle on it, exactly like it already does
/// for the initial lease's own placement token.
///
internal bool TryGetPendingContinuationPlacement(
RuntimeEntityKey key,
out RuntimeEntityPlacementToken placement)
{
if (_progress.TryGetValue(key, out Progress? progress)
&& progress.PendingContinuationPlacement.IsValid)
{
placement = progress.PendingContinuationPlacement;
return true;
}
placement = default;
return false;
}
///
/// Exposes the exact classified route the pending continuation placement
/// is running, so a caller can drive
/// with the matching
/// /
/// - the same information
/// already exposes for the initial placement.
///
internal bool TryGetPendingContinuationRoute(
RuntimeEntityKey key,
out RuntimeAuthoritativePositionRoute route)
{
if (_progress.TryGetValue(key, out Progress? progress)
&& progress.PendingContinuationPlacement.IsValid)
{
route = progress.PendingContinuationRoute;
return true;
}
route = default;
return false;
}
///
/// Deterministic cleanup hook wired into the SAME choke points that
/// forget a residence lease ().
/// A retired residence can never leave orphaned executor progress
/// behind. Also forgets any in-flight CONTINUATION placement token
/// (distinct from the residence's own initial-lease placement, which
/// ForgetInitialCreateResidence already forgets separately, and
/// distinct from the unconditional Physics.SetPosition.Forget
/// every existing ForgetInitialCreateResidence caller already
/// runs alongside it - which independently cancels whatever operation
/// currently exists for this key, continuation placement included).
/// This is defensive-in-depth: DiscardProgress owns cleanup of the
/// state IT introduces (PendingContinuationPlacement) rather than
/// relying on every current AND future caller pairing it with an
/// ordinary Forget of its own.
///
internal void DiscardProgress(RuntimeEntityKey key)
{
// F2: reap this key's completion-receipt correlation entry
// unconditionally - DiscardProgress owns cleanup of every piece of
// state IT introduces, and this cache is exactly that (see
// _completionReceipts's own doc comment). Independent of whether
// _progress still tracks this key: a completed drain has ALREADY
// removed its own Progress entry before this correlation entry was
// ever added (see ExecuteCore's Released case), so this is the
// ONLY choke point that reaps it outside of a normal acknowledge.
_completionReceipts.Remove(key);
if (!_progress.Remove(key, out Progress? progress))
return;
if (progress.PendingContinuationPlacement.IsValid)
{
RuntimePlacementCancellationReceipt cancellation =
_physics.SetPosition.ForgetExactPlacement(
progress.PendingContinuationPlacement);
_physics.SetPosition.PublishCancellation(cancellation);
}
}
///
/// Deterministic bulk cleanup wired into
/// 's call site
/// (). Also
/// forgets every in-flight continuation placement token, defensively -
/// Physics.ResetSessionPhysics() runs immediately after this in
/// the same session-clear sequence and would otherwise be the only
/// thing to reap them.
///
internal void DiscardAll()
{
foreach (Progress progress in _progress.Values)
{
if (!progress.PendingContinuationPlacement.IsValid)
continue;
RuntimePlacementCancellationReceipt cancellation =
_physics.SetPosition.ForgetExactPlacement(
progress.PendingContinuationPlacement);
_physics.SetPosition.PublishCancellation(cancellation);
}
_progress.Clear();
// F2: bulk-reap every completion-receipt correlation entry - a full
// session clear must not carry any of this cache across a reset.
_completionReceipts.Clear();
}
internal RuntimeInitialCreateExecutionStatus Execute(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
in RuntimeInitialCreateExecutionInputs inputs,
out RuntimeInitialCreateExecutionReceipt receipt)
{
ArgumentNullException.ThrowIfNull(canonical);
receipt = default;
if (!token.IsValid || canonical.Key is not { } key)
return RuntimeInitialCreateExecutionStatus.RejectedToken;
// A reentrant Execute for the SAME entity while one is already on the
// stack (e.g. a synchronous event observer re-entering) fails closed
// rather than interleaving two drains of the same FIFO.
if (!_executing.Add(key))
return RuntimeInitialCreateExecutionStatus.RejectedAuthority;
try
{
return ExecuteCore(canonical, token, inputs, key, out receipt);
}
finally
{
_executing.Remove(key);
}
}
private RuntimeInitialCreateExecutionStatus ExecuteCore(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
in RuntimeInitialCreateExecutionInputs inputs,
RuntimeEntityKey key,
out RuntimeInitialCreateExecutionReceipt receipt)
{
receipt = default;
// C0-2: resolve ONCE per Execute call - a bound Runtime source always
// wins over the caller-supplied test-override struct (see
// ResolveInputs's own doc comment for the exact fallback rule).
RuntimeInitialCreateExecutionInputs effectiveInputs =
ResolveInputs(canonical, inputs);
// An existing Progress for a DIFFERENT (older or ABA-reused) lease
// id is discarded here, and THIS exact call fails closed - an old
// incarnation's progress can never leak into a reused GUID/key. A
// retry with no prior progress starts fresh and succeeds normally.
if (_progress.TryGetValue(key, out Progress? existing)
&& existing.LeaseId != token.LeaseId)
{
DiscardProgress(key);
return RuntimeInitialCreateExecutionStatus.RejectedAuthority;
}
// Round 3 B11: a FRESH Progress for the CURRENT lease id is never
// materialized here - only lazily below, once Complete() actually
// reports Completed. A PendingPlacement/RejectedToken/RejectedAuthority
// outcome on THIS call must leave the ownership ledger (ProgressCount)
// untouched when nothing was ever tracked before - it should reflect
// drain work actually in flight, not a placeholder for a residence
// that has not even resolved yet.
Progress? progress = existing;
// Resume a placement that a PREVIOUS Execute call began and yielded
// on, before doing anything else. This can belong either to a
// standalone Position continuation or to a Position stage inside a
// SameIncarnationCreate envelope; ApplyContinuation/ApplyEnvelope
// both check PendingContinuationPlacement first for exactly this
// reason.
while (true)
{
// The ONLY legitimate window where one of the four baseline
// fields can move BETWEEN Execute calls without the executor's
// own synchronous code running is a pending continuation
// placement's host-driven prepare/submit/acknowledge cycle
// (RuntimeSetPositionState's own commit machinery advances
// FullCellId/PlacementCommitVersion there). Re-sync the
// baseline ONLY when that exact window was left open by a
// PREVIOUS call - never unconditionally, or every call with
// nothing in flight would bless an external race on these
// fields before Complete() ever gets a chance to see it. Safe
// even when the placement was displaced/cancelled instead of
// committed: ResumePendingPlacement below independently
// re-derives that outcome from
// IsPlacementCurrent/TryPeekAcknowledgedPlacement, not from
// these four fields.
if (progress is not null && progress.PendingContinuationPlacement.IsValid)
{
// Round 4 R4-4: only FullCellId/PlacementCommitVersion can
// legitimately move in this exact window (RuntimeSetPositionState's
// own commit machinery, not the executor) - PositionAuthorityVersion/
// CreateIntegrationVersion moving here would be a genuine
// external race Complete() must still catch.
_residences.AdvanceExecutorBaseline(
canonical,
token,
RuntimeExecutorBaselineFields.FullCellId
| RuntimeExecutorBaselineFields.PlacementCommitVersion);
}
RuntimeInitialCreateResidenceCompletionStatus completion =
_residences.Complete(canonical, token, out RuntimeInitialCreateResidenceReceipt residenceReceipt);
switch (completion)
{
case RuntimeInitialCreateResidenceCompletionStatus.PendingPlacement:
// Nothing has been drained yet for this exact lease -
// leave _progress exactly as found (untouched if it
// never existed).
return RuntimeInitialCreateExecutionStatus.PendingPlacement;
case RuntimeInitialCreateResidenceCompletionStatus.RejectedToken:
DiscardProgress(key);
return RuntimeInitialCreateExecutionStatus.RejectedToken;
case RuntimeInitialCreateResidenceCompletionStatus.RejectedAuthority:
DiscardProgress(key);
return RuntimeInitialCreateExecutionStatus.RejectedAuthority;
}
// Completed: a residence now exists to drain. Materialize
// Progress exactly once, lazily, only at this point.
if (progress is null)
{
progress = new Progress { LeaseId = token.LeaseId };
_progress[key] = progress;
}
if (progress.TailPhase != InitialTailPhase.DeferredReplayed)
{
RuntimeInitialCreateExecutionStatus tailStatus =
RunInitialTail(canonical, token, residenceReceipt, progress);
if (tailStatus != RuntimeInitialCreateExecutionStatus.Completed)
return Abandon(canonical, key);
}
while (progress.AppliedThroughSequence
< (ulong)residenceReceipt.Continuations.Length)
{
if (!_entities.IsCurrent(canonical) || canonical.Key != token.Entity)
return Abandon(canonical, key);
int index = (int)progress.AppliedThroughSequence;
RuntimeInitialCreateResidenceContinuation continuation =
residenceReceipt.Continuations[index];
if (continuation.InstanceSequence != canonical.Incarnation)
return Abandon(canonical, key);
RuntimeInitialCreateExecutionStatus applyStatus =
ApplyContinuation(canonical, token, key, continuation, effectiveInputs, progress);
// Round 3 B2: every apply method below now rebaselines
// itself immediately after its own canonical mutation and
// BEFORE its own publish (mutate -> rebaseline -> publish),
// closing the reentrant-retirement window a synchronous
// Publish observer could otherwise see (the baseline would
// still show the PRE-mutation values while the observer
// reenters residence/executor state). No blanket
// re-synchronize belongs here anymore - each apply already
// guarantees its own baseline is current before ANY
// observer can run.
if (applyStatus
== RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement)
{
return applyStatus;
}
if (applyStatus != RuntimeInitialCreateExecutionStatus.Completed)
return applyStatus;
progress.AppliedThroughSequence = continuation.Sequence;
progress.EnvelopeStageIndex = -1;
}
RuntimeInitialCreateResidenceExecutorReleaseStatus release =
_residences.ConsumeExecuted(
canonical,
residenceReceipt.Adoption,
progress.AppliedThroughSequence);
switch (release)
{
case RuntimeInitialCreateResidenceExecutorReleaseStatus.Released:
{
var completedReceipt = new RuntimeInitialCreateExecutionReceipt(
key,
residenceReceipt.FullCellId,
residenceReceipt.TeleportHookPhase,
progress.Trace.ToImmutable(),
progress.ReplayedDeferredChildCount);
receipt = completedReceipt;
// C3-1: project to the public host-consumption shape
// exactly once here, alongside the internal receipt -
// never recomputed per host read/retry (see
// ProjectCompletion's and _completionReceipts's own doc
// comments).
RuntimeInitialCreatePlacementCompletion publicCompletion =
ProjectCompletion(completedReceipt);
_progress.Remove(key);
// C0-1: bridge the executor's own completion onto the
// SAME ordered placement receipt stream every
// Place/Withdraw/Discard uses (canonical is still
// current here - nothing between the last continuation
// apply and ConsumeExecuted's Released outcome mutates
// it). Correlate the full trace via the fresh token's
// Entity/Sequence identity - see TryGetCompletionReceipt.
// F2: registration happens INSIDE PublishExecutorCompletion's
// beforePublish callback (before the synchronous observer
// dispatch), not after this call returns - a subscriber
// reading the correlation back from inside its own
// OnPlacement callback must already find it. receipt is
// copied to a local (completedReceipt) because an `out`
// parameter cannot be captured by a lambda.
_physics.SetPosition.PublishExecutorCompletion(
canonical,
beforePublish: token =>
_completionReceipts[key] =
(token.Sequence, completedReceipt, publicCompletion));
return RuntimeInitialCreateExecutionStatus.Completed;
}
case RuntimeInitialCreateResidenceExecutorReleaseStatus.Revised:
// A new continuation arrived mid-drain (Enqueue bumps the
// completed entry's Adoption.Revision in place). Re-fetch
// via Complete and drain only the newly-appended tail -
// AppliedThroughSequence already reflects everything this
// progress has committed, so the outer while(true) loop's
// inner drain loop naturally continues from there.
continue;
default:
return Abandon(canonical, key);
}
}
}
///
/// Round 3 B1: the ONE choke point every abandonment path routes
/// through. Retiring the RESIDENCE itself (not just this executor's own
/// progress) is essential here - a caller that only discarded progress
/// and returned RejectedAuthority would leave the residence's own
/// completed entry sitting there fully current; the NEXT Execute call
/// for the same key would re-fetch it via Complete(), start a FRESH
/// Progress at sequence zero, and REPLAY every continuation already
/// committed to the canonical snapshot in this attempt.
/// 's own
/// retirement notification (bound at
/// construction) already
/// routes back to for a successful Forget;
/// the explicit call here is the same idempotent defense-in-depth every
/// other DiscardProgress caller uses, covering the case where Forget
/// finds no matching residence at all (nothing left to retire, but this
/// key's own progress must still go).
///
private RuntimeInitialCreateExecutionStatus Abandon(
RuntimeEntityRecord canonical,
RuntimeEntityKey key)
{
if (_residences.Forget(
canonical,
out _,
out RuntimePlacementCancellationReceipt cancellation))
{
_physics.SetPosition.PublishCancellation(cancellation);
}
DiscardProgress(key);
return RuntimeInitialCreateExecutionStatus.RejectedAuthority;
}
private RuntimeInitialCreateExecutionStatus RunInitialTail(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
in RuntimeInitialCreateResidenceReceipt residenceReceipt,
Progress progress)
{
if (progress.TailPhase == InitialTailPhase.NotStarted)
{
// Resolves the runtime-surface.md 3.1 deadlock: BeginAcceptedPlacementCore
// (every placement-begin entry point) rejects while HasRetainedCompletion
// is true for this key. Consuming the initial placement's
// acknowledged completion here - exactly once, guarded by
// PlacementAdopted - is what lets a later Position continuation
// begin its OWN authored placement for the same key.
if (!_residences.AdoptCompletedPlacement(canonical, token))
return RuntimeInitialCreateExecutionStatus.RejectedAuthority;
progress.Trace.Add(new RuntimeInitialCreateExecutedAction(
RuntimeInitialCreateExecutedActionKind.InitialAdoption,
0UL,
-1,
null,
RuntimeTeleportHookPhase.None));
progress.TailPhase = InitialTailPhase.Adopted;
}
if (progress.TailPhase == InitialTailPhase.Adopted)
{
// Retail: SmartBox new-object player branch, init_player /
// PlayerPositionUpdated (function 1 in retail-notes.md,
// SmartBox::HandleCreateObject 0x00454c80). The hook REQUEST is
// the Runtime-side fact; a host runs the actual after-enter
// teleport suffix at cutover.
if (residenceReceipt.TeleportHookPhase
== RuntimeTeleportHookPhase.AfterEnterWorld)
{
progress.Trace.Add(new RuntimeInitialCreateExecutedAction(
RuntimeInitialCreateExecutedActionKind.TeleportHookRequest,
0UL,
-1,
null,
RuntimeTeleportHookPhase.AfterEnterWorld));
}
progress.TailPhase = InitialTailPhase.HookRecorded;
}
if (progress.TailPhase == InitialTailPhase.HookRecorded)
{
if (!ReplayDeferredChildren(canonical, progress))
return RuntimeInitialCreateExecutionStatus.RejectedAuthority;
progress.TailPhase = InitialTailPhase.DeferredReplayed;
}
if (progress.TailPhase == InitialTailPhase.DeferredReplayed)
{
// Round 5 R5-1: drains the accepted-relation queue keyed to
// THIS guid AFTER the raw-Create replay above - wire-arrival
// order means any relation waiting on this exact guid was
// queued no earlier than the raw children were (retail's
// ProcessObjectNetBlobs replays both classes of blob from the
// SAME per-guid bucket; our queues are split by shape but
// drained in the same relative order).
if (!ReplayDeferredAcceptedRelations(canonical, progress))
return RuntimeInitialCreateExecutionStatus.RejectedAuthority;
progress.TailPhase = InitialTailPhase.RelationsReplayed;
}
return RuntimeInitialCreateExecutionStatus.Completed;
}
///
/// Retail: SmartBox::ProcessObjectNetBlobs 0x00454b20, called at the tail
/// of HandleCreateObject's new-object path for the object that was just
/// created - a parent's own successful Create replays every blob queued
/// waiting on ITS guid, synchronously, in the same call stack, in FIFO
/// order. Wire-arrival order means this replay runs BEFORE the FIFO
/// drain below: children were queued before any continuation targeting
/// this entity itself could exist.
///
/// Round 3 B7: retail detaches the ENTIRE queued netblob list for one
/// parent atomically before dispatching any of it (pseudo-C ~93617) -
/// "detach" IS retail's "consume"; there is no separate peek-then-remove
/// step. This replaces the previous peek/consume loop, which needed a
/// stale-AdmissionId escape hatch for a race that an atomic detach makes
/// structurally impossible: a NEW Create arriving for this same parent
/// during replay enqueues into a brand-new queue instance, since
/// DetachDeferredCreates already removed the old one from the
/// dictionary before this loop starts.
///
/// Round 4 R4-1: two containment gaps closed. (1) one child's
/// registration THROWING no longer escapes Execute or strands
/// the remaining siblings - each registration runs inside a try/catch,
/// recording a
/// outcome and continuing with the next entry on an exception. (2) a
/// mid-loop abandonment (this entity no longer current - e.g. a
/// reentrant delete/reset fired synchronously from an earlier sibling's
/// own registration callback) restores the UNPROCESSED remainder into
/// - in original FIFO order, with
/// original AdmissionIds - rather than permanently destroying it.
/// Retail's own queued blobs live on CObjectMaint (per-GUID), not
/// on the object instance being replayed, so they survive the object
/// and replay again against a recreated GUID; our GUID-keyed
/// persistence already pins this, RestoreDeferredCreates just makes an
/// abandoned-mid-replay attempt honor it too.
///
private bool ReplayDeferredChildren(RuntimeEntityRecord canonical, Progress progress)
{
if (!_entities.IsCurrent(canonical))
return false;
ImmutableArray detached =
_entities.ParentAttachments.DetachDeferredCreates(
canonical.ServerGuid, out DeferredReplayWindowToken window);
for (int index = 0; index < detached.Length; index++)
{
if (!_entities.IsCurrent(canonical))
{
_entities.ParentAttachments.RestoreDeferredCreates(
window,
detached.AsSpan()[index..]);
return false;
}
DeferredParentCreate deferred = detached[index];
RuntimeDeferredChildReplayOutcome outcome;
try
{
RuntimeEntityRegistrationResult result =
_registerDeferredChild(deferred.Spawn, deferred.IsLocalPlayer);
outcome = result.Canonical is not null
? RuntimeDeferredChildReplayOutcome.Registered
: result.DeferredForParent
? RuntimeDeferredChildReplayOutcome.ReDeferred
: RuntimeDeferredChildReplayOutcome.Rejected;
}
catch (Exception error)
{
// Round 4 R4-1: contain the exception here - one bad child
// must not strand the remaining siblings or escape Execute
// as a typed status. Round 5 R5-3: record it on the
// observable failure surface rather than swallowing it.
RecordReplayFailure(error);
outcome = RuntimeDeferredChildReplayOutcome.Rejected;
}
progress.Trace.Add(new RuntimeInitialCreateExecutedAction(
RuntimeInitialCreateExecutedActionKind.DeferredChildReplay,
0UL,
-1,
null,
RuntimeTeleportHookPhase.None,
outcome));
progress.ReplayedDeferredChildCount++;
}
// Nothing left to restore on a full pass - releases the window.
_entities.ParentAttachments.RestoreDeferredCreates(
window, ReadOnlySpan.Empty);
return true;
}
///
/// Round 5 R5-1: drains the accepted-relation queue keyed to this exact
/// guid, replaying every relation a standalone Parent continuation or
/// envelope CreateParent stage deferred because this parent was
/// unaddressable or named a not-yet-arrived incarnation. Mirrors
/// 's detach-first / cancellation-
/// aware-window / contained-failure shape exactly - see that method's
/// remarks for the retail citations and the R4-1/R5-2 rationale, which
/// apply identically here.
///
private bool ReplayDeferredAcceptedRelations(RuntimeEntityRecord canonical, Progress progress)
{
if (!_entities.IsCurrent(canonical))
return false;
ImmutableArray detached =
_entities.ParentAttachments.DetachDeferredAcceptedRelations(
canonical.ServerGuid, out DeferredReplayWindowToken window);
for (int index = 0; index < detached.Length; index++)
{
if (!_entities.IsCurrent(canonical))
{
_entities.ParentAttachments.RestoreDeferredAcceptedRelations(
window,
detached.AsSpan()[index..]);
return false;
}
DeferredAcceptedParentRelation entry = detached[index];
RuntimeParentRelationOutcome outcome;
try
{
outcome = ApplyReplayedParentRelation(canonical, entry);
}
catch (Exception error)
{
RecordReplayFailure(error);
outcome = RuntimeParentRelationOutcome.Rejected;
}
progress.Trace.Add(new RuntimeInitialCreateExecutedAction(
RuntimeInitialCreateExecutedActionKind.ParentRelationReplay,
0UL,
-1,
null,
RuntimeTeleportHookPhase.None,
null,
null,
RuntimePositionConstrainPhase.None,
false,
false,
false,
false,
false,
outcome));
if (outcome == RuntimeParentRelationOutcome.DeferredAwaitingParent)
{
// Relation still names an incarnation that has not arrived
// yet - wait for the NEXT one. Re-enqueues into a BRAND NEW
// queue instance (the whole bucket was already detached
// above), so this same detach loop never re-observes it.
_entities.ParentAttachments.EnqueueDeferredAcceptedRelation(entry);
}
}
_entities.ParentAttachments.RestoreDeferredAcceptedRelations(
window, ReadOnlySpan.Empty);
return true;
}
///
/// Round 5 R5-1: incarnation dispatch vs THIS parent (),
/// mirroring 's own rules -
/// equal incarnation (or the envelope flavor, which has none to compare)
/// commits the attach; THIS parent newer than the relation discards it
/// (stale); the relation newer than THIS parent re-enqueues (wait for
/// the next incarnation - handled by the caller). The merge already
/// committed at the relation's ORIGINAL drain (its own position-
/// timestamp-only stamp); replay commits ONLY the attach tail, never
/// re-runs it.
///
private RuntimeParentRelationOutcome ApplyReplayedParentRelation(
RuntimeEntityRecord parent,
in DeferredAcceptedParentRelation entry)
{
if (!_entities.TryGetActive(entry.ChildGuid, out RuntimeEntityRecord child)
|| child.Key != entry.ChildKey)
{
return RuntimeParentRelationOutcome.Rejected;
}
if (entry.ParentInstanceSequence is { } relationParentInstance
&& parent.Incarnation != relationParentInstance)
{
return PhysicsTimestampGate.IsNewer(relationParentInstance, parent.Incarnation)
? RuntimeParentRelationOutcome.DiscardedStaleParent
: RuntimeParentRelationOutcome.DeferredAwaitingParent;
}
// Round 5 R5-3 note: the child's OWN residence token (if its
// initial-tail is somehow still open at this exact moment) is not
// held here - only the parent's is in scope. AdvanceExecutorBaseline
// is deliberately SKIPPED (rebaseline: false) rather than guessed at;
// if the child's own residence is still active, its own next
// Complete() call will correctly observe this PositionAuthorityVersion
// bump as an external race and fail closed - the safe direction -
// rather than this call silently blessing a baseline it does not
// own.
CommitParentAttachment(child, default, rebaseline: false, buffer: null);
return RuntimeParentRelationOutcome.Applied;
}
private RuntimeInitialCreateExecutionStatus ApplyContinuation(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeEntityKey key,
in RuntimeInitialCreateResidenceContinuation continuation,
in RuntimeInitialCreateExecutionInputs inputs,
Progress progress)
{
if (continuation.Kind
== RuntimeInitialCreateContinuationKind.SameIncarnationCreate)
{
return ApplyEnvelope(canonical, token, key, continuation, inputs, progress);
}
if (progress.PendingContinuationPlacement.IsValid)
{
RuntimeInitialCreateExecutionStatus resumeStatus =
ResumePendingPlacement(canonical, key, progress, out RuntimeAuthoritativePositionRoute route);
if (resumeStatus != RuntimeInitialCreateExecutionStatus.Completed)
return resumeStatus;
progress.Trace.Add(BuildPositionTrace(continuation.Sequence, -1, route));
return RuntimeInitialCreateExecutionStatus.Completed;
}
RuntimeInitialCreateTailAction action = continuation.Actions[0];
switch (continuation.Kind)
{
case RuntimeInitialCreateContinuationKind.ObjDesc:
if (!ApplyObjDescAction(canonical, token, action, null))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.ObjDesc,
continuation.Sequence));
return RuntimeInitialCreateExecutionStatus.Completed;
case RuntimeInitialCreateContinuationKind.Parent:
return ApplyParentContinuation(canonical, token, key, continuation, action, progress);
case RuntimeInitialCreateContinuationKind.Pickup:
if (!ApplyPickupAction(canonical, token, action, null))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.Pickup,
continuation.Sequence));
return RuntimeInitialCreateExecutionStatus.Completed;
case RuntimeInitialCreateContinuationKind.Movement:
if (!ApplyMovementAction(canonical, token, action, null))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.Movement,
continuation.Sequence));
return RuntimeInitialCreateExecutionStatus.Completed;
case RuntimeInitialCreateContinuationKind.State:
if (!ApplyStateAction(canonical, token, action, null))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.State,
continuation.Sequence));
return RuntimeInitialCreateExecutionStatus.Completed;
case RuntimeInitialCreateContinuationKind.Vector:
if (!ApplyVectorAction(canonical, token, action, null))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.Vector,
continuation.Sequence));
return RuntimeInitialCreateExecutionStatus.Completed;
case RuntimeInitialCreateContinuationKind.Position:
return ApplyPositionAction(
canonical,
token,
key,
continuation.Sequence,
-1,
action,
inputs,
progress,
null);
default:
throw new InvalidOperationException(
$"Unsupported initial-Create continuation kind {continuation.Kind}.");
}
}
///
/// Round 3 B9 revalidated the standalone Parent continuation's parent
/// incarnation at EXECUTION time. Round 4 R4-5 replaced admission's
/// dead-letter re-Enqueue with a DISCARD; Round 5 R5-1 OVERTURNS that
/// discard with hard retail evidence: a missing/stale parent QUEUES the
/// raw blob under the PARENT's guid (standalone parent handler
/// 0x004535D0 -> QueueBlobForObject, pseudo-C 92326; GUID-keyed
/// placeholder bucket in CObjectMaint, 271082-271088) and replays
/// it via ProcessObjectNetBlobs when that guid is created - retail
/// NEVER discards on this path; its only check is pointer addressability
/// (92312). The already-accepted position-timestamp merge still runs
/// exactly once here (gate/snapshot lockstep preserved); the dispatch
/// that follows mirrors 's
/// OWN established staleness rules verbatim: unaddressable parent or a
/// relation naming a not-yet-arrived incarnation both ENQUEUE (wait);
/// only a relation whose named incarnation the LIVE parent has already
/// superseded is discarded.
/// drains the queue this enqueues into, in the target parent's own
/// initial tail, after its raw-Create replay.
///
private RuntimeInitialCreateExecutionStatus ApplyParentContinuation(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeEntityKey key,
in RuntimeInitialCreateResidenceContinuation continuation,
RuntimeInitialCreateTailAction action,
Progress progress)
{
ParentEvent.Parsed parentUpdate = action.Parent!.Value;
if (!ApplyParentPositionTimestampOnly(canonical, parentUpdate))
return Abandon(canonical, key);
RuntimeParentRelationOutcome outcome;
if (!_entities.TryGetActive(parentUpdate.ParentGuid, out RuntimeEntityRecord parent))
{
_entities.ParentAttachments.EnqueueDeferredAcceptedRelation(
canonical.ServerGuid, key, parentUpdate, null, action.AcceptedTimestamps);
outcome = RuntimeParentRelationOutcome.DeferredAwaitingParent;
}
else if (parent.Incarnation != parentUpdate.ParentInstanceSequence)
{
if (PhysicsTimestampGate.IsNewer(parentUpdate.ParentInstanceSequence, parent.Incarnation))
{
// Live parent is NEWER than the relation's named incarnation
// - stale, discard (Resolve's own discard branch).
outcome = RuntimeParentRelationOutcome.DiscardedStaleParent;
}
else
{
// Relation names a FUTURE incarnation - wait for it.
_entities.ParentAttachments.EnqueueDeferredAcceptedRelation(
canonical.ServerGuid, key, parentUpdate, null, action.AcceptedTimestamps);
outcome = RuntimeParentRelationOutcome.DeferredAwaitingParent;
}
}
else
{
CommitParentAttachment(canonical, token, rebaseline: true, null);
outcome = RuntimeParentRelationOutcome.Applied;
}
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.Parent,
continuation.Sequence,
parentRelationOutcome: outcome));
return RuntimeInitialCreateExecutionStatus.Completed;
}
///
/// The position-timestamp-only stamp that ALWAYS runs, exactly once,
/// when a standalone Parent continuation is first drained - regardless
/// of whether the dispatch that follows applies, defers, or discards
/// the relation.
/// is exactly retail's ApplyPositionTimestampOnly. None of the
/// four executor-tracked baseline fields move here (Round 4 R4-4), so
/// no AdvanceExecutorBaseline call belongs here either.
///
private bool ApplyParentPositionTimestampOnly(
RuntimeEntityRecord canonical,
ParentEvent.Parsed update)
{
if (!_entities.ApplyAcceptedParentSnapshot(
canonical.ServerGuid,
update,
out WorldSession.EntitySpawn stamped))
{
return false;
}
_entities.RefreshSnapshot(canonical, stamped);
return true;
}
///
/// The envelope's CreateParent stage revalidates parent ADDRESSABILITY
/// only - carries no
/// ParentInstanceSequence at all (retail-notes.md's
/// TryApplyCreateParent remarks: "unlike standalone ParentEvent
/// it carries no parent INSTANCE_TS"), so there is no incarnation to
/// compare - only whether the parent is addressable at all. Round 5
/// R5-1: an unaddressable parent now QUEUES (same retail-faithful
/// deferral as the standalone Parent continuation), not discards - the
/// merge already ran once, unconditionally, before this dispatch.
///
private (bool Success, RuntimeParentRelationOutcome Outcome) ApplyCreateParentContinuation(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeEntityKey key,
RuntimeInitialCreateTailAction action,
List? buffer)
{
CreateParentUpdate createParentUpdate = action.CreateParent!.Value;
if (!ApplyCreateParentPositionTimestampOnly(canonical, createParentUpdate))
return (false, default);
if (!_entities.TryGetActive(createParentUpdate.ParentGuid, out _))
{
_entities.ParentAttachments.EnqueueDeferredAcceptedRelation(
canonical.ServerGuid, key, null, createParentUpdate, action.AcceptedTimestamps);
return (true, RuntimeParentRelationOutcome.DeferredAwaitingParent);
}
CommitParentAttachment(canonical, token, rebaseline: true, buffer);
return (true, RuntimeParentRelationOutcome.Applied);
}
/// Instance-seam-only stamp - see 's remarks.
private bool ApplyCreateParentPositionTimestampOnly(
RuntimeEntityRecord canonical,
CreateParentUpdate update)
{
if (!_entities.ApplyAcceptedCreateParentSnapshot(
canonical.ServerGuid,
update,
out WorldSession.EntitySpawn stamped))
{
return false;
}
_entities.RefreshSnapshot(canonical, stamped);
return true;
}
private RuntimeInitialCreateExecutionStatus ApplyEnvelope(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeEntityKey key,
in RuntimeInitialCreateResidenceContinuation continuation,
in RuntimeInitialCreateExecutionInputs inputs,
Progress progress)
{
int startStage = progress.EnvelopeStageIndex < 0 ? 0 : progress.EnvelopeStageIndex;
if (progress.PendingContinuationPlacement.IsValid)
{
RuntimeInitialCreateExecutionStatus resumeStatus =
ResumePendingPlacement(canonical, key, progress, out RuntimeAuthoritativePositionRoute route);
if (resumeStatus != RuntimeInitialCreateExecutionStatus.Completed)
return resumeStatus;
progress.Trace.Add(BuildPositionTrace(continuation.Sequence, startStage, route));
startStage++;
progress.EnvelopeStageIndex = startStage;
}
for (int i = startStage; i < continuation.Actions.Length; i++)
{
if (!_entities.IsCurrent(canonical))
return Abandon(canonical, key);
RuntimeInitialCreateTailAction action = continuation.Actions[i];
switch (action.Kind)
{
case RuntimeInitialCreateTailActionKind.PreTailDescriptionAdaptation:
// AP-119 compatibility: retail does NOT re-run
// set_description for an equal-generation Create tail.
// The retained PhysicsSpawnData is a presentation-side
// compat artifact only; no canonical mutation here.
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.PreTailDescriptionAdaptation,
continuation.Sequence,
i));
break;
case RuntimeInitialCreateTailActionKind.ObjDesc:
if (!ApplyObjDescAction(canonical, token, action, progress.EnvelopeBuffer))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.ObjDesc,
continuation.Sequence,
i));
break;
case RuntimeInitialCreateTailActionKind.CreateParent:
{
(bool success, RuntimeParentRelationOutcome outcome) =
ApplyCreateParentContinuation(canonical, token, key, action, progress.EnvelopeBuffer);
if (!success)
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.CreateParent,
continuation.Sequence,
i,
parentRelationOutcome: outcome));
break;
}
case RuntimeInitialCreateTailActionKind.Pickup:
if (!ApplyPickupAction(canonical, token, action, progress.EnvelopeBuffer))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.Pickup,
continuation.Sequence,
i));
break;
case RuntimeInitialCreateTailActionKind.Position:
{
progress.EnvelopeStageIndex = i;
RuntimeInitialCreateExecutionStatus status = ApplyPositionAction(
canonical,
token,
key,
continuation.Sequence,
i,
action,
inputs,
progress,
progress.EnvelopeBuffer);
if (status
== RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement)
{
// A mid-envelope yield publishes NOTHING - the
// buffer accumulated so far stays on Progress and is
// flushed only once the whole envelope completes.
return status;
}
if (status != RuntimeInitialCreateExecutionStatus.Completed)
return status;
break;
}
case RuntimeInitialCreateTailActionKind.Movement:
if (!ApplyMovementAction(canonical, token, action, progress.EnvelopeBuffer))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.Movement,
continuation.Sequence,
i));
break;
case RuntimeInitialCreateTailActionKind.State:
if (!ApplyStateAction(canonical, token, action, progress.EnvelopeBuffer))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.State,
continuation.Sequence,
i));
break;
case RuntimeInitialCreateTailActionKind.Vector:
if (!ApplyVectorAction(canonical, token, action, progress.EnvelopeBuffer))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.Vector,
continuation.Sequence,
i));
break;
case RuntimeInitialCreateTailActionKind.WeenieDescription:
if (!ApplyWeenieDescriptionAction(canonical, token, action, progress.EnvelopeBuffer))
return Abandon(canonical, key);
progress.Trace.Add(Simple(
RuntimeInitialCreateExecutedActionKind.WeenieDescription,
continuation.Sequence,
i));
break;
case RuntimeInitialCreateTailActionKind.ResidentCellCleanup:
{
RuntimeResidentCellCleanupDisposition? cleanupDisposition =
ApplyResidentCellCleanup(canonical);
if (cleanupDisposition is null)
{
// Round 3 B1: the fail-closed invariant violation
// (claimed+celless+not-deferred) is a typed
// abandonment, never a throw escaping Execute.
return Abandon(canonical, key);
}
progress.Trace.Add(new RuntimeInitialCreateExecutedAction(
RuntimeInitialCreateExecutedActionKind.ResidentCellCleanup,
continuation.Sequence,
i,
null,
RuntimeTeleportHookPhase.None,
null,
cleanupDisposition));
break;
}
default:
throw new InvalidOperationException(
$"Unsupported same-incarnation tail action {action.Kind}.");
}
// Round 3 B1: persist EnvelopeStageIndex after EVERY committed
// stage, not only Position. Without this, a retry after an
// unexpected mid-envelope failure (or any future non-Position
// yield point) would resume from a stale index and REPLAY
// stages already committed to the canonical snapshot -
// Position's own yield/resume already tracks this correctly;
// this makes every other stage kind do the same.
progress.EnvelopeStageIndex = i + 1;
}
// Retail's tail is one synchronous critical section; no observer
// boundary between stages. Publish every buffered per-stage event
// consecutively, in stage order, only now that every stage committed.
foreach (PendingPublish pending in progress.EnvelopeBuffer)
PublishNow(canonical, pending.Change, pending.Matches, pending.Cancellation);
progress.EnvelopeBuffer.Clear();
progress.EnvelopeStageIndex = -1;
return RuntimeInitialCreateExecutionStatus.Completed;
}
///
/// Round 3 B4: matches
/// 's FULL
/// record/projection agreement rather than a subset of it - a
/// continuation's own authored placement deserves the same staleness
/// rigor as the initial lease's placement. Beyond the projection's own
/// reported facts, this also re-checks the LIVE canonical record's
/// PositionAuthorityVersion (has something ELSE moved the record since
/// this exact placement began?) and FullCellId/PlacementCommitVersion
/// (does the projection's committed cell/version still match reality?).
///
private RuntimeInitialCreateExecutionStatus ResumePendingPlacement(
RuntimeEntityRecord canonical,
RuntimeEntityKey key,
Progress progress,
out RuntimeAuthoritativePositionRoute route)
{
route = progress.PendingContinuationRoute;
RuntimeEntityPlacementToken placementToken = progress.PendingContinuationPlacement;
if (_physics.SetPosition.IsPlacementCurrent(placementToken))
return RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement;
if (!_physics.SetPosition.TryPeekAcknowledgedPlacement(
placementToken,
out RuntimePlacementProjectionToken projection)
|| projection.Entity != placementToken.Entity
|| projection.SessionLifetimeVersion != placementToken.SessionLifetimeVersion
|| projection.PositionAuthorityVersion != placementToken.PositionAuthorityVersion
|| canonical.PositionAuthorityVersion != placementToken.PositionAuthorityVersion
|| projection.ExactCellId == 0u
|| projection.ExactCellId != canonical.FullCellId
|| projection.PlacementCommitVersion != canonical.PlacementCommitVersion)
{
// Round 4 R4-2: forget -> clear -> Abandon. Neither still in
// flight nor acknowledged with matching facts - cancelled or
// superseded by a newer authoritative operation. ForgetExactPlacement
// removes the retained _acknowledgedPlacementCompletions entry
// (via ForgetPlacementCompletionCore) even in this mismatch
// case - without it, HasRetainedCompletion for this key would
// stay true forever and block EVERY later placement begin (the
// runtime-surface.md 3.1 deadlock this executor exists to
// resolve). A newer owner now has the entity; abandon this
// execution.
RuntimePlacementCancellationReceipt forgotten =
_physics.SetPosition.ForgetExactPlacement(placementToken);
_physics.SetPosition.PublishCancellation(forgotten);
progress.PendingContinuationPlacement = default;
return Abandon(canonical, key);
}
if (!_physics.SetPosition.ConsumeAcknowledgedPlacement(placementToken, projection))
{
// Round 4 R4-2: same forget -> clear -> Abandon ordering - a
// concurrent consumer raced this exact acknowledgement away
// between TryPeek and here; still forget defensively so no
// stale watch/ack entry survives under this token.
RuntimePlacementCancellationReceipt forgotten =
_physics.SetPosition.ForgetExactPlacement(placementToken);
_physics.SetPosition.PublishCancellation(forgotten);
progress.PendingContinuationPlacement = default;
return Abandon(canonical, key);
}
progress.PendingContinuationPlacement = default;
progress.PendingContinuationSequence = 0UL;
return RuntimeInitialCreateExecutionStatus.Completed;
}
private RuntimeInitialCreateExecutionStatus ApplyPositionAction(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeEntityKey key,
ulong sequence,
int stage,
RuntimeInitialCreateTailAction action,
in RuntimeInitialCreateExecutionInputs inputs,
Progress progress,
List? buffer)
{
if (!_residences.TryGetTransaction(canonical, out RuntimeInitialCreateResidenceLease lease)
|| canonical.Key != key)
{
return Abandon(canonical, key);
}
WorldSession.EntityPositionUpdate update = action.Position!.Value;
RuntimePositionEntityKind entityKind = EntityKindOf(lease.Route.OperationKind);
bool isLocalPlayer = entityKind is RuntimePositionEntityKind.LocalPlayer;
RuntimeAuthoritativePositionRoute route;
if (progress.PositionMergeCommittedForRetry)
{
// Round 3 B1: a PREVIOUS attempt already merged and published
// this exact position continuation; TryBeginExclusiveAuthoredPlacement
// failed on transient operation-slot contention rather than
// staleness, and this re-entry retries ONLY the placement
// begin. Re-running the merge here would double-publish.
route = progress.PendingContinuationRoute;
}
else
{
// Round 3 A3 / B5 and Round 4 R4-13 (contact from the retained
// wire packet's own IsGrounded bit only; the data-driven
// HasAnimations proxy with its PhysicsSpawnData fallback) now
// live in the ONE shared request builder, which C4 route 4a's
// remote classification also uses - see
// RuntimeAcceptedPositionRouteRequests for why a second
// hand-written copy of this construction is not allowed.
RuntimeAcceptedPositionRouteRequest request =
RuntimeAcceptedPositionRouteRequests.Build(
CurrentGeneration(),
canonical,
key,
update,
entityKind,
action.PositionSource,
action.PositionDisposition,
action.PreviousTeleportSequence,
action.AcceptedTimestamps.Teleport,
inputs.PlayerDistance,
inputs.UsePositionFromServer);
route = RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition(request);
if (!route.Accepted)
{
// Round 3 B10: distinguish two retained-action shapes.
// When admission ITSELF already rejected (only
// FORCE_POSITION_TS could have moved), the ordinary Rejected
// merge is the correct stamp-only path. When admission
// ACCEPTED (Apply/ForcePosition - POSITION_TS/TELEPORT_TS/
// FORCE_POSITION_TS genuinely advanced) but EXECUTION-time
// classification now rejects, the snapshot must still
// reflect every channel the gate actually moved, not just
// ForcePosition.
bool stampedOk = action.PositionDisposition
is PositionTimestampDisposition.Rejected
? _entities.ApplyAcceptedPositionSnapshot(
canonical.ServerGuid,
update,
PositionTimestampDisposition.Rejected,
action.AcceptedTimestamps,
isLocalPlayer,
null,
null,
installPlacementFrame: false,
clearParent: false,
out WorldSession.EntitySpawn stampedOnly)
: _entities.ApplyAcceptedPositionExecutionRejectedSnapshot(
canonical.ServerGuid,
update.PositionSequence,
action.AcceptedTimestamps,
out stampedOnly);
if (!stampedOk)
return Abandon(canonical, key);
_entities.RefreshSnapshot(canonical, stampedOnly);
progress.Trace.Add(BuildPositionTrace(sequence, stage, route));
return RuntimeInitialCreateExecutionStatus.Completed;
}
// CANONICAL CELL SEMANTICS (deliberate difference from the
// legacy direct-commit InboundPhysicsStateController.TryApplyPosition
// caller): refreshPosition stays false. A wire position never
// directly makes the record resident; only a Runtime SetPosition
// commit (below) or a simulation full-cell commit may change
// FullCellId. This matches retail (HandleReceivedPosition never
// sets a resident cell) and the classifier's own documented rule
// that a target frame with a nonzero cell does not make a
// cellless canonical body resident. The snapshot's Position
// field itself IS refreshed; only the derived FullCellId write
// is withheld.
//
// Round 3 B6: installPlacementFrame/clearParent come from the
// classified route's OWN ApplyPlacementFrameBeforeRouting/
// UnparentBeforeRouting flags, not the legacy path's
// unconditional true/true.
PhysicsBody? body = canonical.PhysicsBody;
bool mergedOk = _entities.ApplyAcceptedPositionSnapshot(
canonical.ServerGuid,
update,
action.PositionDisposition,
action.AcceptedTimestamps,
isLocalPlayer,
body?.Orientation,
body?.Velocity,
installPlacementFrame: route.ApplyPlacementFrameBeforeRouting,
clearParent: route.UnparentBeforeRouting,
out WorldSession.EntitySpawn merged);
if (!mergedOk)
return Abandon(canonical, key);
_entities.RefreshSnapshot(canonical, merged, refreshPosition: false);
_entities.AdvancePositionAuthority(canonical);
_entities.ParentAttachments.EndChildProjection(canonical.ServerGuid);
// Round 3 B2: mutate -> rebaseline -> publish. Rebaselining
// BEFORE Publish closes the reentrant-retirement window a
// synchronous observer could otherwise see (the baseline would
// still show pre-mutation values while the observer reenters
// residence/executor state). Round 4 R4-4: only
// PositionAuthorityVersion moved (AdvancePositionAuthority also
// bumps VelocityAuthorityVersion, which is not one of the four
// executor-tracked baseline fields).
_residences.AdvanceExecutorBaseline(
canonical, token, RuntimeExecutorBaselineFields.PositionAuthorityVersion);
ulong positionVersion = canonical.PositionAuthorityVersion;
ulong spatialVersion = canonical.SpatialAuthorityVersion;
Publish(
canonical,
RuntimeEntityChange.Updated,
() => canonical.PositionAuthorityVersion == positionVersion
&& canonical.SpatialAuthorityVersion == spatialVersion,
default,
buffer);
if (!route.PerformsSetPosition)
{
// Interpolate / NoPositionOperation / AwaitFreshPosition:
// typed trace result only. Binding to the live
// interpolation owner is cutover work.
progress.Trace.Add(BuildPositionTrace(sequence, stage, route));
return RuntimeInitialCreateExecutionStatus.Completed;
}
progress.PositionMergeCommittedForRetry = true;
progress.PendingContinuationRoute = route;
progress.PositionMergeCommittedVersion = canonical.PositionAuthorityVersion;
}
RuntimeEntityPlacementToken placement = _physics.SetPosition
.TryBeginExclusiveAuthoredPlacement(
canonical,
canonical.PositionAuthorityVersion,
route.OperationKind);
if (!placement.IsValid)
{
// Round 3 B1: distinguish genuine staleness (abandon) from
// transient operation-slot contention (retry - the SAME merge
// stays committed; only the begin attempt repeats).
if (!_entities.IsCurrent(canonical)
|| canonical.PositionAuthorityVersion != progress.PositionMergeCommittedVersion)
{
progress.PositionMergeCommittedForRetry = false;
return Abandon(canonical, key);
}
return RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement;
}
if (!_physics.SetPosition.WatchPlacementCompletion(placement))
{
_ = _physics.SetPosition.ForgetExactPlacement(placement);
progress.PositionMergeCommittedForRetry = false;
return Abandon(canonical, key);
}
progress.PositionMergeCommittedForRetry = false;
progress.PendingContinuationPlacement = placement;
progress.PendingContinuationSequence = sequence;
progress.PendingContinuationRoute = route;
return RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement;
}
private bool ApplyObjDescAction(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeInitialCreateTailAction action,
List? buffer)
{
if (!_entities.ApplyAcceptedObjDescSnapshot(
canonical.ServerGuid,
action.ObjDesc!.Value,
out WorldSession.EntitySpawn merged))
{
return false;
}
_entities.RefreshSnapshot(canonical, merged);
_entities.AdvanceObjDescAuthority(canonical);
// Round 4 R4-4: ObjDescAuthorityVersion is not one of the four
// executor-tracked baseline fields (PositionAuthorityVersion/
// CreateIntegrationVersion/FullCellId/PlacementCommitVersion) - no
// AdvanceExecutorBaseline call belongs here at all; calling it
// unconditionally would silently bless an external race on those
// four fields that this apply never touched.
ulong version = canonical.ObjDescAuthorityVersion;
Publish(
canonical,
RuntimeEntityChange.Updated,
() => canonical.ObjDescAuthorityVersion == version,
default,
buffer);
return true;
}
///
/// Round 5 R5-1: the SHARED attach-commit tail for BOTH the standalone
/// Parent continuation and the envelope CreateParent stage - the two
/// were byte-identical bodies before this round. The merge step that
/// precedes them (ApplyAcceptedParentSnapshot/
/// ApplyAcceptedCreateParentSnapshot, factored out into
/// /
/// ) runs EXACTLY
/// once at the relation's original drain and is deliberately
/// position-timestamp-only - it never sets ParentGuid/ParentLocation on
/// the snapshot. Per the test
/// StandaloneParentContinuationAppliesPositionTimestampOnlyMergeWithNoReDefer's
/// established, pre-Round-5 precedent (verified against it directly:
/// an earlier revision of this method wrongly called
/// here and broke
/// that test), the actual attach commit is out of scope for
/// this residence-continuation drain - it is the App-layer
/// EquippedChildRenderController's job, invoked through
/// only after
/// it validates the parent's render-side PartArray/holding-location can
/// actually host the child (see LiveEntityRuntime.CommitStagedParent's
/// remarks). This method therefore commits ONLY the residence tail
/// (AdvancePositionAuthority/LeaveWorld/Forget/rebaseline/publish) and is
/// payload-agnostic, so it serves the live-continuation apply AND
/// 's replayed apply
/// identically - "apply through the SAME parent-apply body used by a
/// live Parent continuation" (round5-fixes.md R5-1) means exactly this
/// tail, not a new attach step neither live nor replay ever performed.
/// Unlike the legacy CommitPositionChannelUpdate helper, this does NOT
/// call ForgetInitialCreateResidence - the executor IS the residence
/// owner mid-drain; forgetting it here would cancel our own in-progress
/// lease. Residence teardown is exclusively the adoption/release
/// machinery's job (RunInitialTail / ConsumeExecuted).
/// is false ONLY at replay time, when the
/// child's own residence token is not held here - see
/// 's remarks for why that is
/// safe (fails closed, never silently blesses a baseline it does not
/// own).
///
private void CommitParentAttachment(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
bool rebaseline,
List? buffer)
{
_entities.AdvancePositionAuthority(canonical);
_physics.CollisionReports.LeaveWorld(canonical);
RuntimePlacementCancellationReceipt cancellation =
_physics.SetPosition.Forget(canonical);
// Round 3 B2: mutate -> rebaseline -> publish. Round 4 R4-4:
// AdvancePositionAuthority only moves PositionAuthorityVersion of
// the four tracked fields.
if (rebaseline)
{
_residences.AdvanceExecutorBaseline(
canonical, token, RuntimeExecutorBaselineFields.PositionAuthorityVersion);
}
ulong positionVersion = canonical.PositionAuthorityVersion;
ulong spatialVersion = canonical.SpatialAuthorityVersion;
Publish(
canonical,
RuntimeEntityChange.Updated,
() => canonical.PositionAuthorityVersion == positionVersion
&& canonical.SpatialAuthorityVersion == spatialVersion,
cancellation,
buffer);
}
private bool ApplyPickupAction(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeInitialCreateTailAction action,
List? buffer)
{
if (!_entities.ApplyAcceptedPickupSnapshot(
canonical.ServerGuid,
action.Pickup!.Value,
out WorldSession.EntitySpawn merged))
{
return false;
}
_entities.RefreshSnapshot(canonical, merged);
// Retail: the object entered world (this residence's initial tail),
// then was picked up - a FIFO entry always executes AFTER the
// initial placement committed. This is a leave-world edge, but it
// must not tear down the residence mid-drain: only ordinary
// SetPosition.Forget runs here, never ForgetInitialCreateResidence.
_entities.AdvancePositionAuthority(canonical);
// D7 (route 7, architecture review A5): retail order is
// unset_parent @0x0045227F THEN leave_world @0x00452286
// (SmartBox::DoPickupEvent) - the same reorder
// RuntimeEntityObjectLifetime.TryApplyPickup applies to the live
// pickup path, applied here to the DORMANT replay of the same wire
// event so both pickup paths are one shape.
_entities.ParentAttachments.EndChildProjection(canonical.ServerGuid);
_physics.CollisionReports.LeaveWorld(canonical);
RuntimePlacementCancellationReceipt cancellation =
_physics.SetPosition.Forget(canonical);
_entities.SuspendObjectClock(canonical);
_entities.SetFullCell(canonical, 0u, 0u);
// Round 3 B2: mutate -> rebaseline -> publish. Round 4 R4-4:
// AdvancePositionAuthority + SetFullCell(0,0) move
// PositionAuthorityVersion and FullCellId, the only two of the
// four tracked fields this apply touches.
_residences.AdvanceExecutorBaseline(
canonical,
token,
RuntimeExecutorBaselineFields.PositionAuthorityVersion
| RuntimeExecutorBaselineFields.FullCellId);
ulong positionVersion = canonical.PositionAuthorityVersion;
ulong spatialVersion = canonical.SpatialAuthorityVersion;
Publish(
canonical,
RuntimeEntityChange.Withdrawn,
() => canonical.PositionAuthorityVersion == positionVersion
&& canonical.SpatialAuthorityVersion == spatialVersion,
cancellation,
buffer);
return true;
}
private bool ApplyMovementAction(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeInitialCreateTailAction action,
List? buffer)
{
WorldSession.EntityMotionUpdate update = action.Movement!.Value;
// Safe to pass the retained wire's own MovementSequence directly
// here (unlike the legacy caller, which must read the live gate -
// see ApplyAcceptedMotion's remarks): a Movement continuation is
// only ever retained when AppliesMovementPayload || HasTimestampMutation,
// which structurally guarantees MOVEMENT_TS itself already advanced
// to this exact value at admission time.
if (!_entities.ApplyAcceptedMotionSnapshot(
canonical.ServerGuid,
update.MovementSequence,
action.AcceptedTimestamps.ServerControlledMove,
update,
retainPayload: false,
out WorldSession.EntitySpawn stamped))
{
return false;
}
_entities.RefreshSnapshot(canonical, stamped);
if (!action.AppliesMovementPayload)
{
// Timestamp-only entry: stamp landed above; no publish beyond
// that, matching legacy's own timestamp-only branch. Round 4
// R4-4: the stamp only moves MovementSequence/ServerControlSequence
// (nested Physics.Timestamps), never any of the four
// executor-tracked baseline fields - no AdvanceExecutorBaseline
// call here.
return true;
}
if (action.RetainMovementPayload)
{
if (!_entities.ApplyAcceptedMotionSnapshot(
canonical.ServerGuid,
update.MovementSequence,
action.AcceptedTimestamps.ServerControlledMove,
update,
retainPayload: true,
out WorldSession.EntitySpawn merged))
{
return false;
}
_entities.RefreshSnapshot(canonical, merged);
_entities.AdvanceMovementAuthority(canonical);
}
_entities.AdvanceMovementCommit(canonical);
// Round 4 R4-4: MovementAuthorityVersion/MovementCommitVersion are
// not among the four executor-tracked baseline fields - no
// AdvanceExecutorBaseline call belongs here.
ulong movementCommitVersion = canonical.MovementCommitVersion;
Publish(
canonical,
RuntimeEntityChange.Updated,
() => canonical.MovementCommitVersion == movementCommitVersion,
default,
buffer);
return true;
}
///
/// Round 4 R4-11: the BecameHidden branch's currency-failure path
/// returns false (routes the caller to the shared
/// Abandon/RejectedAuthority), not true as an earlier
/// revision of this method did - the legacy equivalent reports failure
/// there too, and the record genuinely mutated under us mid-apply.
/// Not independently unit-tested with a live reentrancy seam: this
/// harness has no constructible way to make
/// RuntimeCollisionReportingState.LeaveWorld invoke an observer
/// callback for a residence-fresh entity - EndExpiredObjectCollisions
/// returns immediately whenever _owners has no established
/// collision record for this key (see
/// RuntimeCollisionReportingState.cs's own early-return guard),
/// which is always true for an entity that has never yet run a real
/// collision batch. Building a synthetic seam to force that callback
/// would be exactly the kind of workaround this project's CLAUDE.md
/// forbids; the fix is verified by direct code review of the
/// now-symmetric bool contract instead (every OTHER Apply*Action
/// method already returns false, never true, on its own currency
/// failure).
///
private bool ApplyStateAction(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeInitialCreateTailAction action,
List? buffer)
{
SetState.Parsed update = action.State!.Value;
if (!_entities.ApplyAcceptedStateSnapshot(
canonical.ServerGuid,
update,
out WorldSession.EntitySpawn merged))
{
return false;
}
_entities.RefreshSnapshot(canonical, merged);
RetailPhysicsStateTransition preview = RetailPhysicsStateTransitions.Apply(
canonical.FinalPhysicsState,
(PhysicsStateFlags)update.PhysicsState);
ulong priorPhysicsMutation = canonical.PhysicsStateMutationVersion;
if (preview.HiddenTransition is RetailHiddenTransition.BecameHidden)
{
_physics.CollisionReports.LeaveWorld(canonical);
if (!_entities.IsCurrent(canonical)
|| canonical.PhysicsStateMutationVersion != priorPhysicsMutation)
{
// Round 4 R4-11: the record mutated out from under us mid-apply
// (LeaveWorld's own synchronous collision-report callbacks can
// reenter and either invalidate currency or bump
// PhysicsStateMutationVersion again) - that IS an external
// race, not a successfully-applied continuation. Return false
// so the caller routes through the shared Abandon
// (RejectedAuthority), matching the legacy equivalent's
// failure report instead of silently claiming success.
return false;
}
}
RetailPhysicsStateTransition transition =
_entities.ApplyRawPhysicsState(canonical, update.PhysicsState);
if (canonical.Key is { } key)
{
_physics.Engine.ShadowObjects.UpdatePhysicsState(
key.LocalEntityId,
(uint)canonical.FinalPhysicsState);
}
// Round 4 R4-4: StateAuthorityVersion/PhysicsStateMutationVersion
// are not among the four executor-tracked baseline fields - no
// AdvanceExecutorBaseline call belongs here.
ulong stateVersion = canonical.StateAuthorityVersion;
ulong physicsMutationVersion = canonical.PhysicsStateMutationVersion;
Publish(
canonical,
transition.HiddenTransition is RetailHiddenTransition.BecameHidden
? RuntimeEntityChange.Hidden
: RuntimeEntityChange.Updated,
() => canonical.StateAuthorityVersion == stateVersion
&& canonical.PhysicsStateMutationVersion == physicsMutationVersion,
default,
buffer);
return true;
}
private bool ApplyVectorAction(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeInitialCreateTailAction action,
List? buffer)
{
if (!_entities.ApplyAcceptedVectorSnapshot(
canonical.ServerGuid,
action.Vector!.Value,
out WorldSession.EntitySpawn merged))
{
return false;
}
_entities.RefreshSnapshot(canonical, merged);
_entities.AdvanceVectorAuthority(canonical);
// Round 4 R4-4: VectorAuthorityVersion is not among the four
// executor-tracked baseline fields - no AdvanceExecutorBaseline
// call belongs here.
ulong version = canonical.VectorAuthorityVersion;
Publish(
canonical,
RuntimeEntityChange.Updated,
() => canonical.VectorAuthorityVersion == version,
default,
buffer);
return true;
}
private bool ApplyWeenieDescriptionAction(
RuntimeEntityRecord canonical,
in RuntimeInitialCreateResidenceToken token,
RuntimeInitialCreateTailAction action,
List? buffer)
{
// Round 3 A2: this must NOT be a wholesale RefreshSnapshot of the
// raw retained packet - it merges exactly like every other
// same-generation Create (MergeUntimestampedCreate via the instance
// seam), keeping the retained Position/appearance/physics-timestamp
// fields earlier stages already committed to _snapshots.
if (!_entities.ApplyAcceptedWeenieDescriptionSnapshot(
canonical.ServerGuid,
action.WeenieDescription!.Value,
out WorldSession.EntitySpawn merged))
{
return false;
}
_entities.RefreshSnapshot(canonical, merged, refreshPosition: false);
// RuntimeEntityObjectLifetime.RegisterEntityCore's ExistingGeneration
// branch only calls Entities.AdvanceCreateAuthority when
// !beginInitialResidence - a residence-pending entity's admission
// deliberately skipped it. This deferred WeenieDescription tail
// action is where that authority mutation actually lands.
_entities.AdvanceCreateAuthority(canonical);
ulong createVersion = canonical.CreateIntegrationVersion;
// Round 3 B12: RuntimeLiveEntitySessionController.OnSpawned (the
// non-residence direct-host Create path) drives
// ApplyAcceptedSpawn(canonical, integrationVersion, canonical.Snapshot,
// replaceGeneration: NewGeneration) for EVERY accepted Create - the
// prior "zero callers" claim for this object-table wiring was false.
// This tail action is the residence path's exact counterpart: it
// only ever runs for an ExistingGeneration same-incarnation Create
// (a residence is admitted only when preview is ExistingGeneration),
// so replaceGeneration is always false here.
//
// Round 4 R4-3: order is AdvanceCreateAuthority -> AdvanceExecutorBaseline
// -> _applyAcceptedSpawn -> (false -> typed Abandon) -> buffered
// publish. Rebaselining BEFORE the object-table apply (rather than
// after, as an earlier revision did) means a reentrant callback
// FROM WITHIN _applyAcceptedSpawn's own synchronous
// ObjectAdded/ObjectUpdated dispatch already observes a current
// baseline. _applyAcceptedSpawn's own result is now actually
// OBSERVED rather than discarded: RuntimeEntityObjectLifetime.
// ApplyAcceptedSpawn re-checks currency before, during, AND after
// its own object-table apply (its callback is synchronous and may
// re-enter entity lifetime) - a nested replacement racing in from
// that same callback invalidates the exact canonical incarnation
// this drain is still executing against, mirroring
// RuntimeLiveEntitySessionController.cs:87's own gate on that same
// call's result. The remaining tail cannot run against a record a
// nested replacement has already superseded.
_residences.AdvanceExecutorBaseline(
canonical,
token,
RuntimeExecutorBaselineFields.PositionAuthorityVersion
| RuntimeExecutorBaselineFields.CreateIntegrationVersion);
if (!_applyAcceptedSpawn(canonical, createVersion, merged, /* replaceGeneration: */ false))
return false;
Publish(
canonical,
RuntimeEntityChange.Updated,
() => canonical.CreateIntegrationVersion == createVersion,
default,
buffer);
return true;
}
///
/// Retail: SmartBox::HandleCreateObject's same-incarnation tail, final
/// step (retail-notes.md function 1, 0x00454c80, lines ~788-801) -
/// objcell_id != 0 && cell == 0 marks for destruction,
/// objcell_id != 0 && cell != 0 un-marks, and no cell
/// claimed with no weenie also marks for destruction. Asserts the
/// invariant rather than building a new destruction mechanism: any
/// claimed-but-celless outcome must already be under lost-cell/deferred
/// SetPosition ownership.
///
private RuntimeResidentCellCleanupDisposition? ApplyResidentCellCleanup(
RuntimeEntityRecord canonical)
{
uint claimedCell = canonical.Snapshot.Physics?.Position?.LandblockId
?? canonical.Snapshot.Position?.LandblockId
?? 0u;
if (claimedCell == 0u)
{
// No cell claimed. See RuntimeResidentCellCleanupDisposition.
// CelllessNoWeenieMarkUnreachable's remarks: retail's matching
// no-weenie destruction-mark condition is structurally
// unreachable through this exact envelope path.
return RuntimeResidentCellCleanupDisposition
.CelllessNoWeenieMarkUnreachable;
}
if (canonical.FullCellId != 0u)
return RuntimeResidentCellCleanupDisposition.ResidentUnmarked;
if (!_physics.SetPosition.IsDeferred(canonical))
{
// Round 3 B1: the fail-closed invariant violation
// (claimed+celless+not-deferred) is a typed abandonment - null
// signals the caller to Abandon rather than letting an
// exception escape Execute.
return null;
}
// Claimed but celless, and an existing lost-cell/deferred
// SetPosition operation already owns this exact entity: the
// destruction mark belongs to that existing lifetime (retail:
// AddObjectToBeDestroyed was already reached via that path), not to
// this tail action - assert the invariant, do not invent a second
// destruction mechanism.
return RuntimeResidentCellCleanupDisposition.DeferredUnderLostCellOwnership;
}
private void Publish(
RuntimeEntityRecord canonical,
RuntimeEntityChange change,
Func matches,
RuntimePlacementCancellationReceipt cancellation,
List? buffer)
{
if (buffer is not null)
{
// Buffered (same-incarnation envelope) publishes flush only
// after EVERY stage has committed. The per-field "matches"
// check the IMMEDIATE (standalone-continuation) path uses
// exists to catch a reentrant race between one mutation and
// its own publish - but a LATER stage in the SAME envelope
// legitimately advances the SAME field again as normal,
// expected progression (e.g. WeenieDescription's
// AdvanceCreateAuthority bumps Position/State/Vector/ObjDesc
// authority all at once), which would make an EARLIER stage's
// captured matches() go stale by flush time even though
// nothing external raced it. IsCurrent (checked unconditionally
// by PublishNow below) is the only currency guard a buffered
// entry needs: envelope processing dispatches no event until
// the flush, so there is no opportunity for reentrancy mid-
// envelope except at a Position-stage yield, and THAT window is
// independently guarded by ApplyEnvelope's own IsCurrent check
// at the top of the resumed loop and by ResumePendingPlacement.
buffer.Add(new PendingPublish(change, static () => true, cancellation));
return;
}
PublishNow(canonical, change, matches, cancellation);
}
private void PublishNow(
RuntimeEntityRecord canonical,
RuntimeEntityChange change,
Func matches,
RuntimePlacementCancellationReceipt cancellation)
{
_physics.SetPosition.PublishCancellation(cancellation);
if (_entities.IsCurrent(canonical) && matches())
_events.PublishEntity(change, canonical);
}
private static RuntimeInitialCreateExecutedAction BuildPositionTrace(
ulong sequence,
int stage,
in RuntimeAuthoritativePositionRoute route) => new(
RuntimeInitialCreateExecutedActionKind.Position,
sequence,
stage,
route.Disposition,
route.TeleportHookPhase,
null,
null,
// Round 3 B6: record the route's own flags in the trace.
route.ConstrainPhase,
route.StopInterpolating,
route.ZeroVelocity,
route.PreserveHeading,
route.SendPositionImmediately,
route.UnparentBeforeRouting);
private static RuntimeInitialCreateExecutedAction Simple(
RuntimeInitialCreateExecutedActionKind kind,
ulong sequence,
int stage = -1,
RuntimeParentRelationOutcome? parentRelationOutcome = null) => new(
kind,
sequence,
stage,
null,
RuntimeTeleportHookPhase.None,
ParentRelationOutcome: parentRelationOutcome);
private static RuntimePositionEntityKind EntityKindOf(
RuntimeSetPositionOperationKind operationKind) => operationKind switch
{
RuntimeSetPositionOperationKind.InitialLogin
or RuntimeSetPositionOperationKind.LocalAuthoritative =>
RuntimePositionEntityKind.LocalPlayer,
RuntimeSetPositionOperationKind.ProjectileAuthoritative =>
RuntimePositionEntityKind.Projectile,
_ => RuntimePositionEntityKind.Remote,
};
private RuntimeGenerationToken CurrentGeneration() => _generation?.Invoke() ?? default;
}