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();
}
}