using System.Numerics;
using AcDream.Content;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Physics;
namespace AcDream.Runtime.Session;
///
/// C4 route 4b-1: per-entity remote-placement service-window predicate. A
/// host implements this over whatever collision-publication residency it
/// actually tracks — GpuWorldState.IsNearTier for the graphical host,
/// HeadlessCollisionNeighborhood's explicit implementation (backed by
/// its private IsCollisionCurrentlyPublished — NOT
/// ,
/// which is a pure geometry test over the requested 3x3 window and answers a
/// different question: "can this landblock EVER collision-publish", not "is
/// it collision-published right now" — B1 review fix) for headless — so
/// can
/// ask, BEFORE attempting any canonical SetPosition, whether the accepted
/// destination is one this host can actually place a remote into right now.
///
///
/// Headless is not a live consumer, stated plainly (C4 route 4b-2, review
/// fix). The headless implementation above exists and is exact, but
/// NOTHING in AcDream.Headless constructs a
/// : the graphical
/// composition root (SessionPlayerComposition) is the only
/// construction site, and
/// RuntimeLiveEntitySessionController.OnPositionUpdated returns early
/// for every non-local GUID before any remote routing could occur. Route
/// 4b-2's contract item 6 ("both hosts drive the identical Runtime entry
/// point, or the divergence is stated plainly rather than satisfied
/// vacuously") is therefore satisfied by THIS statement, not by a shared
/// caller. The far snap is a graphical-host-only path until a headless
/// remote-motion consumer exists.
///
///
public interface IRuntimeRemotePlacementServiceWindow
{
///
/// True when 's collision is currently
/// published by this host, so a canonical SetPosition into it is worth
/// attempting rather than certain to park (see
/// docs/research/2026-08-04-c4-route-4b-1-contract.md's "central
/// decision"). may be a full cell id — the
/// implementation canonicalizes to the containing landblock.
///
///
/// C4 route 4b-2 delta review: this answer is an OPTIMISATION only. It
/// cannot be a correctness gate — see
/// 's
/// CanAttemptDestination for the two Core predicates that are
/// strictly broader than any pre-flight, one of which reads a sweep
/// footprint that does not exist until the sweep has run.
///
///
bool IsWithinServiceWindow(uint landblockId);
}
///
/// Typed yields for
/// .
///
///
/// The partition this enum exists to express (C4 route 4b-2, delta review
/// MAJOR A). Retail's CPhysicsObj::SetPositionInternal @0x00515BD0
/// has exactly two shapes, and the earlier three-value enum could not tell
/// them apart:
///
///
/// - Stores. AdjustPosition yielded no
/// transition @0x00515C1D, so the resolve never ran:
/// prepare_to_leave_visibility @0x00515CDA,
/// store_position(this, arg2) @0x00515CE2,
/// CObjectMaint::GotoLostCell @0x00515CF2, transient-state clear
/// @0x00515CF7, return 0 @0x00515D07. The object MOVES to the accepted
/// destination.
/// - Does not store. The resolve DID run and refused:
/// CheckPositionInternal == 0 @0x00515C85 →
/// handle_all_collisions @0x00515CC2 →
/// return ((eax_14 - eax_14) & 2) + 2 @0x00515CD5 (2 or 4), and
/// sphere_path.curr_cell == 0 @0x00515C8F → return 3
/// @0x00515CB2. Both leave the object where it was.
///
///
///
/// is the single predicate that reads this partition; every value below
/// states which side it falls on and WHY, so a new value cannot be added
/// without deciding.
///
///
internal enum RuntimeRemotePlacementExecutionStatus : byte
{
///
/// Out of this route's scope: not a disposition this controller owns
/// (),
/// no canonical body, or no incarnation key. Stores — nothing ran.
///
///
/// The three sub-cases are not symmetric, and round 3 (correction m4)
/// states each. Not owned stores the accepted destination like any
/// other never-ran outcome. No canonical body stores nothing:
/// there is nowhere to write, which
///
/// tests directly rather than inferring from this value. No
/// incarnation key, with a body DOES write, and that is correct on
/// retail's partition — nothing ran, so the pose must advance — but it is
/// unreachable in production regardless: the far arm requires an
/// already-classified route, and
/// RuntimeAcceptedPositionRouteRequests.TryBuild refuses to build
/// one for a record whose Key is null, so no caller can hold a
/// route for a keyless record.
///
///
NotApplicable,
///
/// The central decision (see the class doc on
/// ): the pre-flight
/// declined to attempt this destination at all, so no operation was ever
/// begun and the canonical resolve never ran.
///
///
/// Stores. This is exactly retail's no-transition branch — "no cell
/// for it here", never "do not move". See
/// .
///
///
Refused,
///
///
/// refused, or this controller retained a preparation retry.
///
///
/// Round 3 (correction m2) — the producers this route can actually reach,
/// not the two the earlier text named. The Begin refuses when the entity
/// already holds an active operation (a concurrent placement authority —
/// portal, teleport, another host route), when it holds a RETAINED
/// COMPLETION that has not been acknowledged yet
/// (HasRetainedCompletion — ordinary right after a
/// far snap whose projection sink declined), or
/// when the record is no longer the current incarnation
/// (!IsCurrent). Its remaining refusals cannot fire from here:
/// the expected position-authority version is read off the same record
/// one statement earlier, and the accepted destination was already
/// required non-null by the pre-flight. This controller adds the fourth
/// producer: a retryable preparation status
/// (RetrySetupUnavailable/RetryWorldFrameUnavailable).
///
///
///
/// Stores in every one of those shapes: all four are decided
/// before _physics.Engine.SetPosition is called for THIS packet,
/// and retail never waits for an asset before committing the pose.
/// (
/// re-validates currency itself, so the !IsCurrent producer cannot
/// write through a superseded record.)
///
///
Contention,
///
/// Preparation refused TERMINALLY, before anything was submitted:
/// RuntimeSetPositionMoverPreparationStatus.RejectedAuthority (the
/// operation/authority pair went stale between Begin and prepare) or
/// InvalidData (the accepted frame fails
/// PositionFrameValidation, or the prepared mover could not be
/// built).
///
///
/// Stores. The engine was never called — this is the same
/// "placement never executed" state as , reached one
/// stage later. Kept distinct from
/// because that one is the opposite side of the partition.
///
///
RejectedPreparation,
/// The canonical SetPosition committed synchronously.
/// Does not store: the committed pose IS the settled pose, and it
/// is already on the canonical body.
Committed,
///
/// Core parked the placement (RuntimeSetPositionStatus.DeferredCell)
/// and this controller cancelled-and-restored it immediately rather than
/// retaining a watch — no park survives this controller.
///
///
/// Does not store. ParkDeferred has ALREADY performed
/// retail's store_position: it snaps the body to the parked result
/// before withdrawing (and RestoreParkWithdrawal deliberately
/// leaves that pose alone). For the pre-sweep quiescence park that result
/// is the accepted destination; for the post-sweep one it is the
/// COLLISION-SETTLED spherePath.CurPos, and writing the raw
/// destination over it would undo the settle (delta review N2). The two
/// DeferredCell returns that park nothing
/// (InheritedLostDeadline, WakeableLostCell) are
/// unreachable here: TryBeginExclusiveAuthoredPlacement refuses
/// whenever an operation already exists for the key, so neither flag can
/// be inherited, and this controller's own retained entries are
/// preparation retries that have not parked.
///
///
Deferred,
///
/// The canonical placement EXECUTED and did not commit:
/// RuntimeSetPositionStatus.Rejected (the engine's own sweep
/// refused the destination — PhysicsEngine.SetPosition returned a
/// non-Ok error, acdream's port of @0x00515CD5/@0x00515CB2) or
/// Cancelled (authority was displaced after the engine ran, which
/// includes the case where CommitCanonical had already SETTLED the
/// body).
///
///
/// Does not store. Retail's matching returns (2/3/4) leave the
/// object where it was, and the Cancelled-after-commit shape has a
/// freshly settled pose (contact plane, step-down) that a raw destination
/// write would destroy.
///
///
///
/// Round 3 (correction B2): "EXECUTED" is a reachability property of the
/// producers this route can reach, not a structural guarantee Core
/// enforces. RuntimeSetPositionStatus.Rejected has two other,
/// PRE-engine producers in SubmitPreparedPlacementCore, both
/// unreachable through this controller; the default: arm of
///
/// enumerates exactly what each depends on, and why the failure direction
/// if one is ever broken is the safe one.
///
///
RejectedByPlacement,
}
internal static class RuntimeRemotePlacementExecutionStatusExtensions
{
///
/// True when retail's store_position @0x00515CE2 is what this
/// outcome corresponds to: the canonical placement never reached the
/// engine, so nothing has committed a pose to the body and the remote
/// would otherwise be frozen at a superseded one. See the enum's own doc
/// for the retail partition and for why each value falls where it does.
///
///
/// Written as an exhaustive switch rather than a set test so adding a
/// value is a compile-time decision, not a silent default.
///
///
internal static bool StoresAcceptedDestination(
this RuntimeRemotePlacementExecutionStatus status) =>
status switch
{
RuntimeRemotePlacementExecutionStatus.NotApplicable => true,
RuntimeRemotePlacementExecutionStatus.Refused => true,
RuntimeRemotePlacementExecutionStatus.Contention => true,
RuntimeRemotePlacementExecutionStatus.RejectedPreparation => true,
RuntimeRemotePlacementExecutionStatus.Committed => false,
RuntimeRemotePlacementExecutionStatus.Deferred => false,
RuntimeRemotePlacementExecutionStatus.RejectedByPlacement => false,
_ => throw new ArgumentOutOfRangeException(nameof(status)),
};
}
///
/// C4 route 4b-1: the Runtime-owned, per-entity accepted-Position execution
/// seam for a remote whose classification is SetPosition (teleport /
/// cell-less — 4b-3's eventual disposition) or SetPositionSimple (far
/// snap, >=96 m — 4b-2's). Route 4a's
/// already owns the two
/// dispositions that perform no SetPosition at all
/// (NoPositionOperation/Interpolate); this controller is the
/// architectural sibling for the remaining two, built from route 2's
/// controller shape with two deliberate omissions and one deliberate
/// generalization:
///
///
/// - No ack. Retail's remote arm has no
/// SendPositionEvent — HandleReceivedPosition @0x00453FD0 calls
/// it only on the local-player FORCE_POSITION branch. There is nothing here
/// resembling route 2's PositionEventOwed/SendPositionEvent
/// pair. The separate question 4b-1's review left open is now settled
/// (C4 route 4b-2 review fix): the SetPositionStruct flag
/// SEND_POSITION_EVENT_SPF = 0x1000 — which
/// CPhysicsObj::SetPositionSimple @0x005162B0 sets as part of
/// 0x1012 @0x005162C4 and which therefore reaches
/// TryPrepareAndSubmitAuthoredPlacement here — is never read.
/// The commit path CPhysicsObj::SetPositionInternal
/// @0x00515330-@0x00515593 takes only this and the CTransition
/// and never sees the struct; the flag-reading overload @0x00515BD0 tests
/// only bit 5 (DONOTCREATECELLS_SPF = 0x20) @0x00515BF8/@0x00515C6C;
/// and no site anywhere in the decomp tests bit 12 of a
/// SetPositionStruct. It is a carried, inert flag, entirely distinct
/// from CommandInterpreter::SendPositionEvent @0x006B4770 (dispatched
/// from @0x00454091, the local-player branch 4b-1 already
/// cleared).
/// - No re-issue funnel. Route 2 re-issues a dead
/// operation because a ForcePosition is a one-shot correction ACE never
/// repeats. A remote Position is a REPEATED stream — re-issuing packet N
/// after N+1 has already merged would apply a pose the newer packet already
/// superseded. When this controller's own tracked operation dies for any
/// reason (superseded, torn down, forgotten by the entity's next accepted
/// Position), it is simply dropped: the next packet supplies the current
/// truth on its own.
/// - Per-entity, not per-session. Route 2's
/// _pending is a single slot because the local player is the only
/// entity that route ever touches. Remotes are N entities, so
/// is a per-key map and every entry is independent —
/// route 2's single-owner invariant (Begin refuses a second live entry for
/// the SAME key) is unchanged, just re-derived per entity instead of
/// globally.
///
///
///
/// The central decision — avoid the PARK where it is cheap to, never the
/// tracking.
/// 's DeferredCell park
/// withdraws the entity from the world (ParkDeferred sets
/// body.InWorld = false, suspends the object clock, and publishes a
/// Withdraw) — and RuntimeEntityObjectLifetime.TryApplyPosition
/// calls RuntimeSetPositionState.Forget on EVERY subsequent accepted
/// Position for that same entity, regardless of disposition. Because ACE
/// broadcasts a remote's Position every 100-150 ms — almost always faster
/// than the collision-generation wake this park would need to resolve on its
/// own — any DeferredCell park opened here would be cancelled by the
/// entity's own next packet long before it could wake.
/// Correcting this paragraph's earlier claim (4b-1's review ordered it
/// and it shipped uncorrected): that cancellation no longer strands the
/// entity "invisible AND intangible for the rest of the session".
/// passes restoreCancelledPark: true, and
/// RuntimeSetPositionState.RestoreParkWithdrawal rolls
/// InWorld, the object clock, and canonical residency back — the
/// AP-136 behaviour 634bc551 landed. The park is still worth avoiding
/// (a restored park re-shows the entity where retail keeps it hidden until
/// cell load, which is precisely what AP-136 records), so
/// stays — but it is an OPTIMISATION, not a
/// correctness mechanism, and it is never the reason a remote stops
/// tracking the server (see
/// 's store_position
/// fallback).
/// checks it BEFORE calling
/// ,
/// and if Core still returns DeferredCell despite the guard passing,
/// the operation is cancelled immediately rather than retained, so the
/// ledger still converges even though the guard's invariant did not hold for
/// that one packet.
///
///
///
/// Why a pre-flight structurally cannot be the correctness mechanism (C4
/// route 4b-2, delta review MAJOR B/C). A tier/residency-backed service
/// window (the graphical host's GpuWorldState.IsNearTier, headless's
/// collision-published check) is co-extensive with collision PUBLICATION in
/// both directions — verified by reading both tier-writer call sites
/// (promotion cannot read Near before collision commits) and the retirement
/// call site (the tier flips to Far as the FIRST, synchronous step of
/// retirement, strictly before collision-side withdrawal). It is NOT
/// co-extensive with a live in-place collision-prefix MUTATION, which is why
/// also reads Core's own
/// . But even
/// that pair only tests ONE prefix — the DESTINATION's — while Core's park
/// predicates are broader: ResultTouchesPrefix scans every
/// QueriedCellIds entry, a sweep footprint that provably spans
/// NEIGHBOUR landblocks (CellTransit.AddOutsideCell re-derives the
/// block id from the global lcoord and has no same-block filter). The sweep
/// footprint does not exist until the sweep has run, so no pre-flight can
/// close that half. The correctness mechanism is therefore at the source:
/// SubmitPreparedPlacementCore's two quiescence parks are RESTORABLE,
/// and ParkDeferred decides whether the rollback is safe by testing
/// the cell it will actually restore into — after the snap, against EVERY
/// live quiescence — so the rollback re-admits nothing into ANY quiescing
/// prefix, which is the only thing AP-136's blanket non-restorable scoping
/// was protecting. This guard merely shrinks the reachable set. Recorded as
/// AP-138(2).
///
///
///
/// One instance per host session route, constructed once per host process
/// and reused across reconnects exactly like
/// and
/// —
/// / assert the same
/// "session reset precedes a new route" ordering and clear any tracked
/// entries left by a torn-down session.
///
///
internal sealed class RuntimeRemotePlacementDriveController
{
private sealed class Pending
{
internal required RuntimeEntityRecord Record { get; init; }
internal required RuntimeEntityPlacementToken Token { get; init; }
internal required RuntimeAuthoritativePositionRoute Route { get; init; }
}
private readonly RuntimeEntityObjectLifetime _entityObjects;
private readonly IGameRuntimeClock _clock;
private readonly IPreparedCollisionSource _collisionSource;
private readonly IRuntimeRemotePlacementServiceWindow _serviceWindow;
///
/// Per-entity preparation-retry map (bounded to the two retryable
/// reasons — Setup
/// or world-frame data not resolved yet, NEITHER of which withdraws the
/// entity). A DeferredCell outcome never enters this map — see the
/// class doc's central decision.
///
private readonly Dictionary _pending = [];
///
/// B4 review fix: per-entity tokens whose
/// outcome was CommittedHostAcknowledgementPending and were STILL
/// live (not synchronously consumed-and-acknowledged by the production
/// placement-projection subscription inside that same call) the instant
/// returned Committed. Without this,
/// the ledger went blind the moment Committed was returned, hiding
/// exactly the declined-sink class the FIFO retry mechanism exists for.
/// Pruned lazily (self-healing) on every read — see
/// — never gates any
/// placement decision itself; Core's own _operations map remains
/// the sole authority TryBeginExclusiveAuthoredPlacement consults.
///
private readonly Dictionary
_awaitingAcknowledgement = [];
private readonly List _driveScratch = [];
///
/// C2-1 review fix (delta round): dedicated reusable scratch list for
/// 's self-heal removal
/// pass — kept separate from (owned by
/// ) so a ledger read reached while Advance
/// is mid-iteration can never corrupt its scratch buffer.
///
private readonly List _awaitingAcknowledgementScratch = [];
///
/// R9 review fix: 's own removal buffer,
/// kept separate from (owned by
/// ) for the same reason
/// is.
///
private readonly List _pendingScratch = [];
private bool _driving;
private object? _routeOwner;
internal RuntimeRemotePlacementDriveController(
RuntimeEntityObjectLifetime entityObjects,
IGameRuntimeClock clock,
IPreparedCollisionSource collisionSource,
IRuntimeRemotePlacementServiceWindow serviceWindow)
{
_entityObjects = entityObjects
?? throw new ArgumentNullException(nameof(entityObjects));
_clock = clock ?? throw new ArgumentNullException(nameof(clock));
_collisionSource = collisionSource
?? throw new ArgumentNullException(nameof(collisionSource));
_serviceWindow = serviceWindow
?? throw new ArgumentNullException(nameof(serviceWindow));
_entityObjects.RegisterRemotePlacementDriveOwnership(
CountLivePending);
// B4 review fix: a second, independent registration — multiple
// registrations sum (RegisterRemotePlacementDriveOwnership's own doc
// comment) — so the awaiting-acknowledgement dimension is visible in
// the SAME ledger without changing what _pending itself reports.
_entityObjects.RegisterRemotePlacementDriveOwnership(
CountLiveAwaitingAcknowledgement);
}
///
/// Preparation-stage retries only (see 's own doc).
/// Deliberately does NOT include —
/// that dimension is reported to the lifetime's ownership ledger via the
/// constructor's second RegisterRemotePlacementDriveOwnership call
/// (B4 review fix) and has no separate test-visible counter of its own.
/// Raw, unhealed: this is the map's literal size. The ownership ledger
/// reads instead, which prunes entries Core
/// has already retired (R9 review fix).
///
internal int PendingCount => _pending.Count;
///
/// True when route 4b owns this classification for a remote — the two
/// dispositions route 4a's
/// does NOT already handle. Everything else (Interpolate,
/// NoPositionOperation, RejectedAuthority,
/// RejectedData, AwaitFreshPosition) is out of scope here.
/// B6 review fix: Disposition alone is not exact — the classifier
/// (RuntimeAuthoritativePositionRouteClassifier.cs) assigns
/// SetPosition/SetPositionSimple at several independent
/// call sites (:256, :332, :355, :403, :459) and derives
/// OperationKind separately (:560-575) — the SAME disposition
/// covers the LOCAL PLAYER's FORCE_POSITION/teleport branches
/// (RuntimeSetPositionOperationKind.LocalAuthoritative).
/// is a parameter separate from the entity
/// record at every call site, so a mismatched pair is expressible;
/// gating on OperationKind narrows to remotes AND the local
/// player's own initial Create — OperationKind's own switch
/// (:559-570) maps InitialLogin to the LOCAL PLAYER's Create
/// ONLY; a REMOTE top-level Create maps to RemoteAuthoritative
/// exactly like a remote accepted Position does (correcting this
/// comment's earlier, wrong claim that all initial Creates map to
/// InitialLogin).
///
/// C2-4 review fix (delta round): so OperationKind alone still
/// does not exclude a remote top-level Create — ClassifyCreate
/// (:254-273) emits SetPosition + RemoteAuthoritative +
/// InitialCreateFlags (Placement|Slide) for one, while
/// ClassifyAcceptedPosition's remote branches (:401-416,
/// :454-472) always carry AuthoritativeTeleportFlags
/// (Teleport|Slide|SendPositionEvent) for the SAME disposition/
/// OperationKind pair. The Teleport bit is the exact
/// discriminator retail's own flag choice provides — Create asks for
/// placement collision, an accepted Position asks for a teleport
/// resolve — so requiring it here excludes Creates (route 4b-1 is a
/// POSITION-only route; the first-entry conductor owns every Create)
/// without excluding either remote Position shape.
///
///
/// C4 route 5 (D-P3): widened to admit
/// ProjectileAuthoritative alongside RemoteAuthoritative —
/// a live missile's teleport/cell-less and far accepted-Position
/// dispositions are disposition-identical to a remote's (the classifier
/// never branches on kind past LocalPlayer), and route 5's
/// is the sibling seam over
/// this SAME shared core ( +
/// ) — never a second pending
/// map, never a sibling controller. OwnsFarSnap and
/// OwnsTeleportPlacement are deliberately NOT widened: both remote
/// arm methods (/
/// ) require and throw without a
/// RemoteMotion, which a projectile never has.
///
///
internal static bool OwnsPlacement(RuntimeAuthoritativePositionRoute route) =>
route.OperationKind is RuntimeSetPositionOperationKind.RemoteAuthoritative
or RuntimeSetPositionOperationKind.ProjectileAuthoritative
&& route.Disposition is RuntimeAuthoritativePositionDisposition.SetPosition
or RuntimeAuthoritativePositionDisposition.SetPositionSimple
&& (route.SetPositionFlags & PhysicsSetPositionFlags.Teleport) != 0;
///
/// Mirrors and
/// : this
/// controller outlives its session routes, so the "session reset
/// precedes a new route" ordering is asserted, not assumed.
///
internal void AttachRoute(object route)
{
ArgumentNullException.ThrowIfNull(route);
if (_routeOwner is not null && !ReferenceEquals(_routeOwner, route))
{
throw new InvalidOperationException(
"A remote placement drive controller serves one session "
+ "route at a time; the prior route must be disposed "
+ "(session reset precedes a new route) before a "
+ "replacement attaches.");
}
_routeOwner = route;
}
///
/// Route-scoped teardown: abandons every tracked entry, but ONLY when
/// is the attached owner.
///
///
/// C2-1 review fix (delta round) — the prior version of this method (and
/// this comment) was wrong: it cleared and
/// as if they were pure
/// bookkeeping, citing
/// as the mirror. That is the WRONG sibling — first-entry's tracked
/// entries have an INDEPENDENT owner (the residence lease) that survives
/// route teardown on its own, so clearing first-entry's local map merely
/// stops WATCHING an operation something else still owns. These two maps
/// have no such owner: EVERY entry holds a Core operation THIS
/// controller alone began — at
/// AwaitingPreparation (already begun via
/// TryBeginExclusiveAuthoredPlacement),
/// at AwaitingCommitAcknowledgement with a published Place.
/// Clearing the local dictionary without cancelling the Core operation
/// left it live forever, pinning its landblock prefix
/// (HasOldPrefixPlacementDebt) — docs/ISSUES.md #310's unbounded
/// streaming-stall hazard, now reachable from an ordinary reconnect/
/// session-reset instead of only a stuck asset retry. The correct mirror
/// is route 2's RuntimeAcceptedPositionDriveController.DetachRoute
/// → AbandonPending, which this now matches: cancel every live
/// operation (, restoreCancelledPark: true
/// — these are cancellations of an abandoned placement INTENT, not
/// withdrawals, so any park rolls back rather than stranding the body)
/// before clearing the local maps.
///
///
internal void DetachRoute(object route)
{
ArgumentNullException.ThrowIfNull(route);
if (!ReferenceEquals(_routeOwner, route))
return;
_routeOwner = null;
RuntimeSetPositionState setPosition = _entityObjects.Physics.SetPosition;
if (_pending.Count != 0)
{
Pending[] abandoned = [.. _pending.Values];
_pending.Clear();
foreach (Pending entry in abandoned)
{
setPosition.ForgetPlacementCompletion(entry.Token);
CancelToken(setPosition, entry.Token);
}
}
if (_awaitingAcknowledgement.Count != 0)
{
RuntimeEntityPlacementToken[] abandoned =
[.. _awaitingAcknowledgement.Values];
_awaitingAcknowledgement.Clear();
foreach (RuntimeEntityPlacementToken token in abandoned)
{
setPosition.ForgetPlacementCompletion(token);
CancelToken(setPosition, token);
}
}
}
///
/// Executes an already-classified remote accepted Position against the
/// canonical Runtime SetPosition owner. 's
/// Snapshot and PositionAuthorityVersion must already reflect the merge
/// performed —
/// this method never re-merges the wire frame, and
/// must already be the result of
///
/// for the SAME packet (the shared classification builder every remote
/// caller uses — never re-derived here).
///
internal RuntimeRemotePlacementExecutionStatus TryExecuteAcceptedRemotePosition(
RuntimeEntityRecord record,
in RuntimeAuthoritativePositionRoute route)
{
ArgumentNullException.ThrowIfNull(record);
if (!OwnsPlacement(route)
|| record.PhysicsBody is null
|| record.Key is not { } key)
{
return RuntimeRemotePlacementExecutionStatus.NotApplicable;
}
RuntimeSetPositionState setPosition = _entityObjects.Physics.SetPosition;
// A retained preparation retry whose operation died some other way
// (superseded, torn down, generation change, or simply forgotten by
// an unrelated accepted Position for this same entity) must not
// block a fresh Begin for THIS packet — self-heal rather than report
// a Contention nothing is actually contending.
if (_pending.TryGetValue(key, out Pending? stale)
&& !setPosition.IsPlacementCurrent(stale.Token))
{
_pending.Remove(key);
}
CreateObject.ServerPosition? destination =
record.Snapshot.Physics?.Position ?? record.Snapshot.Position;
if (destination is not { } accepted
|| !CanAttemptDestination(setPosition, accepted.LandblockId))
{
// The central decision: skip a placement this host already knows
// will only park and be cancelled again. An OPTIMISATION — see
// CanAttemptDestination for the two Core predicates it cannot
// reproduce and where correctness actually lives — and the body
// still advances, because Refused is on the storing side of
// retail's partition.
return RuntimeRemotePlacementExecutionStatus.Refused;
}
RuntimeEntityPlacementToken token =
setPosition.TryBeginExclusiveAuthoredPlacement(
record,
record.PositionAuthorityVersion,
route.OperationKind);
if (!token.IsValid)
return RuntimeRemotePlacementExecutionStatus.Contention;
return SubmitAndResolve(record, token, route);
}
///
/// C4 route 4b-2: retail's far branch, end to end, for one remote whose
/// accepted Position already classified to
/// . This is the
/// route's FIRST production caller of
/// .
///
///
/// 005163c1 position_manager = this_1->position_manager;
/// 005163c9 if (position_manager != 0)
/// 005163cb PositionManager::StopInterpolating(position_manager);
/// 005163d9 CPhysicsObj::SetPositionSimple(this_1, arg2, 1);
/// 005163e8 return 1;
///
///
///
/// The ORDER is load-bearing and is why this lives here rather than being
/// assembled by a caller: StopInterpolating runs strictly BEFORE
/// the placement (@0x005163CB before @0x005163D9). acdream's
/// InterpolationManager queue is the analogue — a stale near
/// waypoint surviving a far snap would immediately drag the freshly
/// placed body back toward it on the next per-tick catch-up. The
/// classifier's own StopInterpolating flag
/// (RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition's
/// remote tail, StopInterpolating: !nearby) is what gates it, so
/// the retail condition is read from the route rather than restated.
///
///
///
/// The destination is NOT a parameter: it is read from the canonical
/// merged snapshot by and
/// resolved through Runtime's own accepted world frame
/// (resolveWorldOffsetFromRuntimeFrame: true). A caller's
/// separately-derived world position is not a second input — #283 proved
/// by measurement that App's streaming origin and Runtime's world frame
/// cannot disagree, and LiveWorldOriginState.EnsureAgreesWithRuntimeFrame
/// is terminal if they ever do.
///
///
///
/// ConstrainTo is deliberately NOT armed here.
/// SmartBox::HandleReceivedPosition owns retail's single arming
/// site (@0x00454272) for all three nonzero-returning branches; acdream's
/// analogue is
/// ,
/// called once by the caller after the routing block. Arming inside this
/// method would be the second site the route 4b scoping forbids.
///
///
///
/// The store_position fallback (C4 route 4b-2 review fix — the
/// one root defect). Every outcome for which the canonical placement
/// never reached the engine
/// ()
/// still advances the body to the accepted destination pose, through
/// . Without it a refused,
/// contended, or preparation-rejected far snap left the body at its stale
/// pose with the interpolation queue ALREADY cleared by the
/// StopInterpolating above — a frozen remote that the next 5-10 Hz
/// packet simply reproduces, because nothing about the refusal reason
/// changes at packet cadence.
///
///
/// This is retail, not a symptom patch.
/// CPhysicsObj::SetPositionInternal @0x00515BD0 takes the
/// no-transition branch @0x00515C1D whenever AdjustPosition
/// resolves no cell, and that branch commits the destination pose —
/// prepare_to_leave_visibility @0x00515CDA,
/// store_position(this, arg2) @0x00515CE2,
/// CObjectMaint::GotoLostCell @0x00515CF2 — and returns 0
/// (success) @0x00515D07. "This host cannot resolve a cell for the
/// destination right now" is exactly that state; retail moves the object
/// and hides it pending cell load, and never leaves it standing at a
/// superseded pose. The deleted legacy far block also always tracked.
///
///
/// The other half of retail's partition is honoured too (delta review
/// MAJOR A — correcting this comment's earlier claim that no non-commit
/// outcome could represent it). Retail's failure returns that do NOT
/// store — curr_cell == 0 @0x00515C8F/@0x00515CB2 and the blocked
/// CheckPositionInternal @0x00515C85/@0x00515CD5 — are reached
/// AFTER the resolve ran, and acdream CAN represent that:
///
/// is returned when PhysicsEngine.SetPosition itself refused, or
/// when authority was displaced after it ran (including after
/// CommitCanonical had already settled the body). Those do not
/// store, so a destination the engine's own sweep refuses no longer
/// teleports the canonical body into it, and a just-settled pose is never
/// overwritten. The residual is recorded as AP-138.
///
///
internal RuntimeRemotePlacementExecutionStatus ApplyAcceptedRemoteFarSnap(
RuntimeEntityRecord record,
RemoteMotion remote,
in RuntimeAuthoritativePositionRoute route)
{
ArgumentNullException.ThrowIfNull(record);
ArgumentNullException.ThrowIfNull(remote);
if (!RuntimeRemoteFarSnapPosition.OwnsFarSnap(route))
{
throw new ArgumentException(
"Only a remote far-snap classification (SetPositionSimple, "
+ "RemoteAuthoritative, Teleport-flagged) may be applied "
+ "through the far-snap arm; the caller must select the arm "
+ "with RuntimeRemoteFarSnapPosition.ResolveArm.",
nameof(route));
}
if (route.StopInterpolating)
remote.Interp.Clear();
RuntimeRemotePlacementExecutionStatus status =
TryExecuteAcceptedRemotePosition(record, route);
if (status.StoresAcceptedDestination())
StoreAcceptedDestinationPose(record);
return status;
}
///
/// C4 route 4b-3: retail's teleport/cell-less branch, end to end, for one
/// remote whose accepted Position already classified to
/// .
///
///
/// 00516386 if (newer_event(TELEPORT_TS) || this_1->cell == 0)
/// 005163ef CPhysicsObj::teleport_hook(this_1, edx_2);
/// 00516414 SetPositionStruct::SetFlags(&var_64, 0x1012);
/// 00516420 CPhysicsObj::SetPosition(this_1, &var_64);
/// 00516438 return 1;
///
///
///
/// The teleport hook is the CALLER's responsibility (D3) — it must run
/// BEFORE this method, regardless of what the placement then yields,
/// exactly like retail's ordering. This method does not clear the
/// interpolation queue itself: unlike the far arm, the classifier's
/// teleport branch carries StopInterpolating: false on purpose —
/// retail's clear for THIS branch lives inside teleport_hook's
/// PositionManager::StopInterpolating @0x00514EFD, not in
/// MoveOrTeleport itself.
///
///
///
/// The store_position fallback (invariant 1) is identical to the
/// far arm's: every outcome for which the canonical placement never
/// reached the engine
/// ()
/// still advances the body to the accepted destination pose. Retail
/// discards SetPosition's error and returns 1 unconditionally
/// @0x00516438 — the placement's outcome never changes whether the
/// packet "succeeded".
///
///
internal RuntimeRemotePlacementExecutionStatus ApplyAcceptedRemoteTeleport(
RuntimeEntityRecord record,
RemoteMotion remote,
in RuntimeAuthoritativePositionRoute route)
{
ArgumentNullException.ThrowIfNull(record);
ArgumentNullException.ThrowIfNull(remote);
if (!RuntimeRemoteTeleportPosition.OwnsTeleportPlacement(route))
{
throw new ArgumentException(
"Only a remote teleport/cell-less classification (SetPosition, "
+ "RemoteAuthoritative, Teleport-flagged) may be applied "
+ "through the teleport arm; the caller must select the arm "
+ "with RuntimeRemoteTeleportPosition.OwnsTeleportPlacement.",
nameof(route));
}
RuntimeRemotePlacementExecutionStatus status =
TryExecuteAcceptedRemotePosition(record, route);
if (status.StoresAcceptedDestination())
StoreAcceptedDestinationPose(record);
return status;
}
///
/// C4 route 5 (D-P2): the projectile arm over this SAME shared core. A
/// live missile carries no , so
/// /
/// are not reusable — both require one and throw without it. This is the
/// sibling seam the D-P2 design pins: same
/// +
/// core, same
/// / ledgers,
/// no second pending map, no sibling controller (trap T9).
///
///
/// Returns for every disposition this route does
/// not own: Interpolate (near, in contact) and
/// NoPositionOperation (airborne) are pinned NO-OPS — retail would
/// lazily build interpolation/leash machinery for a manager-less missile
/// (@0x005163AF / @0x00454272-@0x00510523), which acdream deliberately
/// does not construct for a ballistic body (the register row this route
/// adds); RejectedAuthority/RejectedData and an ownership
/// mismatch are SWALLOWED — write nothing, never fall through to the
/// remote tail (trap T5). A caller must not fall back to any remote arm
/// when this returns .
///
///
///
/// No velocity write (D-P5). Retail's MoveOrTeleport
/// @0x00516330 never references its velocity argument in the decompiled
/// body, and a byte-level disassembly of the PDB-paired binary
/// (@0x00516330-@0x00516438) confirms no instruction anywhere in the
/// function reads that argument's stack slot. This seam commits no
/// velocity from the Position packet at all — the Vector channel
/// ()
/// remains the sole velocity authority for a missile.
///
///
///
/// No constraint leash armed (D-P4). Unlike the remote arms, this
/// method never calls TryArmConstraintAfterOperation — the
/// classifier's projectile routes still carry
/// ConstrainPhase.AfterPositionOperation (kind-blind), but this arm
/// deliberately does not consume it, matching the pinned divergence.
///
///
///
/// Teleport hook reduction (D-P4). Of retail's six
/// teleport_hook @0x00514ED0 actions, five are structurally absent
/// for a manager-less missile (no MovementManager/
/// PositionManager/TargetManager). The sixth,
/// report_collision_end(this, 1) @0x00514F31-@0x00514620, applies
/// to any object with a collision table and runs BEFORE the placement —
/// ported here as RuntimeCollisionReportingState.LeaveWorld (the
/// exact force-end seam the 4b-3 round-2 review validated against the
/// same retail address).
///
///
///
/// Prediction is invalidated once per packet, before any body write on
/// this route (placement or the store fallback) — mirroring
/// RuntimeProjectilePhysicsUpdater's existing invalidate-before-
/// write ordering — so an in-flight split quantum straddling this packet
/// aborts at Complete rather than clobbering a canonical
/// placement. The no-op dispositions invalidate nothing: the body is
/// untouched, so a straddling quantum completing over them is correct.
///
///
internal RuntimeRemotePlacementExecutionStatus? ApplyAcceptedProjectilePosition(
RuntimeEntityRecord record,
in RuntimeAuthoritativePositionRoute route)
{
ArgumentNullException.ThrowIfNull(record);
if (route.OperationKind
is not RuntimeSetPositionOperationKind.ProjectileAuthoritative
|| record.Projectile is not RuntimeProjectile projectile
|| record.PhysicsBody is not { } body
|| !ReferenceEquals(body, projectile.Body))
{
return null;
}
// A6 fix (review round): captured BEFORE the placement dispatch,
// mirroring the deleted tail's `bool wasInWorld = body.InWorld;`
// ordering — TryExecuteAcceptedRemotePosition's canonical commit
// calls body.SnapToCell, which sets InWorld = true, so reading this
// AFTER the dispatch (as the first cut of this seam did) makes the
// re-activation branch below permanently dead on every committed
// outcome.
bool wasInWorld = body.InWorld;
RuntimeRemotePlacementExecutionStatus status;
switch (route.Disposition)
{
case RuntimeAuthoritativePositionDisposition.SetPosition:
_entityObjects.Physics.CollisionReports.LeaveWorld(record);
projectile.InvalidatePrediction();
status = TryExecuteAcceptedRemotePosition(record, route);
break;
case RuntimeAuthoritativePositionDisposition.SetPositionSimple:
// B1/B2 fix (round-2 review): retail's far branch runs
// `StopInterpolating` @0x005163C9-@0x005163CB whenever
// `position_manager != 0` — the SAME guard the remote far
// arm ports as `ApplyAcceptedRemoteFarSnap`'s
// `if (route.StopInterpolating) remote.Interp.Clear();`. A
// bare missile has no RemoteMotion so this is structurally
// inert, but the ADOPTED-BODY case (TryBind's shared-body
// branch: an ordinary remote whose Missile bit was set by a
// later State packet) carries a live Interp queue the far
// branch must clear too — the teleport hook only covers the
// SetPosition disposition.
if (route.StopInterpolating
&& record.RemoteMotion is RemoteMotion adoptedFar)
{
adoptedFar.Interp.Clear();
}
projectile.InvalidatePrediction();
status = TryExecuteAcceptedRemotePosition(record, route);
break;
default:
// Interpolate / NoPositionOperation: pinned no-op (D-P4).
// RejectedAuthority / RejectedData: swallow (T5) — the
// shared authority gate already rejected an invalid payload
// upstream; there is nothing left to route.
return null;
}
if (status.StoresAcceptedDestination())
StoreAcceptedDestinationPose(record);
if (status is not RuntimeRemotePlacementExecutionStatus.Deferred
and not RuntimeRemotePlacementExecutionStatus.RejectedByPlacement)
{
// Invariant 2: presentation advances on every committed/stored
// outcome only — Deferred/RejectedByPlacement leave the body at
// its prior (already-synced) pose.
SyncProjectilePresentation(record, projectile, body, wasInWorld);
}
return status;
}
///
/// C4 route 5 (REVISED after the review round — A6/A7/A8): the
/// J5.6-owned post-commit lifecycle tail (InWorld/Activate/shadow-sync
/// on spatial+visible, suspend on spatial+hidden, deactivate+suspend on
/// non-spatial), reduced from the deleted
/// RuntimeProjectilePhysicsUpdater.ApplyAuthoritativePosition
/// tail (former :390-422) to this controller's own seam —
/// is the caller's pre-dispatch capture
/// (A6: reading body.InWorld here, after the canonical commit's
/// own SnapToCell already forced it true, made the re-activation
/// branch permanently dead); supplies the same
/// clock source the deleted method took as an explicit
/// currentTime parameter; the world-frame offset comes from
/// (the same
/// source uses) instead of an
/// App-supplied live-center pair.
///
private void SyncProjectilePresentation(
RuntimeEntityRecord record,
RuntimeProjectile projectile,
PhysicsBody body,
bool wasInWorld)
{
if (!_entityObjects.Entities.IsCurrent(record)
|| !ReferenceEquals(record.Projectile, projectile)
|| !ReferenceEquals(record.PhysicsBody, body))
{
return;
}
RuntimePhysicsState physics = _entityObjects.Physics;
bool spatial = physics.IsSpatialProjectile(record, projectile);
bool hidden =
(record.FinalPhysicsState & PhysicsStateFlags.Hidden) != 0;
uint localId = record.LocalEntityId ?? 0u;
if (spatial && !hidden)
{
if (!wasInWorld)
{
body.LastUpdateTime = _clock.SimulationTimeSeconds;
if ((body.State & PhysicsStateFlags.Static) == 0)
body.TransientState |= TransientStateFlags.Active;
}
body.InWorld = true;
// A8 fix: #284's policy ("a frame that can never arrive is
// terminal, never silent") applies here exactly as it does to
// StoreAcceptedDestinationPose. A false result during the
// legitimate pre-local-player-Create window silently skips the
// publish (self-heals once the frame arrives); a false result
// AFTER that window is a genuinely stuck frame, and
// ThrowIfWorldFrameUnreachable escalates it instead of leaving
// the shadow silently stale forever.
if (physics.TryGetWorldFrameOffset(
record.FullCellId,
out float offsetX,
out float offsetY))
{
physics.Engine.ShadowObjects.UpdatePosition(
localId,
body.Position,
body.Orientation,
offsetX,
offsetY,
record.FullCellId,
seedCellId: record.FullCellId);
}
else
{
physics.ThrowIfWorldFrameUnreachable(record.FullCellId);
}
}
else if (spatial)
{
body.InWorld = true;
// A7 fix: retail's hidden-branch clock consumption — restored,
// matching ProjectileController.TryBind's equivalent branch
// ("consume the hidden clock so UnHide cannot replay a time
// backlog").
body.LastUpdateTime = _clock.SimulationTimeSeconds;
physics.Engine.ShadowObjects.Suspend(localId);
}
else
{
body.InWorld = false;
body.TransientState &= ~TransientStateFlags.Active;
physics.Engine.ShadowObjects.Suspend(localId);
}
}
///
/// Retail CPhysicsObj::store_position @0x00515CE2, reached from
/// SetPositionInternal's no-resolvable-cell branch @0x00515C1D.
/// Commits the SAME accepted destination
/// reads — the canonical
/// merged snapshot, resolved through Runtime's own accepted world frame,
/// which is the exact pair
/// RuntimeSetPositionMoverPreparer.TryBuild composes for a
/// committed placement (cellLocal + ShadowWorldOffset in X/Y, the
/// authored Z, the authored quaternion). A caller's separately-derived
/// wire pose is deliberately not an input here either, for the same
/// reason the committed path does not take one.
///
///
/// Currency is re-validated first (delta review MAJOR D).
/// publishes its cancellation receipt
/// SYNCHRONOUSLY, and the production placement-projection sink can delete
/// or replace this incarnation from inside that publish — so by the time
/// control returns to the record
/// may no longer be the canonical one for its GUID. Writing a pose
/// through a superseded incarnation is the exact class the R5 fix
/// introduced the rule for, and
/// RuntimeSetPositionState.RestoreParkWithdrawal already guards the
/// same way. The App-side re-validation happens only after this seam
/// returns, which is too late.
///
///
///
/// The write target is the CANONICAL body (delta review N5).
/// Retail's store_position(this, arg2) writes the CPhysicsObj's own
/// position, and acdream's canonical CPhysicsObj is
/// .
/// RemoteMotion.Body IS that instance in production, by two
/// independent routes: RuntimePhysicsState.GetOrCreateRemoteMotion
/// passes record.PhysicsBody into the constructor, and when the
/// record has no body yet SetRemoteMotion ADOPTS the component's
/// private one as canonical (InitializeNewPhysicsBody +
/// SetPhysicsBody). So the divergent pair is not constructible
/// through the production seam and this change moves no production
/// behaviour — it removes the ability to write a pose into a body the
/// canonical record does not own, which is what
/// 's
/// no-body producer would otherwise permit for a caller-supplied
/// component. Reading the record directly makes "there is no canonical
/// body" and "there is nothing to store into" the same test.
///
///
///
/// Returns false only when there is nothing to write, or nowhere to write
/// it: no canonical body, no accepted destination on the snapshot, a
/// superseded incarnation, or no Runtime world frame yet. The last case
/// reuses Core's own #284 policy —
/// makes a
/// frame that can never arrive terminal rather than silent — so the only
/// surviving false there is the genuine pre-local-player-Create window,
/// which the far arm cannot reach anyway: the classifier needs a live
/// player_distance to emit SetPositionSimple at all, and
/// that requires the local movement controller to exist.
///
///
private bool StoreAcceptedDestinationPose(RuntimeEntityRecord record)
{
if (!_entityObjects.Entities.IsCurrent(record)
|| record.PhysicsBody is not { } body)
{
return false;
}
CreateObject.ServerPosition? destination =
record.Snapshot.Physics?.Position ?? record.Snapshot.Position;
if (destination is not { } accepted)
return false;
if (!_entityObjects.Physics.TryGetWorldFrameOffset(
accepted.LandblockId,
out float worldOffsetX,
out float worldOffsetY))
{
_entityObjects.Physics.ThrowIfWorldFrameUnreachable(
accepted.LandblockId);
return false;
}
body.Position = new Vector3(
accepted.PositionX + worldOffsetX,
accepted.PositionY + worldOffsetY,
accepted.PositionZ);
body.Orientation = new Quaternion(
accepted.RotationX,
accepted.RotationY,
accepted.RotationZ,
accepted.RotationW);
return true;
}
///
/// Host cadence pump: retries a preparation-only retry status
/// (RetrySetupUnavailable/RetryWorldFrameUnavailable) by
/// re-calling the SAME prepare+submit pair, exactly like
/// 's own continuation
/// completion. Bounded, non-allocating iteration mirrors
/// 's _driveScratch
/// template. Safe to call from any host cadence point; a no-op when
/// nothing is pending.
///
internal void Advance()
{
if (_driving || _pending.Count == 0)
return;
_driving = true;
try
{
_driveScratch.Clear();
foreach (RuntimeEntityKey key in _pending.Keys)
_driveScratch.Add(key);
RuntimeSetPositionState setPosition =
_entityObjects.Physics.SetPosition;
foreach (RuntimeEntityKey key in _driveScratch)
{
if (!_pending.TryGetValue(key, out Pending? pending))
continue;
if (!setPosition.IsPlacementCurrent(pending.Token))
{
// Forgotten by some other accepted Position for this
// same entity before this retry resolved. No re-issue
// funnel for remotes: the next packet supplies current
// truth on its own.
_pending.Remove(key);
continue;
}
_pending.Remove(key);
// R3 fix (review round): a retained retry can belong to a
// projectile operation exactly as it can belong to a
// remote's — this is the SAME shared _pending map (trap T9:
// no second map), and pending.Route carries the OperationKind
// that was classified when the retry was first parked.
// Neither invariant this route pins (prediction invalidated
// before every body write; presentation advances on every
// committed/stored outcome) may hold on the direct arm only.
// Real nullable locals (not a stored bool) so the compiler
// can track definite assignment through the branches below.
RuntimeProjectile? pendingProjectile = null;
PhysicsBody? pendingBody = null;
if (pending.Route.OperationKind
is RuntimeSetPositionOperationKind.ProjectileAuthoritative
&& pending.Record.Projectile is RuntimeProjectile candidateProjectile
&& pending.Record.PhysicsBody is { } candidateBody
&& ReferenceEquals(candidateBody, candidateProjectile.Body))
{
pendingProjectile = candidateProjectile;
pendingBody = candidateBody;
}
bool pendingWasInWorld = pendingBody?.InWorld ?? false;
// B3 review fix: a retry can sit retained across many host
// cadence pumps (bounded only by how long the asset stayed
// unavailable) while its destination's collision publication
// retires out from under it. Re-check the SAME pre-flight the
// entry point uses BEFORE resubmitting — dropping (and
// cancelling the already-begun token) rather than resubmitting
// matches the entry point's own Refused semantics: no
// operation survives and the next packet supplies current
// truth on its own. Without this re-check a destination that
// fell out of the window would just keep coming back
// Contention forever (the asset source has not changed), never
// converging even though the window already knows better.
//
// Delta review N1: this path is the entry point's Refused
// semantics, so it must ALSO be the entry point's
// store_position — correcting this comment's earlier claim
// that "the entity keeps its last committed pose". A retained
// retry whose destination leaves the window is exactly the
// freeze the fallback exists to prevent, one cadence pump
// later; the pre-flight is an optimisation here for the same
// reason it is one at the entry point, and it may never be the
// reason a remote stops tracking.
CreateObject.ServerPosition? destination =
pending.Record.Snapshot.Physics?.Position
?? pending.Record.Snapshot.Position;
RuntimeRemotePlacementExecutionStatus retryStatus;
if (destination is not { } accepted
|| !CanAttemptDestination(
setPosition,
accepted.LandblockId))
{
CancelToken(setPosition, pending.Token);
pendingProjectile?.InvalidatePrediction();
StoreAcceptedDestinationPose(pending.Record);
retryStatus = RuntimeRemotePlacementExecutionStatus.Refused;
}
else
{
// B5 fix (round-2 review): invalidating BEFORE this call
// unconditionally was wrong when SubmitAndResolve itself
// re-parks (returns Contention) — that outcome writes
// NOTHING (no store, no commit), so invalidating for it
// violates invariant 4's "the no-op dispositions
// invalidate nothing" on this arm specifically (unlike
// the entry point, where StoresAcceptedDestination()
// treats Contention as a storing outcome via the
// caller's own StoreAcceptedDestinationPose — this retry
// arm does not store on a re-parked Contention, matching
// the pre-existing residual A3/round-1 already named).
// Invalidating AFTER the call instead of before is safe
// here: this method is single-threaded and synchronous,
// so a write performed inside SubmitAndResolve and the
// very next statement's invalidate are never observably
// separated by a quantum's Complete call.
retryStatus = SubmitAndResolve(
pending.Record, pending.Token, pending.Route);
if (retryStatus
is not RuntimeRemotePlacementExecutionStatus.Contention)
{
pendingProjectile?.InvalidatePrediction();
}
}
if (pendingProjectile is { } confirmedProjectile
&& pendingBody is { } confirmedBody
&& retryStatus is not RuntimeRemotePlacementExecutionStatus.Deferred
and not RuntimeRemotePlacementExecutionStatus.RejectedByPlacement)
{
SyncProjectilePresentation(
pending.Record,
confirmedProjectile,
confirmedBody,
pendingWasInWorld);
}
}
}
finally
{
_driving = false;
}
}
private RuntimeRemotePlacementExecutionStatus SubmitAndResolve(
RuntimeEntityRecord record,
in RuntimeEntityPlacementToken token,
in RuntimeAuthoritativePositionRoute route)
{
RuntimeSetPositionState setPosition = _entityObjects.Physics.SetPosition;
RuntimeSetPositionMoverPreparationStatus status =
setPosition.TryPrepareAndSubmitAuthoredPlacement(
record,
token,
route.OperationKind,
route.SetPositionFlags,
_collisionSource,
_clock.SimulationTimeSeconds,
out RuntimeSetPositionOutcome outcome,
resolveWorldOffsetFromRuntimeFrame: true);
if (status != RuntimeSetPositionMoverPreparationStatus.Prepared)
{
if (status.IsRetryable())
{
_pending[token.Entity] = new Pending
{
Record = record,
Token = token,
Route = route,
};
return RuntimeRemotePlacementExecutionStatus.Contention;
}
// Preparation refused TERMINALLY (RejectedAuthority/InvalidData).
// Nothing was submitted, so the engine never ran — this is the
// STORING side of retail's partition, one stage later than the
// pre-flight Refused. Delta review MAJOR A.
CancelToken(setPosition, token);
return RuntimeRemotePlacementExecutionStatus.RejectedPreparation;
}
switch (outcome.Status)
{
case RuntimeSetPositionStatus.CommittedHostAcknowledgementPending:
// No ack for remotes. The production placement-projection
// subscription (shared infrastructure, not owned here) has
// already applied-and-acknowledged the Place receipt
// synchronously inside the SetPosition call above, if it was
// going to — exactly like route 2's own commit branch, minus
// the ack call route 2 makes for the local player. B4 review
// fix: when the sink declined instead (host not ready — the
// exact scenario the FIFO retry mechanism exists for), the
// operation is STILL LIVE in Core's _operations map until
// some later AcknowledgeProjection retires it. Track it so
// the ledger can see that class of outstanding operation
// instead of going blind the instant this method returns.
if (setPosition.IsPlacementCurrent(token))
_awaitingAcknowledgement[token.Entity] = token;
return RuntimeRemotePlacementExecutionStatus.Committed;
case RuntimeSetPositionStatus.DeferredCell:
// Central decision: the pre-flight passed but Core still
// parked the destination — the residual the class doc's
// central-decision paragraph explains, now that the pre-flight
// is explicitly only an optimisation (it tests the DESTINATION
// prefix; Core's own post-sweep predicate also matches the
// whole swept footprint, which spans neighbouring landblocks
// and does not exist until the sweep has run). Cancel
// immediately rather than
// retain a watch: no re-issue, no park survives this
// controller, and CancelToken's restoreCancelledPark rolls the
// withdrawal back at the destination cell.
//
// ParkDeferred has already performed retail's store_position —
// it snaps the body to the parked result before withdrawing,
// and RestoreParkWithdrawal deliberately leaves that pose
// alone — so the fallback must NOT fire here. For the
// post-sweep park that result is the collision-SETTLED
// position and a raw destination write would undo the settle
// (delta review N2).
CancelToken(setPosition, token);
return RuntimeRemotePlacementExecutionStatus.Deferred;
default:
// Rejected/Cancelled. Correcting this comment's earlier claim
// that "the body never moved" (delta review MAJOR A — it was
// the claim that made the fallback fire here): the statuses
// that reach this arm are returned AFTER
// _physics.Engine.SetPosition ran. Rejected is the engine's
// own sweep refusing the destination (retail @0x00515CD5 /
// @0x00515CB2, neither of which stores), and Cancelled
// includes the shape where CommitCanonical already SETTLED
// the body and only the projection ownership was displaced.
// Neither may be overwritten with the raw accepted
// destination.
//
// Round 3 (correction B2) — that is a REACHABILITY claim, not
// a structural one, so state what it rests on. Core's
// SubmitPreparedPlacementCore has three Rejected producers
// and only the post-engine one (`!result.IsSuccessful` after
// the SetPosition call) can reach here. Its two PRE-engine
// producers are unreachable through this controller because
// each is shadowed by a check PrepareMover already made, with
// nothing reentrant in between:
// * the entry validation's identity/authority terms (token
// ownership, stage, preparation authority) re-test what
// PrepareMover's own guard clause tested one call
// earlier, and IsPreparationAuthorityCurrent catches
// all seven of its version dimensions there first — as
// RejectedAuthority, i.e. RejectedPreparation, which
// DOES store. The one stage divergence PrepareMover
// admits and SubmitPreparedPlacementCore refuses is
// AwaitingCell, which additionally requires
// DormantLocalActivation — forbidden for a record that
// has a body, and this route requires one. Its remaining
// terms (finite GameTime, matching Kind/Portal, the exact
// PreparedCommand, the velocity-version echo) are
// satisfied by construction: TryPrepareAndSubmitAuthored-
// Placement submits the very command PrepareMover just
// stored, built from the same operation, with nothing in
// between.
// * the NULL-BODY term is the one entry-validation test
// PrepareMover does NOT shadow (round 4, D3 — its guard
// clause has no body test at all). This route's own
// entry check does: TryExecuteAcceptedRemotePosition's
// `record.PhysicsBody is null → NotApplicable` above.
// The only two writers that CLEAR it are entity teardown
// (RuntimeEntityObjectLifetime's canonical leave-world
// tail) and local-player activation retirement, and each
// retires the operation as part of the same transaction —
// so a body lost in between surfaces as Cancelled, not
// as a pre-engine Rejected.
// * !IsStructurallyValid(canonicalRequest) re-tests the
// command PrepareMover just validated. The `with` between
// them only replaces fields IsStructurallyValid reads
// when WakeableLostCell is set, which requires an
// inherited park — and TryBeginExclusiveAuthoredPlacement
// refuses whenever an operation already exists for the
// key, so nothing can be inherited here.
// If a future change breaks one of those, the failure lands
// on the SAFE side: a pre-engine Rejected filed here does not
// store, so the body holds a stale pose for ONE packet (no
// operation is retained — CancelToken runs below — so the
// next 5-10 Hz packet starts clean). The opposite mistake,
// storing after the engine refused, teleports the canonical
// body into a destination the sweep just declined. That
// asymmetry is why this stays a documented invariant rather
// than a stage flag threaded out of Core: the flag would
// change the shared placement contract three shipped routes
// depend on, to make the already-safe direction slightly
// safer.
CancelToken(setPosition, token);
return RuntimeRemotePlacementExecutionStatus.RejectedByPlacement;
}
}
///
/// R9 review fix — the same self-healing read
/// already performed, for
/// . Both maps are keyed by
/// (guid + incarnation), so a reused GUID
/// produces a NEW key and cannot displace a dead incarnation's entry by
/// itself. drops such entries, but it early-returns
/// whenever nothing is pending and is a host cadence call the ledger has
/// no right to assume ran; without this the count converged only at
/// , i.e. never in-session — contract item 7's
/// GUID-reuse/incarnation dimension.
///
///
/// Removal is gated on !IsPlacementCurrent, so this can only ever
/// drop entries Core has ALREADY retired. It never cancels anything and
/// therefore cannot hide the live-operation leak
/// 's own review fix exists to prevent. The
/// disposal guard matches its sibling's, for the same
/// post-Dispose() CaptureOwnership() contract.
///
///
private int CountLivePending()
{
if (_pending.Count == 0)
return 0;
if (_entityObjects.Physics.IsDisposed)
return _pending.Count;
RuntimeSetPositionState setPosition = _entityObjects.Physics.SetPosition;
_pendingScratch.Clear();
foreach ((RuntimeEntityKey key, Pending entry) in _pending)
{
if (!setPosition.IsPlacementCurrent(entry.Token))
_pendingScratch.Add(key);
}
foreach (RuntimeEntityKey key in _pendingScratch)
_pending.Remove(key);
return _pending.Count;
}
///
/// Self-healing read: prunes every
/// entry whose token Core no longer considers current (the ack already
/// landed through whatever path — the synchronous in-call apply, a later
/// FIFO retry, or Runtime's own session-reset/generation-change teardown
/// clearing the operation outright) before returning the live count.
/// This is what lets the constructor's second
/// RegisterRemotePlacementDriveOwnership registration converge to
/// zero (B4 review fix) without this controller needing a separate
/// periodic pump for this one dictionary — every ownership-snapshot read
/// (including the exact convergence checks teardown/reset/generation
/// change assert) sees the truth as of that read.
///
///
/// C2-1 review fix (delta round), disposal safety: IsPlacementCurrent's
/// first statement is EnsureNotDisposed, which THROWS once
/// is disposed. A post-Dispose()
/// CaptureOwnership() read is the designed contract
/// (GameWindowLifetime.DisposeGameRuntime: runtime.Dispose();
/// runtime.CaptureOwnership();), so this — the first ledger provider
/// to reach into another disposable subsystem — must survive it. When
/// is already true, Core
/// itself is gone; there is nothing left to ask, so this returns
/// whatever count is STILL in the local map rather than calling into the
/// disposed state. A healthy teardown already cancelled and cleared
/// every entry via before disposal, so this
/// branch reports 0 in the healthy path and a genuine nonzero leak
/// otherwise — never an exception either way.
///
///
/// C2-1 review fix (delta round), allocation: reuses
/// instead of a per-call
/// List<RuntimeEntityKey>, mirroring 's
/// own template.
///
///
private int CountLiveAwaitingAcknowledgement()
{
if (_awaitingAcknowledgement.Count == 0)
return 0;
if (_entityObjects.Physics.IsDisposed)
return _awaitingAcknowledgement.Count;
RuntimeSetPositionState setPosition = _entityObjects.Physics.SetPosition;
_awaitingAcknowledgementScratch.Clear();
foreach ((RuntimeEntityKey key, RuntimeEntityPlacementToken token)
in _awaitingAcknowledgement)
{
if (!setPosition.IsPlacementCurrent(token))
_awaitingAcknowledgementScratch.Add(key);
}
foreach (RuntimeEntityKey key in _awaitingAcknowledgementScratch)
_awaitingAcknowledgement.Remove(key);
return _awaitingAcknowledgement.Count;
}
///
/// An OPTIMISATION, never the correctness mechanism: can this host place a
/// remote into RIGHT NOW without paying for
/// a park that will only be cancelled again?
///
///
/// Deliberately NOT complete, and it cannot be (C4 route 4b-2, delta
/// review MAJOR B/C — correcting this comment's earlier "the complete
/// pre-flight" framing). This tests ONE prefix, the destination's.
/// Core's own park predicates are broader in two independent ways that no
/// pre-flight can reproduce:
///
///
/// - PlacementTouchesPrefix also matches the
/// request's CurrentCellId. Round 3 — the earlier text called
/// that "the SOURCE landblock the remote is snapping OUT of" and "the
/// likelier shape"; that is not what production supplies.
/// CurrentCellId is read from record.FullCellId, and both
/// accepted-Position callers commit the accepted WIRE cell to that field
/// before this seam is reached — the graphical remote path through
/// LiveEntityRuntime.RebucketLiveEntity →
/// RuntimeEntityObjectLifetime.CommitRebucket in its shared
/// prologue, route 2 through the merge itself. So on the FIRST submit
/// this arm names the DESTINATION, and it is the destination prefix — the
/// one this pre-flight DOES see — that it matches. Measured, not argued.
/// Scoped deliberately (round 4, D5): that is a property of the first
/// submit only. A RETAINED operation re-submits from a cadence pump with
/// no fresh merge, so see the paragraph below for
/// what the arm can name by then — the arm is live, not dead
/// code.
/// - ResultTouchesPrefix scans every
/// QueriedCellIds entry, and that sweep footprint provably spans
/// NEIGHBOUR landblocks (CellTransit.AddOutsideCell re-derives the
/// block id from the global lcoord and states outright that there is no
/// same-block filter). The footprint does not EXIST until the sweep has
/// run.
///
///
/// re-reads this predicate and is subject to the
/// same two gaps, plus a third: a non-Position rebucket (the projection
/// materializer DatLiveEntityProjectionMaterializer — C4 route
/// 4b-3 deleted the second shipped writer, RemoteTeleportController's
/// rollback, and C4 route 7 D4 demoted the third,
/// EquippedChildRenderController.TickChild, to a presentation-
/// only bucket move that no longer touches record.FullCellId —
/// Runtime's own D1/D2 propagation is the child's canonical writer now)
/// can move record.FullCellId to a THIRD landblock between the
/// retained submit and the retry. The remaining writer is
/// harmless for the same reason (delta review N3). That reason is the
/// paragraph below — NOT, as the round-2 text claimed, that re-reading
/// record.CurrentCellId here would "re-derive a private Core
/// predicate outside Core": this method already calls Core's own
/// internal ,
/// so that objection was simply false (round-3 correction A5).
///
///
/// Correctness lives at the source instead.
/// SubmitPreparedPlacementCore's two quiescence parks are opted
/// into ParkDeferred's rollback, and ParkDeferred then
/// restores only when the cell it will actually restore into —
/// body.CellPosition.ObjCellId, read AFTER the snap and tested
/// against EVERY live quiescence — is not itself quiescing.
/// Corrected round 4 (D5): the earlier text said "every shape this
/// predicate cannot see is a merely-swept NEIGHBOUR", which contradicts
/// the third-landblock rebucket shape named ten lines above. The right
/// statement is weaker and does not need the enumeration: whatever the
/// blocking prefix turns out to be, the rollback is decided against the
/// RESTORE cell rather than against that prefix, so it re-admits nothing
/// into any quiescing prefix and AP-136's reason for the blanket
/// non-restorable scoping (a spatial root re-entering the prefix that is
/// trying to quiesce) cannot apply. This predicate therefore only
/// shrinks the reachable set, and the DeferredCell case in
/// handles whatever still reaches Core.
/// Recorded as AP-138(2).
///
///
/// Two other properties DEPEND on this pre-flight staying here
/// (round-3 correction A5). Do not delete it as redundant. First,
/// N3's "a retained retry can no longer strand" argument: a retry whose
/// destination has begun quiescing since the submit is refused HERE and
/// takes the window-drop store_position,
/// rather than reaching Core and opening a park into a prefix whose own
/// restore is (correctly) declined. Second, the destination-quiescing
/// shape is the one case where ParkDeferred would refuse to
/// restore, so refusing ahead of it is what keeps that shape off the
/// remote path entirely; without this call it becomes reachable and the
/// remote is left withdrawn until a later packet commits.
///
///
private bool CanAttemptDestination(
RuntimeSetPositionState setPosition,
uint landblockId) =>
_serviceWindow.IsWithinServiceWindow(landblockId)
&& !setPosition.IsCollisionPrefixQuiescing(landblockId);
private static void CancelToken(
RuntimeSetPositionState setPosition,
in RuntimeEntityPlacementToken token)
{
// Cancellation, not withdrawal: this controller abandons a placement
// intent while the remote stays in the world, so a DeferredCell park
// must roll back rather than strand the entity invisible and
// intangible (RuntimeSetPositionState.Forget).
RuntimePlacementCancellationReceipt cancellation =
setPosition.ForgetExactPlacement(
token,
restoreCancelledPark: true);
if (cancellation.IsValid)
setPosition.PublishCancellation(cancellation);
}
}