fix(streaming): #138-B avatar vanishes after teleporting out — don't relocate during PortalSpace
After a teleport-OUT the per-frame avatar-sync (GameWindow ~:8018) called GpuWorldState.RelocateEntity with the player controller's cell — which stays the FROZEN SOURCE cell until PlaceTeleportArrival materializes the destination. So mid-transit it dragged the avatar (which the teleport's rescue/re-inject had correctly placed at the destination center) back into the now-UNLOADED source landblock's pending bucket, where nothing recovers it (RelocateEntity only scans _loaded). Net: the avatar vanished after teleporting out and stayed gone. Fix: skip the per-frame relocate while the player is in PortalSpace. The teleport machinery (DrainRescued + PlaceTeleportArrival) owns the avatar's landblock during transit; per-frame relocation resumes at FireLoginComplete (InWorld). Live-verified with a new env-gated avatar-lifecycle probe (ACDREAM_PROBE_ENT / EntityVanishProbe; [ent] draw-set transitions + [dyn] cull check): pre-fix the trace showed `[ent] APPEND lb=0x0007FFFF(source) -> PENDING -> DRAWSET ABSENT` never recovering; post-fix every teleport goes RESCUE -> ABSENT -> APPEND(destination) -> PRESENT and stays drawn (4 teleports incl. to Holtburg, session ended PRESENT). Suites green: Core 1568(+2 skip), App 468(+2 skip), UI 425, Net 317. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
57e79dc679
commit
afd5f2a012
4 changed files with 111 additions and 1 deletions
|
|
@ -298,6 +298,7 @@ public sealed class GpuWorldState
|
|||
if (entity.ServerGuid != 0 && _persistentGuids.Contains(entity.ServerGuid))
|
||||
{
|
||||
_persistentRescued.Add(entity);
|
||||
EntityVanishProbe.Log($"[ent] RESCUE guid=0x{entity.ServerGuid:X8} from=loaded lb=0x{landblockId:X8}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +332,10 @@ public sealed class GpuWorldState
|
|||
foreach (var entity in pendingForLb)
|
||||
{
|
||||
if (entity.ServerGuid != 0 && _persistentGuids.Contains(entity.ServerGuid))
|
||||
{
|
||||
_persistentRescued.Add(entity);
|
||||
EntityVanishProbe.Log($"[ent] RESCUE guid=0x{entity.ServerGuid:X8} from=pending lb=0x{landblockId:X8}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -439,6 +443,8 @@ public sealed class GpuWorldState
|
|||
_entityScriptActivator?.OnCreate(entity);
|
||||
|
||||
uint canonicalLandblockId = (landblockId & 0xFFFF0000u) | 0xFFFFu;
|
||||
bool probePersistent = EntityVanishProbe.Enabled
|
||||
&& entity.ServerGuid != 0 && _persistentGuids.Contains(entity.ServerGuid);
|
||||
|
||||
if (_loaded.TryGetValue(canonicalLandblockId, out var lb))
|
||||
{
|
||||
|
|
@ -451,6 +457,8 @@ public sealed class GpuWorldState
|
|||
lb.LandblockId,
|
||||
lb.Heightmap,
|
||||
newEntities);
|
||||
if (probePersistent)
|
||||
EntityVanishProbe.Log($"[ent] APPEND guid=0x{entity.ServerGuid:X8} lb=0x{canonicalLandblockId:X8} -> LOADED(drawn)");
|
||||
RebuildFlatView();
|
||||
return;
|
||||
}
|
||||
|
|
@ -464,6 +472,8 @@ public sealed class GpuWorldState
|
|||
_pendingByLandblock[canonicalLandblockId] = bucket;
|
||||
}
|
||||
bucket.Add(entity);
|
||||
if (probePersistent)
|
||||
EntityVanishProbe.Log($"[ent] APPEND guid=0x{entity.ServerGuid:X8} lb=0x{canonicalLandblockId:X8} -> PENDING(hidden)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -570,5 +580,29 @@ public sealed class GpuWorldState
|
|||
private void RebuildFlatView()
|
||||
{
|
||||
_flatEntities = _loaded.Values.SelectMany(lb => lb.Entities).ToArray();
|
||||
if (EntityVanishProbe.Enabled) ProbeFlatViewTransitions();
|
||||
}
|
||||
|
||||
// TEMP (#138-B): persistent guids currently present in the drawn flat
|
||||
// view, for EntityVanishProbe transition logging. Strip with the probe.
|
||||
private readonly HashSet<uint> _persistentInFlatProbe = new();
|
||||
|
||||
// TEMP (#138-B): log when a persistent (player) entity enters/leaves the
|
||||
// drawn flat view. Transition-gated → low volume (fires at teleport
|
||||
// boundaries, not every rebuild). Strip with EntityVanishProbe.
|
||||
private void ProbeFlatViewTransitions()
|
||||
{
|
||||
var now = new HashSet<uint>();
|
||||
foreach (var e in _flatEntities)
|
||||
if (e.ServerGuid != 0 && _persistentGuids.Contains(e.ServerGuid))
|
||||
now.Add(e.ServerGuid);
|
||||
foreach (var g in now)
|
||||
if (!_persistentInFlatProbe.Contains(g))
|
||||
EntityVanishProbe.Log($"[ent] DRAWSET guid=0x{g:X8} -> PRESENT (flatCount={_flatEntities.Count})");
|
||||
foreach (var g in _persistentInFlatProbe)
|
||||
if (!now.Contains(g))
|
||||
EntityVanishProbe.Log($"[ent] DRAWSET guid=0x{g:X8} -> ABSENT (flatCount={_flatEntities.Count})");
|
||||
_persistentInFlatProbe.Clear();
|
||||
foreach (var g in now) _persistentInFlatProbe.Add(g);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue