From 23ab17362a768dd04f10ef9a775c42acb290f3b5 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 21 May 2026 09:17:37 +0200 Subject: [PATCH] =?UTF-8?q?fix(physics):=20#92=20=E2=80=94=20seed=20resolv?= =?UTF-8?q?er=20with=20server=20cell=20id=20at=20player-mode=20entry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EnterPlayerModeNow computed the initial cellId from landblock prefix + hardcoded low byte 0x0001 (outdoor sentinel) and passed only the low 16 bits to PhysicsEngine.Resolve. When the server places the player INSIDE a building (spawn cell id e.g. 0xA9B4015A indoor), the sentinel forced the outdoor seed branch — for the first several ticks CheckBuildingTransit hadn't yet picked up the interior cell (it depends on the sphere overlapping the destination cell's BSP), the player was classified outdoor, indoor BSP queries didn't run, and exterior walls were passable until enough inward motion finally promoted them. User-visible symptom: "logged in inside the inn, ran out through the exterior wall; ran back in and the walls now block." Fix: use spawn.Position.LandblockId (the server's authoritative full cell id with landblock prefix) when available; fall back to the old sentinel only if the spawn record is missing (defensive — shouldn't fire in live play since OnLiveEntitySpawnedLocked writes _lastSpawnByGuid before EnterPlayerModeNow can possibly run). 1147 + 8 baseline maintained. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 35 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) 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);