# Handoff — teleport issues cluster (2026-06-21) **Branch:** `claude/thirsty-goldberg-51bb9b` — committed, NOT merged ("merge later"). **Account:** `notan` / `MittSnus81!` → `+Je`. Portal via the Town Network hub (`0x0007`) to far towns. --- ## What shipped this session (context) #145 **cell-relative physics frame (Option B)**, Slices 1–3 + 7 (`438bb68`→`403a338`). The carried-anchor fix closes the far-town cascade **for a STREAMED-terrain arrival** — live-verified across ~10 far-town landblocks over 2 sessions, zero march. Design + plan: `docs/superpowers/specs/2026-06-21-145-cell-relative-physics-frame-design.md` + the matching plan. Adversarial review passed (2 flagged issues were verified false positives). Slices **4–6** (contact-plane / walkable cell-relative + `_liveCenter` fully render-only — invisible architectural completeness) remain. --- ## Work item A — #145 RESIDUAL (reopened): unstreamed-arrival-near-edge cascade **Trigger:** a teleport that arrives onto a **NOT-YET-STREAMED** landblock **near an edge** → the cascade recurs (+ a Z free-fall). **Evidence** (`launch5.log`): - `[snap] claim=0xC98C0028 pos=(113.666,190.259,22.010) branch=NO-LANDBLOCK (lbs=0) -> verbatim` — arrival local Y=190.3, **1.7 m from the 192 edge**, destination not streamed → cell marches `0xC98C → 0xC9FE` (+2 lbY/tick), Z free-falls 22→−19; outbound wire sends the marched cell `C9FE0031` + compensating garbage `localY=−21684` → ACE `MOVEMENT SPEED` / `failed transition` → **player stuck**. - Same session, **mid-block** unstreamed arrival `0x977B000C` (Y=73.8) → Z free-fall only, **no march**. - Hub `0x00070133` **VALIDATED** (cells resident) → fine. **Root (HYPOTHESIS — not apparatus-confirmed):** outdoor teleport places immediately but the destination streams a few ticks later (`streaming: dungeon EXIT-expand -> (201,140)` logs just after placement). During the gap the resolve runs against an empty world; at an edge the player crosses into the **unstreamed neighbour**, where the carried anchor (which computes to `(0,0,0)` at the recentered center) gives no protection. **Same root as the Z free-fall** (`#135`/`#138` placed-before-streamed gap). **⚠️ DO NOT guess-patch** (original #145 burned 5 attempts). **Apparatus first:** add a diagnostic that logs the **anchor value + the engagement-guard state** in `PhysicsEngine.ResolveWithTransition` at the crossing tick (behind a probe flag), reproduce a `0xC98C`-style arrival once, and confirm whether the anchor **disengages** (CellId/CellPosition landblock divergence) or the **body is repositioned into a marched frame first**. *Then* the fix is a known quantity. **Likely fix:** a **streaming-gap HOLD** — freeze the per-tick resolve (no integration/gravity, no membership march) while the player's landblock is unloaded, until it streams in (a few ticks). The async-streaming equivalent of retail's synchronous load; would fix **both** the cascade and the Z plunge. **Note:** this may be **subsumed by Work item B** — the retail teleport flow holds until the world loads, so there's no unstreamed gap to begin with. Decide A-vs-B ordering at brainstorm. --- ## Work item B — KEYSTONE FEATURE: the retail teleport flow **Symptoms (user-reported; all ONE root):** 1. Camera **floats** to the destination on teleport (disliked). 2. Teleport **takes too long** / feels off vs retail. 3. Player **input is not locked** during teleport. **Retail behaviour (user = oracle, treat as axiom):** teleport plays an **animation** (portal effect), holds input + camera, **waits for the world to load**, then exits/places the player. Used for **ALL** teleportation: **login, logout, death, portal.** **Insight:** acdream places **immediately** (raw placement → the floating camera + the unstreamed gap). A faithful retail teleport flow (anim + input/camera lock + **hold-until-loaded** + single exit point) fixes the float, the timing, the input-lock, **and very likely Work item A.** This is the keystone. **This is a FEATURE → resume `superpowers:brainstorming`** (user is the retail oracle on look/feel). The 3-agent research (workflow `wf_61501a7a-1d4`) is DONE — findings below. **Extend acdream's machinery, don't rebuild.** ### RETAIL flow (decomp-verified) — `gmSmartBoxUI` teleport **animation state machine** `TeleportAnimState` enum (`acclient.h:6871`): the client plays a **fade + portal-tunnel animation that COVERS the world-load wait** — used by ALL of login / logout / death / portal: ``` TAS_OFF(0) → WORLD_FADE_OUT(1, ~1s) → TUNNEL_FADE_IN(2, ~1s) → TUNNEL(3 = HOLD for world load) → TUNNEL_CONTINUE(4, min ~2s) → TUNNEL_FADE_OUT(5, ~1s) → WORLD_FADE_IN(6, ~1s) → OFF ``` - State machine: `gmSmartBoxUI::UseTime` (pc 219400); begin `BeginTeleportAnimation` (218888); end `EndTeleportAnimation` (218994). - **The world-load HOLD** is `TAS_TUNNEL(3)`, gated on `SmartBox::teleport_in_progress` (pc 90815) = `(player != 0) && (position_update_complete == 0)`; `position_update_complete` flips to 1 when the **DDD interrogation / position update** completes → animation advances. **This is the "exit when the world has loaded" the user wants.** - **Input lock:** `PlayerModule::SetLockUI` sets bit `0x1000000` in `options2_` (pc 486713) for the whole animation; cleared at `TAS_OFF`. - **Unification:** **death + logout share** the flow via `logOffRequested`; **login** enters via `RecvNotice_BeginEnterWorld`; **portal** starts directly at `TAS_TUNNEL` (skips the fade-out). Sounds: `Sound_UI_EnterPortal`(0x6A) / `ExitPortal`(0x6B). Exit callback `SendLoginCompleteNotification` (pc 367516). - Missing from decomp: exact `TELEPORT_ANIM_FADE_TIME` / `MIN_CONTINUE_TIME` constants; the DDD code that sets `position_update_complete`. ### acdream TODAY — what exists, what's missing acdream ALREADY has the *skeleton*: `PlayerState.PortalSpace` **input lock** (`PlayerMovementController.cs:840-854`) + a **hold-until-readiness** controller (`TeleportArrivalController.cs:56-105`; `GameWindow.OnTeleportStarted` :5487, `OnLivePositionUpdated`/`BeginArrival` :5394, `PlaceTeleportArrival` :5443-5478, readiness :5419-5439). **The gaps (the actual work):** 1. **NO animation / visual cover.** During the hold the camera is frozen at the OLD spot, then `PlaceTeleportArrival` **snaps** both cameras (`:5464-5469`, no fade/lerp). That snap + the world streaming in *is* the "floating" the user dislikes. → **Build a client teleport-anim state machine (TAS-shaped: fade-out → tunnel/hold → fade-in) that covers the wait.** 2. **Readiness gate is too weak → this is the #145 residual.** Outdoor readiness gates on `SampleTerrainZ != null` (`:5436`) — terrain heightmap can sample BEFORE the collision **landblock cells** load, so the player is placed onto a `branch=NO-LANDBLOCK` world → the unstreamed-arrival cascade + Z free-fall (Work item A). → **Gate on the full landblock being loaded** (`teleport_in_progress`-equivalent). **Fixing this likely closes Work item A.** 3. **`yaw` still updates during the hold** (`MouseLook` not frozen, gotcha) — lock it too. 4. **Login / logout / death NOT unified.** Login uses inline auto-entry guards (`:1036-1065`) that DUPLICATE teleport's readiness (`:5419-5439`) + recenter (`:2874-2881` vs `:5364-5370`). **Logout does not exist; death only feeds `Chat.OnPlayerKilled` (`:2569`), no game effect.** → Route all four through one arrival/anim controller; de-dupe readiness + recenter. ### Design direction (for the brainstorm) Build a **client-side teleport animation + arrival controller** (port the TAS state machine shape) layered on the existing PortalSpace lock + TeleportArrivalController hold: (a) a fade/tunnel **visual cover** over the world-load wait (kills the float); (b) **strengthen the readiness gate** to the full landblock (kills the #145 residual + the Z free-fall); (c) **unify** login/logout/death/portal entry points + de-dupe the readiness/recenter logic; (d) tune timing so it doesn't feel longer than retail (the current 600-frame/10s timeout is the worst case). --- ## Work item C — FPS leak after teleports (perf bug, SEPARATE) **Symptom:** FPS drops low after a couple of teleports (cumulative). **Hypothesis:** a streaming/entity **leak** — landblocks or render/physics entities not freed on teleport, accumulating across round-trips (candidates: dungeon collapse/expand not cleaning up; `RehydrateServerEntities` / `_lastSpawnByGuid` accumulating; `GpuWorldState` entities not pruned). **NEXT:** capture FPS + landblock / entity counts across N teleports (`ACDREAM_DUMP_LIVE_SPAWNS=1` + streaming logs + the DebugPanel counts), find what grows unbounded. Independent of A and B. --- ## Pointers / apparatus - **Logs (worktree root):** `launch5.log` (#145 residual capture — the `0xC98C` stuck-at-`−21684` run), `launch4.log` (Z free-fall + a 3-far-town round-trip), `launch3`/`launch2` (clean streamed-arrival runs). - **#145 record:** `docs/ISSUES.md` #145 (reopened, with the trigger table) + the physics digest banner (`claude-memory/project_physics_collision_digest.md`). - **Probes (no rebuild):** `ACDREAM_PROBE_CELL=1` (`[cell-transit]`), `ACDREAM_PROBE_RESOLVE=1`, `ACDREAM_CAPTURE_RESOLVE=`, `ACDREAM_DUMP_LIVE_SPAWNS=1`. - **Decomp oracle:** `docs/research/named-retail/` (grep `class::method`); for the teleport flow, look at the portal/teleport + DDD-loading + animation paths. - **Wire cross-check:** `references/holtburger` (client teleport flow) + `references/ACE` (server teleport / `MOVEMENT SPEED` validation — the `failed transition` rejects). ## Suggested order (brainstorm to confirm) 1. **B first** (retail teleport flow) — it's the keystone and likely subsumes **A**. Brainstorm → spec → build. 2. Re-test **A**: if the hold-until-loaded flow eliminates the unstreamed gap, the #145 residual is gone; if a narrow edge case survives, apply the diagnostic + targeted hold. 3. **C** (FPS leak) — independent; can run in parallel as its own investigation. 4. (Background) Slices **4–6** — the invisible Option-B completeness, unrelated to this cluster.