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