fix(ui): gate — no void frames around the login wormhole; vitals icons centered
The login tunnel now covers from the first world-facing frame (the sky-void backdrop can never present pre-tunnel) and holds through an atomic tunnel-to-world swap at reveal completion — the void is structurally unreachable on both edges, pinned by frame-sequence tests across WorldSceneRenderer/WorldRevealCoordinator/LocalPlayerTeleport- Controller/RuntimeWorldTransitState. Vitals detail icons draw at their authored centered offsets in both stacked and side-by-side layouts. Implemented and live-probed by the fix agent; finalized by the lead after the agent parked post-verification (gates re-run green: App 5512/3, Runtime 1747/0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2f8c046aba
commit
fdc4fd496d
17 changed files with 649 additions and 66 deletions
|
|
@ -1631,6 +1631,98 @@ public sealed class LocalPlayerTeleportControllerTests
|
|||
Assert.Equal(loginCompletes, harness.Session.LoginCompleteCount);
|
||||
}
|
||||
|
||||
// ── Gate-fix round (2026-08-17): void frames at the wormhole edges ──
|
||||
|
||||
[Fact]
|
||||
public void LoginPlaceEdge_ReleasesWorldSimulationWhileTunnelInFront()
|
||||
{
|
||||
var harness = new Harness(worldReady: false);
|
||||
harness.Reveal.BeginLogin(0x20210001u);
|
||||
harness.Controller.Tick(0.016f);
|
||||
harness.Presentation.Enqueue(TeleportAnimEvent.EnterTunnel);
|
||||
harness.Controller.Tick(0.016f);
|
||||
Assert.True(harness.Presentation.IsPortalViewportVisible);
|
||||
Assert.False(harness.Transit.IsWorldSimulationAvailable);
|
||||
|
||||
// The hold ends: destination readiness + canonical first placement.
|
||||
harness.WorldReady = true;
|
||||
harness.Controller.OnLocalPlayerFirstEntryCompleted();
|
||||
harness.Controller.Tick(0.016f);
|
||||
|
||||
// The login Place edge is retail's blocking-ends edge —
|
||||
// CObjectMaint/CPhysics resume (SmartBox::UseTime @ 0x00455410)
|
||||
// while the tunnel is STILL in front. The world generation must be
|
||||
// available before the viewport swap so WorldFadeIn's first frame
|
||||
// draws the world instead of an empty void.
|
||||
harness.Presentation.Enqueue(TeleportAnimEvent.Place);
|
||||
harness.Controller.Tick(0.016f);
|
||||
Assert.True(harness.Presentation.IsPortalViewportVisible);
|
||||
Assert.True(harness.Transit.IsWorldSimulationAvailable);
|
||||
Assert.True(harness.Transit.Snapshot.Materialized);
|
||||
Assert.False(harness.Transit.Snapshot.Completed);
|
||||
Assert.False(harness.Placement.Called);
|
||||
|
||||
// Swap edge: the tunnel retires on this same tick and the world is
|
||||
// already available — no frame can exist where neither presents.
|
||||
harness.Presentation.Enqueue(TeleportAnimEvent.PlayExitSound);
|
||||
harness.Controller.Tick(0.016f);
|
||||
Assert.False(harness.Presentation.IsPortalViewportVisible);
|
||||
Assert.True(harness.Transit.IsWorldSimulationAvailable);
|
||||
|
||||
// The pump completes exactly as before.
|
||||
harness.Presentation.Enqueue(TeleportAnimEvent.FireLoginComplete);
|
||||
harness.Controller.Tick(0.016f);
|
||||
Assert.Equal(1, harness.Session.LoginCompleteCount);
|
||||
Assert.True(harness.Reveal.Snapshot.Completed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoginCover_PresentsPortalViewportShapeUntilChaseModeEntered()
|
||||
{
|
||||
// Entry-edge contract: retail's pre-player gameplay screen draws NO
|
||||
// world (black behind the UI) between char-select Enter and
|
||||
// CreatePlayer (CPlayerSystem::LogOnCharacter @ 0x0055F890 queues
|
||||
// game mode immediately; SmartBox::teleport_in_progress
|
||||
// @ 0x00451C20 stays low until the player exists). The composed
|
||||
// render-state source must therefore present the portal-viewport
|
||||
// frame shape for every live pre-world frame — the sky-only void
|
||||
// backdrop must never present.
|
||||
var harness = new Harness(worldReady: false);
|
||||
var login = new StubLoginState { IsWaitingForLogin = true };
|
||||
var source = new LocalPlayerTeleportRenderStateSource(
|
||||
harness.Controller, login);
|
||||
|
||||
// Pre-reveal: no tunnel scene, but the login cover holds the frame
|
||||
// black.
|
||||
Assert.False(harness.Presentation.IsPortalViewportVisible);
|
||||
Assert.True(source.IsPortalViewportVisible);
|
||||
|
||||
// Tunnel armed (the activation tick flips ChaseModeEverEntered and
|
||||
// the tunnel visibility together, before the next render): the
|
||||
// cover hands off to the tunnel with no gap.
|
||||
harness.Reveal.BeginLogin(0x20210001u);
|
||||
harness.Controller.Tick(0.016f);
|
||||
harness.Presentation.Enqueue(TeleportAnimEvent.EnterTunnel);
|
||||
harness.Controller.Tick(0.016f);
|
||||
login.IsWaitingForLogin = false;
|
||||
Assert.True(source.IsPortalViewportVisible);
|
||||
|
||||
// Swap edge: tunnel retires -> the world frame presents (the cover
|
||||
// stays off; ChaseModeEverEntered never reverts mid-session).
|
||||
harness.WorldReady = true;
|
||||
harness.Controller.OnLocalPlayerFirstEntryCompleted();
|
||||
harness.Presentation.Enqueue(TeleportAnimEvent.Place);
|
||||
harness.Controller.Tick(0.016f);
|
||||
harness.Presentation.Enqueue(TeleportAnimEvent.PlayExitSound);
|
||||
harness.Controller.Tick(0.016f);
|
||||
Assert.False(source.IsPortalViewportVisible);
|
||||
}
|
||||
|
||||
private sealed class StubLoginState : IRenderLoginStateSource
|
||||
{
|
||||
public bool IsWaitingForLogin { get; set; }
|
||||
}
|
||||
|
||||
private sealed class FakePresentation : ILocalPlayerTeleportPresentation
|
||||
{
|
||||
private readonly List<string> _order;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue