using System.Collections.Concurrent; using AcDream.Core.World.Cells; namespace AcDream.Core.Physics; /// /// One exclusive-by-ownership collision-world root. A preparation mutates only /// its private root; activation transfers the complete root through one volatile /// reference publication shared by every collision facade. /// internal sealed class CollisionWorldState { internal Dictionary Landblocks { get; } = new(); internal List LandblockSlots { get; } = new(); internal Dictionary LandblockIndices { get; } = new(); internal Stack LandblockFreeSlots { get; } = new(); internal ConcurrentDictionary CellStruct { get; } = new(); internal ConcurrentDictionary FlatCellStruct { get; } = new(); internal ConcurrentDictionary FlatEnvCell { get; } = new(); internal ConcurrentDictionary Buildings { get; } = new(); internal ConcurrentDictionary EnvCells { get; } = new(); internal ConcurrentDictionary Terrain { get; } = new(); internal ConcurrentDictionary OutdoorCells { get; } = new(); internal Dictionary> ShadowCells { get; } = new(); internal Dictionary> ShadowEntityCells { get; } = new(); internal HashSet SuspendedShadowEntities { get; } = new(); internal Dictionary> WithdrawnPrefixesByOwner { get; } = new(); internal Dictionary> ShadowEntityShapes { get; } = new(); internal Dictionary ShadowEntityRegistrations { get; } = new(); internal Dictionary ShadowOwnerVersions { get; } = new(); internal Dictionary> ShadowOwnerPrefixes { get; } = new(); internal Dictionary> ShadowPrefixOwnerSlots { get; } = new(); internal Dictionary> ShadowPrefixOwnerIndices { get; } = new(); internal Dictionary> ShadowPrefixFreeSlots { get; } = new(); internal List ShadowOwnerSlots { get; } = new(); internal Dictionary ShadowOwnerIndices { get; } = new(); internal Stack ShadowOwnerFreeSlots { get; } = new(); } /// /// Stable indirection shared by PhysicsEngine, PhysicsDataCache, CellGraph, /// and ShadowObjectRegistry. Readers observe either complete root, never a /// mixture assembled by several facade assignments. /// internal sealed class CollisionWorldStateSlot { private CollisionWorldState? _current = new(); private bool _revoked; internal CollisionWorldStateSlot() { } internal CollisionWorldStateSlot(CollisionWorldState current) { _current = current ?? throw new ArgumentNullException(nameof(current)); } internal CollisionWorldState Current { get { if (_revoked) throw new ObjectDisposedException("Transferred collision generation"); return Volatile.Read(ref _current) ?? throw new ObjectDisposedException("Transferred collision generation"); } } internal CollisionWorldState TransferTo(CollisionWorldStateSlot destination) { ArgumentNullException.ThrowIfNull(destination); if (_revoked) throw new ObjectDisposedException("Transferred collision generation"); CollisionWorldState transferred = _current ?? throw new ObjectDisposedException("Transferred collision generation"); _revoked = true; Volatile.Write(ref destination._current, transferred); _current = null; return transferred; } internal CollisionWorldState Capture() => Current; }