diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 18f1beb..10b6aba 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -10106,11 +10106,38 @@ public sealed class GameWindow : IDisposable _playerController.StepDownHeight = 0.4f; Console.WriteLine($"physics: player step heights — defaulting to 0.4 m (no setup dat)"); } - int plbX = _liveCenterX + (int)MathF.Floor(playerEntity.Position.X / 192f); - int plbY = _liveCenterY + (int)MathF.Floor(playerEntity.Position.Y / 192f); - uint pinitCellId = ((uint)plbX << 24) | ((uint)plbY << 16) | 0x0001u; + // Issue #92 (2026-05-20): seed the resolver with the SERVER's + // authoritative cell id from the spawn message instead of a + // hardcoded outdoor sentinel (`landblockPrefix | 0x0001`). When + // the player logs in INSIDE a building (server reports an indoor + // cell like `0xA9B4015A`), the old sentinel forced the resolver + // into the outdoor seed branch — for the first several ticks + // CheckBuildingTransit hadn't yet picked up the interior cell, + // so the player was classified outdoor, indoor BSP queries + // didn't run, and exterior walls were passable until the player + // moved far enough INWARD that the sphere overlap eventually + // promoted them. Visible symptom: "logged in inside the inn, + // ran out through the exterior wall." + // + // Fall back to the old sentinel when no spawn record is cached + // (defensive — should never fire in live play because the + // _lastSpawnByGuid entry was written by OnLiveEntitySpawnedLocked + // before EnterPlayerModeNow could possibly be reached). + uint pinitCellId; + if (_lastSpawnByGuid.TryGetValue(_playerServerGuid, out var playerSpawn) + && playerSpawn.Position is { } spawnPos + && spawnPos.LandblockId != 0) + { + pinitCellId = spawnPos.LandblockId; + } + else + { + int plbX = _liveCenterX + (int)MathF.Floor(playerEntity.Position.X / 192f); + int plbY = _liveCenterY + (int)MathF.Floor(playerEntity.Position.Y / 192f); + pinitCellId = ((uint)plbX << 24) | ((uint)plbY << 16) | 0x0001u; + } var initResult = _physicsEngine.Resolve( - playerEntity.Position, pinitCellId & 0xFFFFu, + playerEntity.Position, pinitCellId, System.Numerics.Vector3.Zero, 100f); _playerController.SetPosition(initResult.Position, initResult.CellId);