From c32cef7e87102509d5c3c2411667f6e931566251 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 13 Apr 2026 15:04:46 +0200 Subject: [PATCH] fix(streaming): player entity no longer disappears on landblock unload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOT CAUSE: MarkPersistent(chosen.Id) stored the SERVER GUID (e.g. 0x5000000A) but RemoveLandblock checked entity.Id which is the LOCAL sequential counter (e.g. 42). Different number spaces → never matched → persistent rescue never triggered → player entity lost on unload. Fix: - Added WorldEntity.ServerGuid field (0 for dat-hydrated scenery) - Live entity spawn sets ServerGuid = spawn.Guid - RemoveLandblock checks entity.ServerGuid against _persistentGuids - MarkPersistent still stores the server GUID (correct) This bug has been reported across multiple sessions as "character disappears when walking far." The neverCullLandblockId fix only prevented frustum culling but didn't prevent the entity from being removed from the render list entirely. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 1 + src/AcDream.App/Streaming/GpuWorldState.cs | 2 +- src/AcDream.Core/World/WorldEntity.cs | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 2968ece..d62a80d 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -767,6 +767,7 @@ public sealed class GameWindow : IDisposable var entity = new AcDream.Core.World.WorldEntity { Id = _liveEntityIdCounter++, + ServerGuid = spawn.Guid, SourceGfxObjOrSetupId = spawn.SetupTableId.Value, Position = worldPos, Rotation = rot, diff --git a/src/AcDream.App/Streaming/GpuWorldState.cs b/src/AcDream.App/Streaming/GpuWorldState.cs index 264c155..9711e9a 100644 --- a/src/AcDream.App/Streaming/GpuWorldState.cs +++ b/src/AcDream.App/Streaming/GpuWorldState.cs @@ -135,7 +135,7 @@ public sealed class GpuWorldState { foreach (var entity in lb.Entities) { - if (_persistentGuids.Contains(entity.Id)) + if (entity.ServerGuid != 0 && _persistentGuids.Contains(entity.ServerGuid)) _persistentRescued.Add(entity); } } diff --git a/src/AcDream.Core/World/WorldEntity.cs b/src/AcDream.Core/World/WorldEntity.cs index 708d825..b98abf5 100644 --- a/src/AcDream.Core/World/WorldEntity.cs +++ b/src/AcDream.Core/World/WorldEntity.cs @@ -5,6 +5,12 @@ namespace AcDream.Core.World; public sealed class WorldEntity { public required uint Id { get; init; } + /// + /// Server-assigned GUID (from CreateObject). Zero for dat-hydrated + /// scenery/static entities that don't come from the server. + /// Used by GpuWorldState for persistent-entity rescue on landblock unload. + /// + public uint ServerGuid { get; init; } public required uint SourceGfxObjOrSetupId { get; init; } /// /// World-space position. Settable so Phase 6.7 position-update events