fix(physics): C4 route 3 — portal placement authority (local player)
Removes a duplicate placement authority for local-player portal arrival. Portalling worked before this change and works after it — this is not a bug fix, EXCEPT that it found and fixed one dead-code production bug. THE PRODUCTION BUG: TryExecuteCanonicalPortalPlacement re-read the accepted destination at Place time, but TryBeginPortalReveal already consumes that slot at Aim time — so the arm was 100% dead code and every real portal Place refused with host-token-unavailable. Found only because we refused to accept 7 skipped tests instead of chasing the count to zero. RETAIL IS THE GENERIC PATH FOR THE THIRD ROUTE RUNNING: SmartBox::TeleportPlayer @0x00453910 = SetPositionSimple(dest, 1) with flags 0x1012, followed by PlayerPositionUpdated. BOTH INVERSIONS, WITH THEIR ANCHORS: unlike route 2, the leash IS armed here (ConstrainTo @0x0045418A) and velocity is zeroed (set_velocity @0x004541B4); unlike route 4b-3, the local teleport_hook runs AFTER placement (@0x004538AE). THE THREE-ROUND DEFECT CHAIN, HONESTLY: - Round 1 released the player at the pre-teleport position while the anim stream marched on — the contract wrongly assumed Place re-fires (process rule 1's third occurrence this campaign). - Round 2's fix inferred commit from a global PendingCount, which three non-committing paths also clear — making the SAME bug complete cleanly and silently. Strictly worse than round 1: round 1 at least tripped portal-complete-before-materialized. - Round 3 latches the commit where it actually happens (ReconcileAndAcknowledgePortal), keyed on reveal generation and teleport sequence, via TryConsumePortalCommit. Two of the three required regression tests landed and are sabotage-verified on both hosts (ParkedPlace_ForgottenByOrdinaryMergeDoesNotLatchAsCommitted / HeadlessPortalPrepareDestinationForgottenByOrdinaryMergeDoesNotLatchAsCommitted). The third (force-arm-takes-the-slot) was judged unnecessary on review: with the inference gone, PendingCount is only a "don't ask yet" guard at both gates, so a force operation occupying or vacating the slot no longer changes an input the commit decision reads — the case collapses into what the landed test already discriminates. THE B2/P3 RESOLUTION: both round-2 reviews were right about different branches of the same synchronous call. RuntimePlacementProjectionSubscription .OnPlacement acknowledges the FIFO head only when TryApply returns true; a Place whose portal authority went stale (transit ended/superseded while parked) used to return false, wedging every later entity's placement receipt behind it forever. Both sinks (RuntimePlacementPresentationSink, HeadlessRuntimePlacementProjectionSink) now acknowledge-and-ignore a stale-authority Place instead of refusing it. The regression test (RuntimePlacementPresentationSinkTests .PortalPlace_StaleTransitHostOrSequenceIsAcknowledgedAndIgnored) had been asserting the old, wrong `false` behaviour; it now asserts and sabotage-verifies the fix. Also lands: AP-144 (register discipline — the portal movement-event send reuses the stricter UsePositionFromServer gate where retail's SendMovementEvent is the looser autonomy_level != 0 test, diverging only at level 1, currently unreachable), AP-145 + issue #318 (the local-player collision-shadow presentation write bypasses its own publisher's ShadowObjects write via a direct cache .Set(), self-healing only once dedup diverges — filed, not fixed, pending a composition test), AD-42 deleted (its last citation retired by the canonical portal arm), AD-2 updated (the wait-cue's trigger predicate now covers a second cause), and two documentation corrections: the enter_world misattribution (both call sites are in SmartBox::HandleCreateObject, only one in the player branch — portal arrival is TeleportPlayer, not enter_world) and the stale "local player never reaches this path" comment on the generic-remote-render-pose write. Suite: 11,090 passed / 4 skipped / 0 failed. No new skips, nothing weakened. STILL OWED: the connected two-client gate, with ACDREAM_PROBE_LOCAL_TELEPORT=1, scored only if [local-tp] lines actually appear in the capture — and explicitly NOT scored as covering issue #318 (no composition test yet asserts PhysicsEngine.ShadowObjects directly). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
cd3129e9d6
commit
e0f96a55bf
24 changed files with 5261 additions and 243 deletions
|
|
@ -1171,6 +1171,63 @@ public static class PhysicsDiagnostics
|
|||
$"[tp-probe] {point,-6} id=0x{id:X8} t={Environment.TickCount64} {extra}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C4 route 3 D-T8 (2026-08-04 — TEMPORARY, strip with the rest of the
|
||||
/// physics-probe family once the connected gate is scored). One line per
|
||||
/// local-player portal-arrival attempt from
|
||||
/// <c>RuntimeAcceptedPositionDriveController.ReconcileAndAcknowledgePortal</c>
|
||||
/// — the single Runtime chokepoint both the graphical and headless hosts
|
||||
/// share, so this is dual-host parity evidence, not per-host guesswork.
|
||||
/// Initial state from <c>ACDREAM_PROBE_LOCAL_TELEPORT=1</c>.
|
||||
/// </summary>
|
||||
public static bool ProbeLocalTeleportEnabled { get; set; } =
|
||||
Environment.GetEnvironmentVariable("ACDREAM_PROBE_LOCAL_TELEPORT") == "1";
|
||||
|
||||
/// <summary>
|
||||
/// Which host process is running — set once at composition startup by
|
||||
/// each host's own entry point (<c>SessionPlayerComposition</c> for
|
||||
/// graphical, <c>HeadlessSessionHost</c> for headless). Runtime itself
|
||||
/// stays presentation-agnostic (Slice K); this is a diagnostics-only
|
||||
/// label so <see cref="LogLocalTeleportArrival"/> can report which
|
||||
/// process produced a given line without threading a host parameter
|
||||
/// through the drive controller's constructor.
|
||||
/// </summary>
|
||||
public static string LocalTeleportHostKind { get; set; } = "graphical";
|
||||
|
||||
/// <summary>
|
||||
/// One <c>[local-tp]</c> line: cause, host, placement status, portal
|
||||
/// generation/sequence, destination cell, resolved cell, and the three
|
||||
/// D-T8 booleans confirming the reconcile suffix actually ran
|
||||
/// (<paramref name="hookTailRan"/> = <c>CommitCanonicalTeleportFrame</c>
|
||||
/// executed, <paramref name="leashArmed"/> = the constraint leash is
|
||||
/// armed post-commit, <paramref name="autorunCancelled"/> =
|
||||
/// <c>CancelAutoRun</c> ran). Self-guards on
|
||||
/// <see cref="ProbeLocalTeleportEnabled"/>. <paramref name="cause"/> is
|
||||
/// always <c>"portal"</c> today — ACE's recall/admin teleports arrive as
|
||||
/// the identical TeleportAdvanced Position and are indistinguishable
|
||||
/// from a doorway portal at this layer; the parameter exists so a future
|
||||
/// wire-level cause signal has somewhere to land without a probe
|
||||
/// signature change.
|
||||
/// </summary>
|
||||
public static void LogLocalTeleportArrival(
|
||||
string cause,
|
||||
string placementStatus,
|
||||
long portalGeneration,
|
||||
ushort teleportSequence,
|
||||
uint destinationCell,
|
||||
uint resolvedCell,
|
||||
bool hookTailRan,
|
||||
bool leashArmed,
|
||||
bool autorunCancelled)
|
||||
{
|
||||
if (!ProbeLocalTeleportEnabled) return;
|
||||
string hookTailText = hookTailRan ? "ran" : "skipped";
|
||||
string leashText = leashArmed ? "armed" : "unarmed";
|
||||
string autorunText = autorunCancelled ? "cancelled" : "unchanged";
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[local-tp] cause={cause} host={LocalTeleportHostKind} status={placementStatus} gen={portalGeneration} seq={teleportSequence} dest=0x{destinationCell:X8} resolved=0x{resolvedCell:X8} hookTail={hookTailText} leash={leashText} autorun={autorunText}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A6.P3 issue #98 step-walk investigation (2026-05-23). When true,
|
||||
/// emits one <c>[step-walk]</c> line at selected points in the transition
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue