Campaign P remaining-physics-divergence, placement cutover slice C3c
(docs/plans/2026-08-02-placement-cutover.md). Both production hosts now
register every initial Create through the residence + continuation-
executor + first-entry-conductor machinery (C0-C3b):
- Graphical (route 1): RegisterEntityWithInitialResidence at Create; the
shared RuntimeFirstEntryDriveController pumps both conductors from the
placement-receipt flow; MaterializeProjection and RebucketLiveEntity
are presentation-only while a residence is ACTIVE (ExecutorCompleted is
the presentation-binding receipt); post-residence entities take the
full legacy path including the prepare_to_enter_world clock edges.
PlayerModeController attaches presentation to the Runtime-published
controller; its legacy resolve/step-heights/host-construction path is
deleted; presentation-only rollback (retail has no entry-flow rollback).
- Headless (route 8): OnSpawned registers with residence when a drive
exists; content-less sessions keep the pre-flip direct registration;
SynchronizeLocalPlayer/CreateController/ApplySetupStepHeights deleted;
prepared-collision read failure is a typed AwaitingCollisionSource
retry; far remotes outside the service window complete celless.
- RuntimeLocalPlayerMovementState.Controller setter sealed internal; all
controller mutation flows through the publication lifecycle.
Fix slices landed within this cutover, each dual-gated:
- F1: live movement-stat/server-physics application routed through the
Runtime ownership seam (post-logout ingest crash on the retired
controller eliminated; RuntimeMovementSkillProjection deleted).
- F2: login activation wedge - collision-admission prefix gate factored
out of the seal (reentrant-commit RejectedAuthority), rearm generation
identity corrected, PlayerModeAutoEntry requires the Runtime-published
controller (world reveal can no longer seal unmaterialized).
- F3: landblock-prefix 0-sentinel replaced by explicit absent-id guards;
map-corner landblocks (grid row/col 0) fully legal through admission,
park/rearm/retire, quiescence, and outdoor shadow seeds.
- F5: local-player first-entry ground contact seeded by the shared
SpawnPlacementSettler (moved App->Core) at FinalizeActivation - the
retail first-gravity-frame touch (enter_world 0x00516170 carries no
seed); the legacy unconditional force-seed is overwritten by a real
floor-found contact; airborne spawns stay airborne; outbound contact
bit verified end-to-end. Fixes the standing-cast 'You can't do that
while in the air!' rejections.
- R1 (dual-review round): login constraint leash armed at the committed
placement (HandleReceivedPosition 0x00453FD0 analog); register rows
AD-61 (settle-timing compression now covering the local player) and
AD-42 (repointed off the deleted resolve split) in this commit;
residence-conversion owner API; wire-landblock guards; drive-pending
ledger in IsConverged; route attach/detach latch; executor-drain drift
model documented + source-pinned.
Gates: Runtime 1,003, App 4,039/3 skips, Headless 79, complete solution
10,816/0 failed/4 skips (Release, -m:1); connected lifecycle/reconnect
gate PASS (logs/connected-world-gate-20260802-175401; graceful exits,
world-visible, zero airborne rejections). The nine-stop soak remains red
for the pre-existing 6b28ff99 whole-world collision-clone throughput
regression (attributed with evidence; scheduled as its own slice before
C5). Dual Opus reviews (retail-conformance + adversarial): delta PASS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1322 lines
54 KiB
C#
1322 lines
54 KiB
C#
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 readonly record struct RuntimeInitialCreateResidenceToken(
|
|
RuntimeEntityKey Entity,
|
|
ulong LeaseId,
|
|
ulong SessionLifetimeVersion,
|
|
ulong PositionAuthorityVersion,
|
|
ulong CreateIntegrationVersion,
|
|
ulong SourcePlacementCommitVersion)
|
|
{
|
|
internal bool IsValid => Entity.LocalEntityId != 0u
|
|
&& LeaseId != 0UL
|
|
&& PositionAuthorityVersion != 0UL
|
|
&& CreateIntegrationVersion != 0UL;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exact, presentation-free initial CreateObject residence authority. The
|
|
/// accepted wire frame remains on the canonical record while FullCell stays
|
|
/// zero until the authored Runtime SetPosition operation commits.
|
|
/// </summary>
|
|
internal readonly record struct RuntimeInitialCreateResidenceLease(
|
|
RuntimeInitialCreateResidenceToken Token,
|
|
RuntimeAuthoritativePositionRoute Route,
|
|
RuntimeEntityPlacementToken Placement,
|
|
WorldSession.EntitySpawn InitialCreate,
|
|
ImmutableArray<RuntimeInitialCreateResidenceContinuation> Continuations)
|
|
{
|
|
internal bool IsValid => Token.IsValid
|
|
&& Route.Accepted
|
|
&& Route.Authority.Entity == Token.Entity
|
|
&& InitialCreate.Guid != 0u
|
|
&& InitialCreate.InstanceSequence == Token.Entity.Incarnation
|
|
&& !Continuations.IsDefault
|
|
&& HasValidContinuationChain()
|
|
&& (!Route.PerformsSetPosition
|
|
|| Placement.IsValid
|
|
&& Placement.Entity == Token.Entity);
|
|
|
|
private bool HasValidContinuationChain()
|
|
{
|
|
uint ownerGuid = InitialCreate.Guid;
|
|
for (int index = 0; index < Continuations.Length; index++)
|
|
{
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Continuations[index];
|
|
if (!continuation.IsValid
|
|
|| continuation.Sequence != (ulong)index + 1UL
|
|
|| continuation.InstanceSequence != Token.Entity.Incarnation
|
|
|| continuation.Actions.Any(
|
|
action => action.OwnerGuid != ownerGuid))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal enum RuntimeInitialCreateContinuationKind : byte
|
|
{
|
|
SameIncarnationCreate,
|
|
ObjDesc,
|
|
Parent,
|
|
Pickup,
|
|
Position,
|
|
Movement,
|
|
State,
|
|
Vector,
|
|
}
|
|
|
|
internal enum RuntimeInitialCreateTailActionKind : byte
|
|
{
|
|
/// <summary>
|
|
/// Isolated AP-119 compatibility behavior. Retail's equal-generation
|
|
/// Create tail starts at ObjDesc and does not call set_description again.
|
|
/// </summary>
|
|
PreTailDescriptionAdaptation,
|
|
ObjDesc,
|
|
CreateParent,
|
|
Parent,
|
|
Pickup,
|
|
Position,
|
|
Movement,
|
|
State,
|
|
Vector,
|
|
WeenieDescription,
|
|
ResidentCellCleanup,
|
|
}
|
|
|
|
internal readonly record struct RuntimeInitialCreateTailAction(
|
|
RuntimeInitialCreateTailActionKind Kind,
|
|
uint OwnerGuid,
|
|
PhysicsSpawnData? Description = null,
|
|
ObjDescEvent.Parsed? ObjDesc = null,
|
|
CreateParentUpdate? CreateParent = null,
|
|
ParentEvent.Parsed? Parent = null,
|
|
PickupEvent.Parsed? Pickup = null,
|
|
WorldSession.EntityPositionUpdate? Position = null,
|
|
WorldSession.EntityMotionUpdate? Movement = null,
|
|
SetState.Parsed? State = null,
|
|
VectorUpdate.Parsed? Vector = null,
|
|
WorldSession.EntitySpawn? WeenieDescription = null,
|
|
RuntimeAcceptedPositionSource PositionSource =
|
|
RuntimeAcceptedPositionSource.Unknown,
|
|
PositionTimestampDisposition PositionDisposition =
|
|
PositionTimestampDisposition.Rejected,
|
|
ushort PreviousTeleportSequence = 0,
|
|
AcceptedPhysicsTimestamps AcceptedTimestamps = default,
|
|
bool AppliesMovementPayload = false,
|
|
bool RetainMovementPayload = true,
|
|
bool HasTimestampMutation = false)
|
|
{
|
|
internal bool IsStructurallyValid => HasExclusivePayload()
|
|
&& Kind switch
|
|
{
|
|
RuntimeInitialCreateTailActionKind.PreTailDescriptionAdaptation =>
|
|
Description is not null,
|
|
RuntimeInitialCreateTailActionKind.ObjDesc => ObjDesc is { } objDesc
|
|
&& objDesc.Guid == OwnerGuid,
|
|
RuntimeInitialCreateTailActionKind.CreateParent =>
|
|
CreateParent is { } createParent
|
|
&& createParent.ChildGuid == OwnerGuid,
|
|
RuntimeInitialCreateTailActionKind.Parent => Parent is { } parent
|
|
&& parent.ChildGuid == OwnerGuid,
|
|
RuntimeInitialCreateTailActionKind.Pickup => Pickup is { } pickup
|
|
&& pickup.Guid == OwnerGuid,
|
|
RuntimeInitialCreateTailActionKind.Position => Position is { } position
|
|
&& position.Guid == OwnerGuid
|
|
&& PositionSource is RuntimeAcceptedPositionSource.PositionEvent
|
|
or RuntimeAcceptedPositionSource.SameIncarnationCreate
|
|
&& (PositionDisposition is PositionTimestampDisposition.Apply
|
|
or PositionTimestampDisposition.ForcePosition
|
|
|| PositionDisposition is PositionTimestampDisposition.Rejected
|
|
&& HasTimestampMutation),
|
|
RuntimeInitialCreateTailActionKind.Movement => Movement is { } movement
|
|
&& movement.Guid == OwnerGuid
|
|
&& (AppliesMovementPayload || HasTimestampMutation),
|
|
RuntimeInitialCreateTailActionKind.State => State is { } state
|
|
&& state.Guid == OwnerGuid,
|
|
RuntimeInitialCreateTailActionKind.Vector => Vector is { } vector
|
|
&& vector.Guid == OwnerGuid,
|
|
RuntimeInitialCreateTailActionKind.WeenieDescription =>
|
|
WeenieDescription is { } weenie
|
|
&& weenie.Guid == OwnerGuid,
|
|
RuntimeInitialCreateTailActionKind.ResidentCellCleanup => true,
|
|
_ => false,
|
|
};
|
|
|
|
internal bool MatchesInstance(ushort instanceSequence) => Kind switch
|
|
{
|
|
RuntimeInitialCreateTailActionKind.PreTailDescriptionAdaptation =>
|
|
Description is { } description
|
|
&& description.Timestamps.Instance == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.ObjDesc =>
|
|
ObjDesc is { } objDesc
|
|
&& objDesc.InstanceSequence == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.CreateParent =>
|
|
CreateParent is { } createParent
|
|
&& createParent.ChildInstanceSequence == instanceSequence,
|
|
// ParentEvent carries only the parent's INSTANCE_TS. Acceptance
|
|
// already exact-gated the child's current incarnation; preserve that
|
|
// proof in AcceptedTimestamps for the later no-regate executor.
|
|
RuntimeInitialCreateTailActionKind.Parent =>
|
|
AcceptedTimestamps.Instance == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.Pickup =>
|
|
Pickup is { } pickup
|
|
&& pickup.InstanceSequence == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.Position =>
|
|
Position is { } position
|
|
&& position.InstanceSequence == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.Movement =>
|
|
Movement is { } movement
|
|
&& movement.InstanceSequence == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.State =>
|
|
State is { } state
|
|
&& state.InstanceSequence == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.Vector =>
|
|
Vector is { } vector
|
|
&& vector.InstanceSequence == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.WeenieDescription =>
|
|
WeenieDescription is { } weenie
|
|
&& weenie.InstanceSequence == instanceSequence,
|
|
RuntimeInitialCreateTailActionKind.ResidentCellCleanup => true,
|
|
_ => false,
|
|
};
|
|
|
|
private bool HasExclusivePayload()
|
|
{
|
|
int payloadCount = (Description is null ? 0 : 1)
|
|
+ (ObjDesc is null ? 0 : 1)
|
|
+ (CreateParent is null ? 0 : 1)
|
|
+ (Parent is null ? 0 : 1)
|
|
+ (Pickup is null ? 0 : 1)
|
|
+ (Position is null ? 0 : 1)
|
|
+ (Movement is null ? 0 : 1)
|
|
+ (State is null ? 0 : 1)
|
|
+ (Vector is null ? 0 : 1)
|
|
+ (WeenieDescription is null ? 0 : 1);
|
|
return Kind is RuntimeInitialCreateTailActionKind.ResidentCellCleanup
|
|
? payloadCount == 0
|
|
: payloadCount == 1;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// One immutable inbound envelope which arrived while retail's synchronous
|
|
/// CreateObject critical section was virtualized by an asynchronous initial
|
|
/// SetPosition. Envelopes are never coalesced. A same-incarnation Create is
|
|
/// one atomic envelope whose ordered tail is expanded only by the executor.
|
|
/// Position retains the raw PositionPack and is classified only at the FIFO
|
|
/// head, after every earlier envelope has committed.
|
|
/// </summary>
|
|
internal readonly record struct RuntimeInitialCreateResidenceContinuation(
|
|
ulong Sequence,
|
|
RuntimeInitialCreateContinuationKind Kind,
|
|
ushort InstanceSequence,
|
|
RuntimeAcceptedPositionSource PositionSource,
|
|
ImmutableArray<RuntimeInitialCreateTailAction> Actions)
|
|
{
|
|
internal bool IsValid => Sequence != 0UL
|
|
&& !Actions.IsDefaultOrEmpty
|
|
&& Actions.All(static action => action.IsStructurallyValid)
|
|
&& HasMatchingInstances()
|
|
&& HasValidShape();
|
|
|
|
private bool HasMatchingInstances()
|
|
{
|
|
for (int index = 0; index < Actions.Length; index++)
|
|
{
|
|
if (!Actions[index].MatchesInstance(InstanceSequence))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool HasValidShape()
|
|
{
|
|
if (Kind is not RuntimeInitialCreateContinuationKind.SameIncarnationCreate)
|
|
{
|
|
if (Actions.Length != 1)
|
|
return false;
|
|
RuntimeInitialCreateTailAction action = Actions[0];
|
|
RuntimeInitialCreateTailActionKind expected = Kind switch
|
|
{
|
|
RuntimeInitialCreateContinuationKind.ObjDesc =>
|
|
RuntimeInitialCreateTailActionKind.ObjDesc,
|
|
RuntimeInitialCreateContinuationKind.Parent =>
|
|
RuntimeInitialCreateTailActionKind.Parent,
|
|
RuntimeInitialCreateContinuationKind.Pickup =>
|
|
RuntimeInitialCreateTailActionKind.Pickup,
|
|
RuntimeInitialCreateContinuationKind.Position =>
|
|
RuntimeInitialCreateTailActionKind.Position,
|
|
RuntimeInitialCreateContinuationKind.Movement =>
|
|
RuntimeInitialCreateTailActionKind.Movement,
|
|
RuntimeInitialCreateContinuationKind.State =>
|
|
RuntimeInitialCreateTailActionKind.State,
|
|
RuntimeInitialCreateContinuationKind.Vector =>
|
|
RuntimeInitialCreateTailActionKind.Vector,
|
|
_ => throw new InvalidOperationException(
|
|
$"Unsupported initial-Create continuation kind {Kind}."),
|
|
};
|
|
return action.Kind == expected
|
|
&& (Kind is RuntimeInitialCreateContinuationKind.Position
|
|
? PositionSource is RuntimeAcceptedPositionSource.PositionEvent
|
|
&& action.PositionSource == PositionSource
|
|
: PositionSource is RuntimeAcceptedPositionSource.Unknown);
|
|
}
|
|
|
|
if (Actions.Length < 2
|
|
|| Actions[^2].Kind
|
|
is not RuntimeInitialCreateTailActionKind.WeenieDescription
|
|
|| Actions[^1].Kind
|
|
is not RuntimeInitialCreateTailActionKind.ResidentCellCleanup)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool hasPosition = Actions.Any(
|
|
static action => action.Kind
|
|
is RuntimeInitialCreateTailActionKind.Position);
|
|
if (hasPosition
|
|
? PositionSource
|
|
is not RuntimeAcceptedPositionSource.SameIncarnationCreate
|
|
: PositionSource is not RuntimeAcceptedPositionSource.Unknown)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int previousStage = -1;
|
|
int positionBranchCount = 0;
|
|
for (int index = 0; index < Actions.Length; index++)
|
|
{
|
|
RuntimeInitialCreateTailAction action = Actions[index];
|
|
int stage = SameCreateStage(action.Kind);
|
|
if (stage <= previousStage)
|
|
return false;
|
|
if (action.Kind is RuntimeInitialCreateTailActionKind.Position
|
|
&& action.PositionSource != PositionSource)
|
|
{
|
|
return false;
|
|
}
|
|
if (action.Kind is RuntimeInitialCreateTailActionKind.CreateParent
|
|
or RuntimeInitialCreateTailActionKind.Pickup
|
|
or RuntimeInitialCreateTailActionKind.Position)
|
|
{
|
|
positionBranchCount++;
|
|
}
|
|
previousStage = stage;
|
|
}
|
|
bool hasPhysicsDescription = Actions.Any(
|
|
static action => action.Kind
|
|
is RuntimeInitialCreateTailActionKind
|
|
.PreTailDescriptionAdaptation);
|
|
return hasPhysicsDescription
|
|
? positionBranchCount <= 1
|
|
: positionBranchCount == 0;
|
|
}
|
|
|
|
private static int SameCreateStage(RuntimeInitialCreateTailActionKind kind) =>
|
|
kind switch
|
|
{
|
|
RuntimeInitialCreateTailActionKind.PreTailDescriptionAdaptation => 0,
|
|
RuntimeInitialCreateTailActionKind.ObjDesc => 1,
|
|
RuntimeInitialCreateTailActionKind.CreateParent
|
|
or RuntimeInitialCreateTailActionKind.Pickup
|
|
or RuntimeInitialCreateTailActionKind.Position => 2,
|
|
RuntimeInitialCreateTailActionKind.Movement => 3,
|
|
RuntimeInitialCreateTailActionKind.State => 4,
|
|
RuntimeInitialCreateTailActionKind.Vector => 5,
|
|
RuntimeInitialCreateTailActionKind.WeenieDescription => 6,
|
|
RuntimeInitialCreateTailActionKind.ResidentCellCleanup => 7,
|
|
_ => int.MinValue,
|
|
};
|
|
}
|
|
|
|
internal readonly record struct RuntimeInitialCreateResidenceAdoptionToken(
|
|
RuntimeEntityKey Entity,
|
|
ulong LeaseId,
|
|
ulong AdoptionId,
|
|
ulong SessionLifetimeVersion,
|
|
ulong Revision)
|
|
{
|
|
internal bool IsValid => Entity.LocalEntityId != 0u
|
|
&& LeaseId != 0UL
|
|
&& AdoptionId != 0UL
|
|
&& Revision != 0UL;
|
|
}
|
|
|
|
internal enum RuntimeInitialCreateResidenceCompletionStatus : byte
|
|
{
|
|
Completed,
|
|
PendingPlacement,
|
|
RejectedToken,
|
|
RejectedAuthority,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Result of <see cref="RuntimeInitialCreateResidenceState.ConsumeExecuted"/>,
|
|
/// the executor-only release that supersedes the host's
|
|
/// <see cref="RuntimeInitialCreateResidenceState.AcknowledgeAdoption"/> once
|
|
/// the initial placement has been adopted.
|
|
/// </summary>
|
|
internal enum RuntimeInitialCreateResidenceExecutorReleaseStatus : byte
|
|
{
|
|
Released,
|
|
Revised,
|
|
RejectedToken,
|
|
RejectedAuthority,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exact post-residence receipt. A local graphical or no-window host may run
|
|
/// the retail after-enter teleport suffix only when this receipt carries
|
|
/// <see cref="RuntimeTeleportHookPhase.AfterEnterWorld"/>.
|
|
/// </summary>
|
|
internal readonly record struct RuntimeInitialCreateResidenceReceipt(
|
|
RuntimeInitialCreateResidenceToken Token,
|
|
RuntimeTeleportHookPhase TeleportHookPhase,
|
|
RuntimePlacementProjectionToken Projection,
|
|
uint FullCellId,
|
|
ulong PlacementCommitVersion,
|
|
RuntimeInitialCreateResidenceAdoptionToken Adoption,
|
|
ImmutableArray<RuntimeInitialCreateResidenceContinuation> Continuations);
|
|
|
|
internal readonly record struct RuntimeInitialCreateResidenceOwnershipSnapshot(
|
|
int ActiveLeaseCount,
|
|
int PendingAdoptionCount,
|
|
ulong LastLeaseId)
|
|
{
|
|
internal bool IsConverged => ActiveLeaseCount == 0
|
|
&& PendingAdoptionCount == 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Round 4 R4-4: field-masked precision for
|
|
/// <see cref="RuntimeInitialCreateResidenceState.AdvanceExecutorBaseline"/>.
|
|
/// The blanket four-field re-sync the executor previously called after
|
|
/// EVERY apply silently absorbed an external race on whichever field(s) a
|
|
/// given apply did NOT itself move - e.g. an ObjDesc/Movement/State/Vector
|
|
/// apply never touches PositionAuthorityVersion/CreateIntegrationVersion/
|
|
/// FullCellId/PlacementCommitVersion, so blanket-resyncing all four there
|
|
/// would mask a genuine concurrent bump to one of them instead of letting
|
|
/// the next <see cref="RuntimeInitialCreateResidenceState.IsCompletedCurrent"/>
|
|
/// check catch it. Each caller now passes exactly the field(s) its OWN
|
|
/// mutation moved.
|
|
/// </summary>
|
|
[Flags]
|
|
internal enum RuntimeExecutorBaselineFields : byte
|
|
{
|
|
None = 0,
|
|
PositionAuthorityVersion = 1 << 0,
|
|
CreateIntegrationVersion = 1 << 1,
|
|
FullCellId = 1 << 2,
|
|
PlacementCommitVersion = 1 << 3,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Owns only initial CreateObject residence leases. DAT lookup, body creation,
|
|
/// and presentation stay outside this owner; their immutable preparation is
|
|
/// submitted through the lease's canonical Runtime SetPosition token.
|
|
/// </summary>
|
|
internal sealed class RuntimeInitialCreateResidenceState
|
|
{
|
|
private sealed class Entry
|
|
{
|
|
internal required RuntimeEntityRecord Record { get; init; }
|
|
internal required RuntimeInitialCreateResidenceLease Lease { get; set; }
|
|
}
|
|
|
|
private sealed class CompletedEntry
|
|
{
|
|
internal required RuntimeEntityRecord Record { get; init; }
|
|
internal required RuntimeInitialCreateResidenceLease Lease { get; set; }
|
|
internal required RuntimeInitialCreateResidenceReceipt Receipt { get; set; }
|
|
|
|
/// <summary>
|
|
/// True once the continuation executor has consumed the initial
|
|
/// placement's acknowledged completion through
|
|
/// <see cref="AdoptCompletedPlacement"/>. A retained
|
|
/// <c>_acknowledgedPlacementCompletions</c> entry on
|
|
/// <see cref="RuntimeSetPositionState"/> blocks EVERY later placement
|
|
/// begin for the same key (see
|
|
/// <see cref="RuntimeSetPositionState.BeginAcceptedPlacementCore"/>'s
|
|
/// <c>HasRetainedCompletion</c> guard) — a Position continuation could
|
|
/// never start its own authored placement while the initial one still
|
|
/// sits unconsumed. Adoption resolves that deadlock by consuming the
|
|
/// proof exactly once, while this flag keeps the completed entry
|
|
/// itself "current" for placement-tracking purposes even though the
|
|
/// placement token is no longer separately tracked.
|
|
/// </summary>
|
|
internal bool PlacementAdopted { get; set; }
|
|
|
|
/// <summary>
|
|
/// Executor-tracked baseline for the four version/cell fields
|
|
/// <see cref="IsCompletedCurrent"/> compares against the LIVE record.
|
|
/// Seeded from <see cref="Receipt"/>'s own (frozen, identity-matching)
|
|
/// <c>Token</c>/<c>FullCellId</c>/<c>PlacementCommitVersion</c> at the
|
|
/// moment <see cref="Complete"/> first produces this entry, then kept
|
|
/// in sync by <see cref="AdvanceExecutorBaseline"/> every time the
|
|
/// continuation executor legitimately advances one of them while
|
|
/// applying a retained continuation. <see cref="Receipt"/>.Token
|
|
/// itself must NEVER be rebaselined — a caller (the executor) always
|
|
/// re-presents the SAME original token instance on every retry, and
|
|
/// <see cref="Complete"/>'s own token-identity match
|
|
/// (<c>completed.Receipt.Token == token</c>) depends on that struct
|
|
/// staying byte-identical. Splitting "identity" (the frozen token)
|
|
/// from "expected current value" (these fields) is what lets the
|
|
/// executor's own sequential mutations keep the entry current
|
|
/// without the residence mistaking its own controlled progress for
|
|
/// an external race - see the 2026-08-01 admission handoff's own
|
|
/// warning about exactly this risk.
|
|
/// </summary>
|
|
internal ulong ExpectedPositionAuthorityVersion { get; set; }
|
|
internal ulong ExpectedCreateIntegrationVersion { get; set; }
|
|
internal uint ExpectedFullCellId { get; set; }
|
|
internal ulong ExpectedPlacementCommitVersion { get; set; }
|
|
}
|
|
|
|
private readonly RuntimeEntityDirectory _entities;
|
|
private readonly RuntimeSetPositionState _setPosition;
|
|
private readonly Dictionary<RuntimeEntityKey, Entry> _entries = [];
|
|
private readonly Dictionary<RuntimeEntityKey, CompletedEntry> _completed = [];
|
|
private Func<RuntimeGenerationToken>? _generation;
|
|
private readonly List<Action<RuntimeEntityKey>> _retirementNotifications = [];
|
|
private ulong _nextLeaseId;
|
|
|
|
internal RuntimeInitialCreateResidenceState(
|
|
RuntimeEntityDirectory entities,
|
|
RuntimeSetPositionState setPosition)
|
|
{
|
|
_entities = entities ?? throw new ArgumentNullException(nameof(entities));
|
|
_setPosition = setPosition
|
|
?? throw new ArgumentNullException(nameof(setPosition));
|
|
}
|
|
|
|
internal void BindGeneration(Func<RuntimeGenerationToken> generation)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(generation);
|
|
if (_generation is not null)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"The initial Create residence generation source is already bound.");
|
|
}
|
|
_generation = generation;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Round 3 B3: the ONE choke point every residence retirement path -
|
|
/// <see cref="Retire(Entry)"/>, <see cref="Retire(CompletedEntry)"/>,
|
|
/// <see cref="Forget"/>, and <see cref="Clear"/> - notifies through,
|
|
/// regardless of which caller (a host query, a staleness check inside
|
|
/// this class, or the continuation executor itself) triggered the
|
|
/// retirement. Without this, a residence retired by a path OTHER than
|
|
/// the executor's own <c>DiscardProgress</c> call (e.g. a host's
|
|
/// <see cref="TryGetTransaction"/> silently discovering staleness) would
|
|
/// leave the executor's progress AND its separately-tracked pending
|
|
/// continuation placement token orphaned - this class owns no reference
|
|
/// to the executor type, so the lifetime binds a plain delegate here
|
|
/// instead. Multicast (ordered invocation list, registration order) so a
|
|
/// second independent per-key owner (e.g. the local-player first-entry
|
|
/// conductor) can subscribe to the SAME retirements the executor already
|
|
/// does, without either overwriting the other's binding.
|
|
/// </summary>
|
|
internal void BindRetirementNotification(Action<RuntimeEntityKey> notify)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(notify);
|
|
_retirementNotifications.Add(notify);
|
|
}
|
|
|
|
/// <summary>
|
|
/// H1: snapshots the subscriber list before invoking anything, mirroring
|
|
/// <see cref="RuntimeEntityObjectEventStream"/>'s own copy-on-write
|
|
/// dispatch precedent. A subscriber binding a NEW notification from
|
|
/// inside a retirement callback it is itself receiving (e.g. a future
|
|
/// third, runtime-bound subscriber added at C3c) must not corrupt or be
|
|
/// skipped by THIS iteration - <c>_retirementNotifications</c> is a
|
|
/// plain <see cref="List{T}"/>, so iterating it directly while
|
|
/// <see cref="BindRetirementNotification"/> appends to it mid-loop would
|
|
/// throw <see cref="InvalidOperationException"/> ("Collection was
|
|
/// modified"). <c>ToArray()</c> is the right granularity here (unlike
|
|
/// the event stream's <see cref="Volatile"/>-guarded array swap) because
|
|
/// binding only ever happens a handful of times at construction, never
|
|
/// on a hot per-frame path.
|
|
/// </summary>
|
|
private void NotifyRetirement(RuntimeEntityKey key)
|
|
{
|
|
foreach (Action<RuntimeEntityKey> notify in _retirementNotifications.ToArray())
|
|
notify(key);
|
|
}
|
|
|
|
internal bool CanAcceptCreate(WorldSession.EntitySpawn incoming)
|
|
{
|
|
bool parented = (incoming.ParentGuid
|
|
?? incoming.Physics?.Parent?.Guid)
|
|
is not null and not 0u;
|
|
bool topLevel = !parented
|
|
&& incoming.Position is { LandblockId: not 0u };
|
|
return CurrentGeneration().Value != 0UL
|
|
&& _nextLeaseId != ulong.MaxValue
|
|
&& (!topLevel
|
|
|| _setPosition.CanBeginAuthoredPlacementSequence
|
|
&& RuntimeAuthoritativePositionRouteClassifier
|
|
.IsValidCreateWirePosition(
|
|
incoming.Position!.Value));
|
|
}
|
|
|
|
internal RuntimeInitialCreateResidenceLease Begin(
|
|
RuntimeEntityRecord record,
|
|
in InboundCreateResult accepted,
|
|
bool isLocalPlayer)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (!_entities.IsCurrent(record)
|
|
|| record.Key is not { } key
|
|
|| record.FullCellId != 0u
|
|
|| accepted.Snapshot.Guid != record.ServerGuid
|
|
|| accepted.Snapshot.InstanceSequence != record.Incarnation)
|
|
{
|
|
return default;
|
|
}
|
|
|
|
RuntimeGenerationToken generation = CurrentGeneration();
|
|
RuntimePositionEntityKind entityKind = isLocalPlayer
|
|
? RuntimePositionEntityKind.LocalPlayer
|
|
: (record.FinalPhysicsState & PhysicsStateFlags.Missile) != 0
|
|
? RuntimePositionEntityKind.Projectile
|
|
: RuntimePositionEntityKind.Remote;
|
|
WorldSession.EntitySpawn snapshot = accepted.Snapshot;
|
|
// PhysicsDesc parent ownership precedes its optional position frame in
|
|
// retail set_description. A parented Create is not a top-level world
|
|
// admission merely because ACE also supplied a position payload.
|
|
RuntimeCreateResidenceKind residence =
|
|
(snapshot.ParentGuid ?? snapshot.Physics?.Parent?.Guid)
|
|
is not null and not 0u
|
|
? RuntimeCreateResidenceKind.Parented
|
|
: snapshot.Position is { LandblockId: not 0u }
|
|
? RuntimeCreateResidenceKind.TopLevel
|
|
: RuntimeCreateResidenceKind.PickedUp;
|
|
var authority = new RuntimeAuthoritativePositionAuthority(
|
|
generation,
|
|
key,
|
|
record.PositionAuthorityVersion,
|
|
snapshot.PositionSequence,
|
|
accepted.Timestamps.Teleport,
|
|
accepted.Timestamps.Teleport,
|
|
PositionTimestampDisposition.Apply);
|
|
RuntimeAuthoritativePositionRoute route =
|
|
RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
|
|
new RuntimeCreatePositionRouteRequest(
|
|
authority,
|
|
entityKind,
|
|
residence,
|
|
snapshot.Position,
|
|
new RuntimePositionPlacementFacts(
|
|
record.FinalPhysicsState,
|
|
HasAuthoredMoverShape: snapshot.SetupTableId is not null)));
|
|
return Own(record, route);
|
|
}
|
|
|
|
internal RuntimeInitialCreateResidenceLease EnqueueAccepted(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceLease prior,
|
|
RuntimeInitialCreateContinuationKind kind,
|
|
RuntimeAcceptedPositionSource positionSource,
|
|
ImmutableArray<RuntimeInitialCreateTailAction> actions)
|
|
{
|
|
var owned = ImmutableArray.CreateBuilder<
|
|
RuntimeInitialCreateTailAction>(actions.Length);
|
|
foreach (RuntimeInitialCreateTailAction action in actions)
|
|
{
|
|
owned.Add(RuntimeInitialCreateAdmissionFreezer.Freeze(action));
|
|
}
|
|
return Enqueue(
|
|
record,
|
|
prior,
|
|
new RuntimeInitialCreateResidenceContinuation(
|
|
NextSequence(prior),
|
|
kind,
|
|
record.Incarnation,
|
|
positionSource,
|
|
owned.MoveToImmutable()));
|
|
}
|
|
|
|
internal bool CanEnqueue(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceLease prior)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (record.Key is not { } key)
|
|
return false;
|
|
if (_entries.TryGetValue(key, out Entry? entry))
|
|
{
|
|
return ReferenceEquals(entry.Record, record)
|
|
&& entry.Lease.Token == prior.Token
|
|
&& IsCurrent(entry)
|
|
&& entry.Lease.Continuations.Length < int.MaxValue;
|
|
}
|
|
return _completed.TryGetValue(key, out CompletedEntry? completed)
|
|
&& ReferenceEquals(completed.Record, record)
|
|
&& completed.Lease.Token == prior.Token
|
|
&& IsCompletedCurrent(completed)
|
|
&& completed.Lease.Continuations.Length < int.MaxValue
|
|
&& completed.Receipt.Adoption.Revision < ulong.MaxValue;
|
|
}
|
|
|
|
private RuntimeInitialCreateResidenceLease Enqueue(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceLease prior,
|
|
in RuntimeInitialCreateResidenceContinuation continuation)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (!continuation.IsValid
|
|
|| continuation.InstanceSequence != record.Incarnation
|
|
|| !CanEnqueue(record, prior))
|
|
{
|
|
return default;
|
|
}
|
|
|
|
RuntimeEntityKey key = record.Key!.Value;
|
|
Entry? active = null;
|
|
CompletedEntry? completed = null;
|
|
RuntimeInitialCreateResidenceLease current;
|
|
if (_entries.TryGetValue(key, out active))
|
|
current = active.Lease;
|
|
else if (_completed.TryGetValue(key, out completed))
|
|
current = completed.Lease;
|
|
else
|
|
return default;
|
|
|
|
if (continuation.Sequence != NextSequence(current))
|
|
return default;
|
|
RuntimeInitialCreateResidenceLease revised = current with
|
|
{
|
|
Continuations = current.Continuations.Add(continuation),
|
|
};
|
|
if (active is not null)
|
|
{
|
|
active.Lease = revised;
|
|
}
|
|
else
|
|
{
|
|
completed!.Lease = revised;
|
|
RuntimeInitialCreateResidenceAdoptionToken adoption =
|
|
completed.Receipt.Adoption with
|
|
{
|
|
Revision = completed.Receipt.Adoption.Revision + 1UL,
|
|
};
|
|
completed.Receipt = completed.Receipt with
|
|
{
|
|
Adoption = adoption,
|
|
Continuations = revised.Continuations,
|
|
};
|
|
}
|
|
return revised;
|
|
}
|
|
|
|
private static ulong NextSequence(
|
|
in RuntimeInitialCreateResidenceLease lease) =>
|
|
(ulong)lease.Continuations.Length + 1UL;
|
|
|
|
internal bool TryGetTransaction(
|
|
RuntimeEntityRecord record,
|
|
out RuntimeInitialCreateResidenceLease lease)
|
|
{
|
|
if (TryGetCurrent(record, out lease))
|
|
return true;
|
|
if (record.Key is { } key
|
|
&& _completed.TryGetValue(key, out CompletedEntry? completed)
|
|
&& ReferenceEquals(completed.Record, record))
|
|
{
|
|
if (IsCompletedCurrent(completed))
|
|
{
|
|
lease = completed.Lease;
|
|
return true;
|
|
}
|
|
Retire(completed);
|
|
}
|
|
lease = default;
|
|
return false;
|
|
}
|
|
|
|
private RuntimeInitialCreateResidenceLease Own(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeAuthoritativePositionRoute route)
|
|
{
|
|
RuntimeEntityKey key = record.Key!.Value;
|
|
if (_entries.ContainsKey(key)
|
|
|| _nextLeaseId == ulong.MaxValue)
|
|
{
|
|
return default;
|
|
}
|
|
ulong leaseId = _nextLeaseId + 1UL;
|
|
|
|
RuntimeEntityPlacementToken placement = default;
|
|
if (route.PerformsSetPosition)
|
|
{
|
|
placement = _setPosition.TryBeginExclusiveAuthoredPlacement(
|
|
record,
|
|
record.PositionAuthorityVersion,
|
|
route.OperationKind);
|
|
if (!placement.IsValid)
|
|
return default;
|
|
if (!_setPosition.WatchPlacementCompletion(placement))
|
|
{
|
|
_ = _setPosition.ForgetExactPlacement(placement);
|
|
return default;
|
|
}
|
|
}
|
|
else if (!route.Accepted)
|
|
{
|
|
return default;
|
|
}
|
|
|
|
var token = new RuntimeInitialCreateResidenceToken(
|
|
key,
|
|
leaseId,
|
|
_entities.SessionLifetimeVersion,
|
|
record.PositionAuthorityVersion,
|
|
record.CreateIntegrationVersion,
|
|
record.PlacementCommitVersion);
|
|
var lease = new RuntimeInitialCreateResidenceLease(
|
|
token,
|
|
route,
|
|
placement,
|
|
RuntimeInitialCreateAdmissionFreezer.Freeze(record.Snapshot),
|
|
ImmutableArray<RuntimeInitialCreateResidenceContinuation>.Empty);
|
|
_entries.Add(key, new Entry
|
|
{
|
|
Record = record,
|
|
Lease = lease,
|
|
});
|
|
_nextLeaseId = leaseId;
|
|
return lease;
|
|
}
|
|
|
|
internal bool TryGetCurrent(
|
|
RuntimeEntityRecord record,
|
|
out RuntimeInitialCreateResidenceLease lease)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (record.Key is { } key
|
|
&& _entries.TryGetValue(key, out Entry? entry)
|
|
&& ReferenceEquals(entry.Record, record))
|
|
{
|
|
if (IsCurrent(entry))
|
|
{
|
|
lease = entry.Lease;
|
|
return true;
|
|
}
|
|
Retire(entry);
|
|
}
|
|
lease = default;
|
|
return false;
|
|
}
|
|
|
|
internal RuntimeInitialCreateResidenceCompletionStatus Complete(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceToken token,
|
|
out RuntimeInitialCreateResidenceReceipt receipt)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
receipt = default;
|
|
if (token.IsValid
|
|
&& _completed.TryGetValue(token.Entity, out CompletedEntry? completed)
|
|
&& completed.Receipt.Token == token
|
|
&& ReferenceEquals(completed.Record, record))
|
|
{
|
|
if (IsCompletedCurrent(completed))
|
|
{
|
|
receipt = completed.Receipt;
|
|
return RuntimeInitialCreateResidenceCompletionStatus.Completed;
|
|
}
|
|
Retire(completed);
|
|
return RuntimeInitialCreateResidenceCompletionStatus
|
|
.RejectedAuthority;
|
|
}
|
|
if (!token.IsValid
|
|
|| !_entries.TryGetValue(token.Entity, out Entry? entry)
|
|
|| entry.Lease.Token != token
|
|
|| !ReferenceEquals(entry.Record, record))
|
|
{
|
|
return RuntimeInitialCreateResidenceCompletionStatus.RejectedToken;
|
|
}
|
|
if (!IsCurrent(entry))
|
|
{
|
|
Retire(entry);
|
|
return RuntimeInitialCreateResidenceCompletionStatus
|
|
.RejectedAuthority;
|
|
}
|
|
|
|
RuntimeInitialCreateResidenceLease lease = entry.Lease;
|
|
RuntimePlacementProjectionToken projection = default;
|
|
if (lease.Route.PerformsSetPosition)
|
|
{
|
|
if (_setPosition.IsPlacementCurrent(lease.Placement))
|
|
{
|
|
return RuntimeInitialCreateResidenceCompletionStatus
|
|
.PendingPlacement;
|
|
}
|
|
if (!_setPosition.TryPeekAcknowledgedPlacement(
|
|
lease.Placement,
|
|
out projection)
|
|
|| projection.Entity != token.Entity
|
|
|| projection.PositionAuthorityVersion
|
|
!= token.PositionAuthorityVersion
|
|
|| projection.SessionLifetimeVersion
|
|
!= token.SessionLifetimeVersion
|
|
|| projection.ExactCellId == 0u
|
|
|| projection.ExactCellId != record.FullCellId
|
|
|| projection.PlacementCommitVersion
|
|
<= token.SourcePlacementCommitVersion
|
|
|| projection.PlacementCommitVersion
|
|
!= record.PlacementCommitVersion)
|
|
{
|
|
Retire(entry);
|
|
return RuntimeInitialCreateResidenceCompletionStatus
|
|
.RejectedAuthority;
|
|
}
|
|
}
|
|
else if (record.FullCellId != 0u)
|
|
{
|
|
Retire(entry);
|
|
return RuntimeInitialCreateResidenceCompletionStatus
|
|
.RejectedAuthority;
|
|
}
|
|
|
|
var adoption = new RuntimeInitialCreateResidenceAdoptionToken(
|
|
token.Entity,
|
|
token.LeaseId,
|
|
token.LeaseId,
|
|
token.SessionLifetimeVersion,
|
|
Revision: 1UL);
|
|
receipt = new RuntimeInitialCreateResidenceReceipt(
|
|
token,
|
|
lease.Route.TeleportHookPhase,
|
|
projection,
|
|
record.FullCellId,
|
|
record.PlacementCommitVersion,
|
|
adoption,
|
|
lease.Continuations);
|
|
_entries.Remove(token.Entity);
|
|
_completed.Add(token.Entity, new CompletedEntry
|
|
{
|
|
Record = record,
|
|
Lease = lease,
|
|
Receipt = receipt,
|
|
ExpectedPositionAuthorityVersion = token.PositionAuthorityVersion,
|
|
ExpectedCreateIntegrationVersion = token.CreateIntegrationVersion,
|
|
ExpectedFullCellId = receipt.FullCellId,
|
|
ExpectedPlacementCommitVersion = receipt.PlacementCommitVersion,
|
|
});
|
|
return RuntimeInitialCreateResidenceCompletionStatus.Completed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executor-only: re-synchronizes the completed entry's staleness
|
|
/// baseline (see <see cref="CompletedEntry.ExpectedPositionAuthorityVersion"/>
|
|
/// remarks) to the record's CURRENT live values, but ONLY for the
|
|
/// field(s) named in <paramref name="fields"/> (Round 4 R4-4). Called
|
|
/// after the continuation executor legitimately advances one or more of
|
|
/// PositionAuthorityVersion/CreateIntegrationVersion/FullCellId/
|
|
/// PlacementCommitVersion while applying a retained continuation, so a
|
|
/// LATER <see cref="Complete"/>/<see cref="IsCompletedCurrent"/> check
|
|
/// does not mistake the executor's own controlled progress for an
|
|
/// external race. Passing a field NOT actually moved by the caller's own
|
|
/// mutation would defeat the whole point - it would silently bless an
|
|
/// external race on that field instead of letting the next currency
|
|
/// check catch it - so every call site names exactly its own field(s);
|
|
/// an apply that moves none of the four tracked fields (ObjDesc,
|
|
/// Movement, State, Vector) must not call this method at all. A no-op
|
|
/// (returns false) if the token no longer matches a live completed
|
|
/// entry - the executor's own currency checks catch that condition
|
|
/// independently and this call is purely advisory bookkeeping, never a
|
|
/// source of truth by itself.
|
|
/// </summary>
|
|
internal bool AdvanceExecutorBaseline(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceToken token,
|
|
RuntimeExecutorBaselineFields fields)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (!token.IsValid
|
|
|| !_completed.TryGetValue(token.Entity, out CompletedEntry? entry)
|
|
|| !ReferenceEquals(entry.Record, record)
|
|
|| entry.Receipt.Token != token)
|
|
{
|
|
return false;
|
|
}
|
|
if ((fields & RuntimeExecutorBaselineFields.PositionAuthorityVersion) != 0)
|
|
entry.ExpectedPositionAuthorityVersion = record.PositionAuthorityVersion;
|
|
if ((fields & RuntimeExecutorBaselineFields.CreateIntegrationVersion) != 0)
|
|
entry.ExpectedCreateIntegrationVersion = record.CreateIntegrationVersion;
|
|
if ((fields & RuntimeExecutorBaselineFields.FullCellId) != 0)
|
|
entry.ExpectedFullCellId = record.FullCellId;
|
|
if ((fields & RuntimeExecutorBaselineFields.PlacementCommitVersion) != 0)
|
|
entry.ExpectedPlacementCommitVersion = record.PlacementCommitVersion;
|
|
return true;
|
|
}
|
|
|
|
internal bool AcknowledgeAdoption(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceAdoptionToken token)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (!token.IsValid)
|
|
return false;
|
|
if (!_completed.TryGetValue(token.Entity, out CompletedEntry? current)
|
|
|| !ReferenceEquals(current.Record, record)
|
|
|| current.Receipt.Adoption != token)
|
|
{
|
|
return false;
|
|
}
|
|
if (!IsCompletedCurrent(current))
|
|
{
|
|
Retire(current);
|
|
return false;
|
|
}
|
|
// Admission-only checkpoint: a completed initial placement remains
|
|
// owned until the next slice's continuation executor has drained the
|
|
// exact FIFO revision. Letting a host acknowledge here would silently
|
|
// discard accepted packets.
|
|
if (!current.Lease.Continuations.IsEmpty)
|
|
return false;
|
|
// The executor's own release path is ConsumeExecuted, not this host
|
|
// method. If the executor already adopted the placement proof
|
|
// (RuntimeInitialCreateContinuationExecutor.AdoptCompletedPlacement),
|
|
// it is gone from RuntimeSetPositionState's tracking table entirely —
|
|
// do not re-consume it a second time, just tolerate the already-
|
|
// satisfied state and fall through to the same removal every other
|
|
// caller of this host method observes.
|
|
if (current.Lease.Route.PerformsSetPosition
|
|
&& !current.PlacementAdopted
|
|
&& !_setPosition.ConsumeAcknowledgedPlacement(
|
|
current.Lease.Placement,
|
|
current.Receipt.Projection))
|
|
{
|
|
return false;
|
|
}
|
|
return _completed.Remove(token.Entity);
|
|
}
|
|
|
|
/// <summary>
|
|
/// C3c-R1 review F7: converts an ACTIVE, not-yet-placed
|
|
/// SetPosition-performing residence to the celless
|
|
/// (AwaitFreshPosition) route shape, forgetting its authored placement
|
|
/// operation. The residence entry itself stays active — the conductor's
|
|
/// next pump takes the existing celless skip-to-Execute path and
|
|
/// completes with FullCell 0, exactly like a Parented/PickedUp lease.
|
|
/// The retirement fan-out is fired to reset conductor/executor progress
|
|
/// for the key (its subscribers are pure progress reapers:
|
|
/// executor <c>DiscardProgress</c> + both conductors' <c>Forget</c>);
|
|
/// the entry itself is deliberately NOT retired. Refused once any
|
|
/// placement has committed (<c>FullCellId != 0</c>) — the entity is not
|
|
/// a far remote then.
|
|
/// </summary>
|
|
internal bool TryConvertToCellessRoute(RuntimeEntityRecord record)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (record.Key is not { } key
|
|
|| !_entries.TryGetValue(key, out Entry? entry)
|
|
|| !ReferenceEquals(entry.Record, record))
|
|
{
|
|
return false;
|
|
}
|
|
if (!IsCurrent(entry))
|
|
{
|
|
Retire(entry);
|
|
return false;
|
|
}
|
|
RuntimeInitialCreateResidenceLease lease = entry.Lease;
|
|
if (!lease.Route.PerformsSetPosition)
|
|
return true;
|
|
if (record.FullCellId != 0u)
|
|
return false;
|
|
RuntimePlacementCancellationReceipt cancellation =
|
|
_setPosition.ForgetExactPlacement(lease.Placement);
|
|
entry.Lease = lease with
|
|
{
|
|
Route = RuntimeAuthoritativePositionRouteClassifier
|
|
.ToCellessCreateRoute(lease.Route),
|
|
Placement = default,
|
|
};
|
|
_setPosition.PublishCancellation(cancellation);
|
|
NotifyRetirement(key);
|
|
return true;
|
|
}
|
|
|
|
internal bool Forget(
|
|
RuntimeEntityRecord record,
|
|
out RuntimeInitialCreateResidenceLease lease,
|
|
out RuntimePlacementCancellationReceipt cancellation)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
cancellation = default;
|
|
if (record.Key is { } key
|
|
&& _entries.TryGetValue(key, out Entry? entry)
|
|
&& ReferenceEquals(entry.Record, record)
|
|
&& _entries.Remove(key))
|
|
{
|
|
lease = entry.Lease;
|
|
cancellation = _setPosition.ForgetExactPlacement(
|
|
lease.Placement);
|
|
NotifyRetirement(key);
|
|
return true;
|
|
}
|
|
if (record.Key is { } completedKey
|
|
&& _completed.TryGetValue(
|
|
completedKey,
|
|
out CompletedEntry? completed)
|
|
&& ReferenceEquals(completed.Record, record)
|
|
&& _completed.Remove(completedKey))
|
|
{
|
|
lease = completed.Lease;
|
|
cancellation = _setPosition.ForgetExactPlacement(
|
|
lease.Placement);
|
|
NotifyRetirement(completedKey);
|
|
return true;
|
|
}
|
|
lease = default;
|
|
return false;
|
|
}
|
|
|
|
internal void Clear()
|
|
{
|
|
Entry[] active = _entries.Values.ToArray();
|
|
CompletedEntry[] completed = _completed.Values.ToArray();
|
|
var cancellations = new RuntimePlacementCancellationReceipt[
|
|
active.Length + completed.Length];
|
|
_entries.Clear();
|
|
_completed.Clear();
|
|
int cancellationCount = 0;
|
|
foreach (Entry entry in active)
|
|
{
|
|
RuntimePlacementCancellationReceipt cancellation =
|
|
_setPosition.ForgetExactPlacement(
|
|
entry.Lease.Placement);
|
|
if (cancellation.IsValid)
|
|
cancellations[cancellationCount++] = cancellation;
|
|
}
|
|
foreach (CompletedEntry entry in completed)
|
|
{
|
|
RuntimePlacementCancellationReceipt cancellation =
|
|
_setPosition.ForgetExactPlacement(
|
|
entry.Lease.Placement);
|
|
if (cancellation.IsValid)
|
|
cancellations[cancellationCount++] = cancellation;
|
|
}
|
|
for (int index = 0; index < cancellationCount; index++)
|
|
{
|
|
_setPosition.PublishCancellation(cancellations[index]);
|
|
}
|
|
foreach (Entry entry in active)
|
|
NotifyRetirement(entry.Lease.Token.Entity);
|
|
foreach (CompletedEntry entry in completed)
|
|
NotifyRetirement(entry.Receipt.Token.Entity);
|
|
}
|
|
|
|
internal RuntimeInitialCreateResidenceOwnershipSnapshot CaptureOwnership() =>
|
|
new(_entries.Count, _completed.Count, _nextLeaseId);
|
|
|
|
private bool IsCurrent(Entry entry)
|
|
{
|
|
RuntimeInitialCreateResidenceToken token = entry.Lease.Token;
|
|
bool placementCurrent = !entry.Lease.Route.PerformsSetPosition
|
|
|| _setPosition.IsPlacementCompletionTracked(
|
|
entry.Lease.Placement);
|
|
return placementCurrent
|
|
&& _entities.IsCurrent(entry.Record)
|
|
&& entry.Record.Key == token.Entity
|
|
&& _entities.SessionLifetimeVersion
|
|
== token.SessionLifetimeVersion
|
|
&& entry.Record.PositionAuthorityVersion
|
|
== token.PositionAuthorityVersion
|
|
&& entry.Record.CreateIntegrationVersion
|
|
== token.CreateIntegrationVersion
|
|
&& entry.Lease.Route.Authority.Generation
|
|
== CurrentGeneration();
|
|
}
|
|
|
|
private RuntimeGenerationToken CurrentGeneration()
|
|
{
|
|
return _generation?.Invoke() ?? default;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The staleness check every completed-entry caller shares. Compares the
|
|
/// live record against <see cref="CompletedEntry.ExpectedPositionAuthorityVersion"/>
|
|
/// et al — an executor-tracked, continuously re-synchronized baseline —
|
|
/// rather than against <see cref="RuntimeInitialCreateResidenceReceipt.Token"/>'s
|
|
/// own FROZEN admission-time fields directly. This is what lets the
|
|
/// continuation executor's own legitimate mutations
|
|
/// (AdvancePositionAuthority, AdvanceCreateAuthority, SetFullCell,
|
|
/// AdvancePlacementCommit — all driven by applying a retained
|
|
/// continuation) keep this entry current across the many
|
|
/// <see cref="Complete"/> re-entries a multi-call drain requires, while
|
|
/// still correctly detecting a genuine EXTERNAL race (anything that
|
|
/// changes one of these fields WITHOUT going through
|
|
/// <see cref="AdvanceExecutorBaseline"/>) exactly as it always did. The
|
|
/// token itself remains the untouched identity/match key -
|
|
/// <see cref="Complete"/>'s <c>completed.Receipt.Token == token</c> check
|
|
/// depends on that.
|
|
/// </summary>
|
|
private bool IsCompletedCurrent(CompletedEntry entry)
|
|
{
|
|
RuntimeInitialCreateResidenceReceipt receipt = entry.Receipt;
|
|
return _entities.IsCurrent(entry.Record)
|
|
&& entry.Record.Key == receipt.Token.Entity
|
|
&& _entities.SessionLifetimeVersion
|
|
== receipt.Token.SessionLifetimeVersion
|
|
&& entry.Record.PositionAuthorityVersion
|
|
== entry.ExpectedPositionAuthorityVersion
|
|
&& entry.Record.CreateIntegrationVersion
|
|
== entry.ExpectedCreateIntegrationVersion
|
|
&& entry.Record.FullCellId == entry.ExpectedFullCellId
|
|
&& entry.Record.PlacementCommitVersion
|
|
== entry.ExpectedPlacementCommitVersion
|
|
&& entry.Lease.Route.Authority.Generation
|
|
== CurrentGeneration()
|
|
&& receipt.Token.SessionLifetimeVersion
|
|
== receipt.Adoption.SessionLifetimeVersion
|
|
&& receipt.Token.LeaseId == receipt.Adoption.LeaseId
|
|
// A completed entry whose placement proof the executor already
|
|
// adopted remains current on the placement dimension without
|
|
// re-querying RuntimeSetPositionState: AdoptCompletedPlacement
|
|
// consumed (removed) the exact tracked token, so
|
|
// IsPlacementCompletionTracked would now report false even though
|
|
// nothing here has gone stale.
|
|
&& (!entry.Lease.Route.PerformsSetPosition
|
|
|| entry.PlacementAdopted
|
|
|| _setPosition.IsPlacementCompletionTracked(
|
|
entry.Lease.Placement));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executor-only: consumes the initial placement's acknowledged
|
|
/// completion exactly once so a later retained Position continuation can
|
|
/// begin its own authored placement for the same
|
|
/// <see cref="RuntimeEntityKey"/> (see the remarks on
|
|
/// <see cref="CompletedEntry.PlacementAdopted"/> for why this is
|
|
/// necessary). Idempotent: a retry after <see cref="PlacementAdopted"/> is
|
|
/// already true is a no-op success, never a double-consume.
|
|
/// </summary>
|
|
internal bool AdoptCompletedPlacement(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceToken token)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (!token.IsValid
|
|
|| !_completed.TryGetValue(token.Entity, out CompletedEntry? entry)
|
|
|| !ReferenceEquals(entry.Record, record)
|
|
|| entry.Receipt.Token != token)
|
|
{
|
|
return false;
|
|
}
|
|
if (entry.PlacementAdopted)
|
|
return IsCompletedCurrent(entry);
|
|
if (!IsCompletedCurrent(entry))
|
|
{
|
|
Retire(entry);
|
|
return false;
|
|
}
|
|
if (!entry.Lease.Route.PerformsSetPosition)
|
|
{
|
|
// A Parented/PickedUp lease never captured a real placement
|
|
// token; there is nothing to consume, but the tail must still be
|
|
// able to progress past this step exactly once.
|
|
entry.PlacementAdopted = true;
|
|
return true;
|
|
}
|
|
if (!_setPosition.ConsumeAcknowledgedPlacement(
|
|
entry.Lease.Placement,
|
|
entry.Receipt.Projection))
|
|
{
|
|
return false;
|
|
}
|
|
entry.PlacementAdopted = true;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executor-only release: consumes the residence entirely once the exact
|
|
/// adoption token still matches AND the caller has applied every
|
|
/// continuation through the CURRENT lease's full length. Placement
|
|
/// consumption already happened via <see cref="AdoptCompletedPlacement"/>,
|
|
/// so this does not call <see cref="RuntimeSetPositionState.ConsumeAcknowledgedPlacement"/>
|
|
/// a second time for an adopted entry — unlike the host-facing
|
|
/// <see cref="AcknowledgeAdoption"/>, which only ever runs for entries the
|
|
/// executor has not touched.
|
|
/// </summary>
|
|
internal RuntimeInitialCreateResidenceExecutorReleaseStatus ConsumeExecuted(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeInitialCreateResidenceAdoptionToken token,
|
|
ulong executedThroughSequence)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
if (!token.IsValid
|
|
|| !_completed.TryGetValue(token.Entity, out CompletedEntry? entry)
|
|
|| !ReferenceEquals(entry.Record, record)
|
|
|| entry.Receipt.Adoption.Entity != token.Entity
|
|
|| entry.Receipt.Adoption.LeaseId != token.LeaseId)
|
|
{
|
|
return RuntimeInitialCreateResidenceExecutorReleaseStatus
|
|
.RejectedToken;
|
|
}
|
|
if (!IsCompletedCurrent(entry))
|
|
{
|
|
Retire(entry);
|
|
return RuntimeInitialCreateResidenceExecutorReleaseStatus
|
|
.RejectedAuthority;
|
|
}
|
|
if (entry.Receipt.Adoption.Revision != token.Revision)
|
|
{
|
|
// A newer continuation arrived mid-drain (Enqueue bumps Revision
|
|
// in place on the SAME completed entry). The executor must
|
|
// re-fetch via Complete and drain the tail, never replay the
|
|
// already-applied prefix.
|
|
return RuntimeInitialCreateResidenceExecutorReleaseStatus.Revised;
|
|
}
|
|
if (!entry.PlacementAdopted && entry.Lease.Route.PerformsSetPosition)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Executor release requires the initial placement to have been adopted first.");
|
|
}
|
|
if ((ulong)entry.Lease.Continuations.Length != executedThroughSequence)
|
|
{
|
|
return RuntimeInitialCreateResidenceExecutorReleaseStatus
|
|
.RejectedAuthority;
|
|
}
|
|
return _completed.Remove(token.Entity)
|
|
? RuntimeInitialCreateResidenceExecutorReleaseStatus.Released
|
|
: RuntimeInitialCreateResidenceExecutorReleaseStatus
|
|
.RejectedAuthority;
|
|
}
|
|
|
|
private void Retire(Entry entry)
|
|
{
|
|
RuntimeEntityKey key = entry.Lease.Token.Entity;
|
|
_entries.Remove(key);
|
|
RuntimePlacementCancellationReceipt cancellation =
|
|
_setPosition.ForgetExactPlacement(entry.Lease.Placement);
|
|
_setPosition.PublishCancellation(cancellation);
|
|
NotifyRetirement(key);
|
|
}
|
|
|
|
private void Retire(CompletedEntry entry)
|
|
{
|
|
RuntimeEntityKey key = entry.Receipt.Token.Entity;
|
|
_completed.Remove(key);
|
|
RuntimePlacementCancellationReceipt cancellation =
|
|
_setPosition.ForgetExactPlacement(entry.Lease.Placement);
|
|
_setPosition.PublishCancellation(cancellation);
|
|
NotifyRetirement(key);
|
|
}
|
|
}
|