fix(streaming): relocate player entity to current landblock every frame

TWO root causes for "character disappears when walking far":

1. MarkPersistent stored SERVER GUID but RemoveLandblock checked LOCAL
   entity.Id — different namespaces, never matched. Fixed by adding
   WorldEntity.ServerGuid field and checking it in RemoveLandblock.

2. Even with rescue working, the player entity stays in its SPAWN
   landblock's entity list forever. When the player walks to a new
   landblock and the spawn landblock gets frustum-culled, the entity
   disappears because neverCullLandblockId is computed from the
   player's current position (new landblock) but the entity is stored
   in the old landblock.

   Fixed by calling GpuWorldState.RelocateEntity every frame in the
   player-mode update loop. This moves the entity from whatever
   landblock it's currently in to the one matching its actual position.
   The scan is O(entities) but only runs for one entity per frame.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-13 15:28:32 +02:00
parent c32cef7e87
commit 08e309c357
2 changed files with 51 additions and 6 deletions

View file

@ -1713,13 +1713,18 @@ public sealed class GameWindow : IDisposable
if (_entitiesByServerGuid.TryGetValue(_playerServerGuid, out var pe))
{
pe.Position = result.Position;
// AC character models face +Y in their default orientation.
// Our yaw convention has cos(yaw)=+X at yaw=0, so yaw=0
// means facing +X. Offset by -PI/2 so the model faces the
// actual walk direction (at yaw=0, model rotation = -PI/2
// = facing +X instead of the model's default +Y).
pe.Rotation = System.Numerics.Quaternion.CreateFromAxisAngle(
System.Numerics.Vector3.UnitZ, _playerController.Yaw - MathF.PI / 2f);
// Move the player entity to its current landblock in GpuWorldState
// so it doesn't get frustum-culled when the player walks away from
// the spawn landblock. Without this, the entity stays in the spawn
// landblock's entity list and disappears when that landblock is culled.
var pp = _playerController.Position;
int plx = _liveCenterX + (int)System.Math.Floor(pp.X / 192f);
int ply = _liveCenterY + (int)System.Math.Floor(pp.Y / 192f);
uint currentLb = (uint)((plx << 24) | (ply << 16) | 0xFFFF);
_worldState.RelocateEntity(pe, currentLb);
}
// Update chase camera.