# Same-dungeon respawn landblock identity — retail pseudocode ## Symptom and captured cause After `/die` in the starter dungeon, ACE respawned the player in the same cell, `0x8C0401AD`. The player stood correctly until the first movement input, then lost floor contact and fell through the world. The destination update and the player controller both named cell `0x8C0401AD`, but acdream reconstructed the source landblock from render-space XYZ. The dungeon cell's valid local Y was about `-30.4 m`, so this expression: ```text sourceLbY = streamingCenterY + floor(renderY / 192 m) = 4 + floor(-30.4 / 192) = 3 ``` misidentified the source as landblock `(140,3)`. The destination was `(140,4)`, so the same-cell respawn entered the cross-landblock branch, removed `0x8C04FFFF` from the physics engine, and left the player with no hydrated floor. Dungeon EnvCells may have arbitrary negative frame origins. Their XYZ values therefore cannot identify an AC landblock. ## Named retail oracle Retail stores cell identity inside every position: ```c // acclient.h:30658-30662 struct Position : PackObj { unsigned int objcell_id; Frame frame; }; ``` The local-player teleport path preserves that complete value end to end: - `SmartBox::TeleportPlayer @ 0x00453910` passes its `Position const*` directly to `CPhysicsObj::SetPositionSimple`. - `CPhysicsObj::SetPositionSimple @ 0x005162B0` copies that complete `Position` into a `SetPositionStruct`, then calls `CPhysicsObj::SetPosition`. - `SmartBox::HandleReceivedPosition`'s local-player teleport branch (`0x0045415F`–`0x00454168`) passes the received `Position` to `SmartBox::TeleportPlayer`; it does not derive a cell from XYZ. References: - `docs/research/named-retail/acclient.h:30658` - `docs/research/named-retail/acclient_2013_pseudo_c.txt:92514` - `docs/research/named-retail/acclient_2013_pseudo_c.txt:93013` - `docs/research/named-retail/acclient_2013_pseudo_c.txt:284276` ## Reference-client/server cross-check The existing L.3 research traces the same contract through two independent implementations: - ACE sends death respawn as `GameMessagePlayerTeleport`, immediately followed by an `UpdatePosition` whose `PositionPack` contains the destination cell and frame (`docs/research/2026-05-04-l3-port/12-hard-teleport-branch.md`, sections 7 and 9). - The retail packet path unpacks `PositionPack.position` as `objcell_id + Frame` and routes that complete value into the hard-teleport branch (`docs/research/2026-05-04-l3-port/03-up-routing.md`, sections 0 and 3). The repository's old external ACE checkout is no longer present after the WorldBuilder extraction, so the checked-in cross-reference research is the stable ACE source citation for this change. ## Faithful pseudocode Retail has synchronous cell access and can install the received `Position` directly. acdream keeps its asynchronous loading cover, but the identity decision must use the same `Position.objcell_id` values: ```text on local player teleport destination(receivedPosition): sourceCell = player.currentPosition.objcell_id destinationCell = receivedPosition.objcell_id if sourceCell is zero: # acdream-only pre-placement fallback; no XYZ inference sourceLandblock = currentStreamingCenterLandblock else: sourceLandblock = landblockOf(sourceCell) destinationLandblock = landblockOf(destinationCell) crossesLandblock = sourceLandblock != destinationLandblock if crossesLandblock: evict the old streaming-origin physics landblock recenter streaming on destinationLandblock start the existing destination hydration path retain receivedPosition.objcell_id as the pending destination cell when the existing readiness gate opens: place the player with the complete destination cell + frame ``` `landblockOf(cell)` means `(cell & 0xFFFF0000) | 0xFFFF`. At no point does the decision divide or floor position coordinates. ## Conformance cases 1. Source and destination are both `0x8C0401AD`, while render Y is negative: same landblock; do not evict or recenter. 2. Source and destination are different EnvCells in `0x8C04`: same landblock; do not evict or recenter. 3. Source `0x8C0401AD`, destination `0xA9B40022`: cross-landblock; retain the existing recenter/hydration behavior. 4. Source cell is zero before first placement: compare against the current streaming-center ID; never infer from XYZ.