using System.Collections.Generic; using System.Linq; using AcDream.Core.World; namespace AcDream.App.Streaming; /// /// Render-thread-owned registry of currently-loaded landblocks and their /// entities. All mutation happens in /// on the render thread; the renderer reads once per /// frame. /// /// /// Replaces GameWindow._entities, which was a flat list updated in /// multiple places. This class is the single point of truth for "what's in /// the world right now" and the only thing that mutates it. /// /// /// /// Threading: not thread-safe. All calls must happen on the render thread. /// The streaming worker never touches this type. /// /// public sealed class GpuWorldState { private readonly Dictionary _loaded = 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. private IReadOnlyList _flatEntities = System.Array.Empty(); public IReadOnlyList Entities => _flatEntities; public IReadOnlyCollection LoadedLandblockIds => _loaded.Keys; public bool IsLoaded(uint landblockId) => _loaded.ContainsKey(landblockId); public void AddLandblock(LoadedLandblock landblock) { _loaded[landblock.LandblockId] = landblock; RebuildFlatView(); } public void RemoveLandblock(uint landblockId) { if (_loaded.Remove(landblockId)) RebuildFlatView(); } /// /// Append an entity to a specific landblock's slot. Used by the live /// CreateObject path where the server spawns entities into an already- /// loaded landblock after the initial hydration pass. /// public void AppendLiveEntity(uint landblockId, WorldEntity entity) { if (!_loaded.TryGetValue(landblockId, out var lb)) return; // LoadedLandblock.Entities is an IReadOnlyList. Rebuild the // landblock record with the new entity appended. We accept the // allocation here because live spawns are rare compared to frame // iteration. var newEntities = new List(lb.Entities.Count + 1); newEntities.AddRange(lb.Entities); newEntities.Add(entity); _loaded[landblockId] = new LoadedLandblock(lb.LandblockId, lb.Heightmap, newEntities); RebuildFlatView(); } private void RebuildFlatView() { _flatEntities = _loaded.Values.SelectMany(lb => lb.Entities).ToArray(); } }