fix(core+app): Phase B.3 — Setup.StepUpHeight + scenery road exclusion
Four targeted fixes for user-reported movement/visual bugs: 1. Player entity disappearing: GpuWorldState now supports persistent entities (MarkPersistent/DrainRescued). The player character survives landblock unloads and gets re-injected into the streaming window at the current center landblock. 2. Feet sinking into terrain: +0.15 Z bias in PlayerMovementController keeps the character model above terrain z-fighting edge cases. 3. Camera after portal teleport: ChaseCamera.Update now called immediately after teleport snap so the camera recenters on the new position instead of lingering at the pre-teleport location. 4. Scenery on roads: SceneryGenerator now checks road status at the final displaced position (not just the origin vertex), catching objects that drift from non-road vertices onto road cells. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9dbb2cbd5c
commit
41013ce3e3
5 changed files with 71 additions and 8 deletions
|
|
@ -48,6 +48,13 @@ public sealed class GpuWorldState
|
|||
/// </summary>
|
||||
private readonly Dictionary<uint, List<WorldEntity>> _pendingByLandblock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Entities that must survive landblock unloads (e.g. the player character).
|
||||
/// On RemoveLandblock, these are rescued and re-parked as pending for their
|
||||
/// current canonical landblock.
|
||||
/// </summary>
|
||||
private readonly HashSet<uint> _persistentGuids = new();
|
||||
|
||||
// Cached flat view over all entities across all loaded landblocks,
|
||||
// rebuilt on each add/remove. The renderer holds a reference to this
|
||||
// list, so rebuilding it replaces the reference atomically.
|
||||
|
|
@ -112,12 +119,27 @@ public sealed class GpuWorldState
|
|||
RebuildFlatView();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark a server-GUID as persistent — this entity survives landblock unloads
|
||||
/// and gets re-parked as pending for its current canonical landblock.
|
||||
/// </summary>
|
||||
public void MarkPersistent(uint serverGuid) => _persistentGuids.Add(serverGuid);
|
||||
|
||||
public void RemoveLandblock(uint landblockId)
|
||||
{
|
||||
// Drop pending entries for the same landblock — if the landblock
|
||||
// is being unloaded the player has moved away from it, and any
|
||||
// pending spawns that arrived for it are no longer relevant. The
|
||||
// server will resend them via CreateObject when the player returns.
|
||||
// Rescue persistent entities before removal. These get appended
|
||||
// to the _persistentRescued list; the caller is responsible for
|
||||
// re-injecting them (via AppendLiveEntity) into whatever landblock
|
||||
// the player is currently on.
|
||||
if (_loaded.TryGetValue(landblockId, out var lb))
|
||||
{
|
||||
foreach (var entity in lb.Entities)
|
||||
{
|
||||
if (_persistentGuids.Contains(entity.Id))
|
||||
_persistentRescued.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
_pendingByLandblock.Remove(landblockId);
|
||||
_aabbs.Remove(landblockId);
|
||||
|
||||
|
|
@ -125,6 +147,20 @@ public sealed class GpuWorldState
|
|||
RebuildFlatView();
|
||||
}
|
||||
|
||||
private readonly List<WorldEntity> _persistentRescued = new();
|
||||
|
||||
/// <summary>
|
||||
/// Drain entities rescued from unloaded landblocks. The caller should
|
||||
/// re-inject each via <see cref="AppendLiveEntity"/> with its current position.
|
||||
/// </summary>
|
||||
public List<WorldEntity> DrainRescued()
|
||||
{
|
||||
if (_persistentRescued.Count == 0) return _persistentRescued;
|
||||
var result = new List<WorldEntity>(_persistentRescued);
|
||||
_persistentRescued.Clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append an entity to a specific landblock's slot. Used by the live
|
||||
/// CreateObject path where the server spawns entities at a server-side
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue