using System.Diagnostics.CodeAnalysis;
namespace AcDream.App.Rendering.Walk;
///
/// Campaign FW3.1 — the walk's per-landblock
/// registry: the production sibling of
/// . Publish/retire
/// mirror that registry's landblock lifecycle exactly — both are committed
/// together in LandblockRenderPublisher.AdvanceCompleteOne's
/// BuildingRegistryCommitted step and retired together from
/// LandblockRenderPublisher.RemoveBuildingRegistry — because a walk
/// building and its BFS-derived Wb.Building counterpart come from the
/// SAME BuildingInfo array at the SAME landblock commit.
///
/// The reverse index () exists because
/// resolves a building's placement
/// by REFERENCE during the portal pass — ViewpointInBuilding,
/// ViewerDistanceTo, and ClipBuildingPolygon are called many
/// times per building per frame once FW3.2 wires the walk into the render
/// loop, so this must be O(1), not a per-landblock scan.
///
public sealed class WalkBuildingRegistry
{
private readonly Dictionary> _byLandblock = new();
private readonly Dictionary _byBuilding = new();
/// Atomically replaces one landblock's complete building set.
/// Mirrors Wb.BuildingRegistry's "no partial landblock" commit
/// discipline — is the immutable, fully-built
/// list carried by the streaming worker's
/// Wb.EnvCellLandblockBuild.WalkBuildings.
public void Publish(uint landblockId, IReadOnlyList entries)
{
uint key = landblockId & 0xFFFF0000u;
if (_byLandblock.TryGetValue(key, out IReadOnlyList? previous))
{
foreach (WalkBuildingFactory.Entry entry in previous)
_byBuilding.Remove(entry.Building);
}
_byLandblock[key] = entries;
foreach (WalkBuildingFactory.Entry entry in entries)
_byBuilding[entry.Building] = entry;
}
/// Removes every building of one landblock. Safe to call on a
/// landblock that never published (no-op).
public void Retire(uint landblockId)
{
uint key = landblockId & 0xFFFF0000u;
if (_byLandblock.Remove(key, out IReadOnlyList? previous))
{
foreach (WalkBuildingFactory.Entry entry in previous)
_byBuilding.Remove(entry.Building);
}
}
/// The buildings of one landblock, or empty when none are
/// published (unloaded, far-tier, or no BuildingInfo entries).
public IReadOnlyList GetBuildings(uint landblockId) =>
_byLandblock.TryGetValue(landblockId & 0xFFFF0000u, out IReadOnlyList? list)
? list
: Array.Empty();
/// O(1) reverse lookup: this building's committed placement.
/// False means the building is not (or no longer) committed — the walk
/// must treat that as a hard desync, never a silent skip (the FW
/// fail-loud rule).
public bool TryGetEntry(
WalkBuilding building, [MaybeNullWhen(false)] out WalkBuildingFactory.Entry entry) =>
_byBuilding.TryGetValue(building, out entry);
/// Number of landblocks with committed buildings (diagnostics).
public int LandblockCount => _byLandblock.Count;
}