using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace MoonTools.ECS { internal class RelationDepot { private EntityStorage EntityStorage; private TypeIndices RelationTypeIndices; private RelationStorage[] storages = new RelationStorage[256]; public RelationDepot(EntityStorage entityStorage, TypeIndices relationTypeIndices) { EntityStorage = entityStorage; RelationTypeIndices = relationTypeIndices; } private void Register(int index) where TRelationKind : unmanaged { if (index >= storages.Length) { Array.Resize(ref storages, storages.Length * 2); } storages[index] = new RelationStorage(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private RelationStorage Lookup() where TRelationKind : unmanaged { var storageIndex = RelationTypeIndices.GetIndex(); // TODO: is there some way to avoid this null check? if (storages[storageIndex] == null) { Register(storageIndex); } return (RelationStorage) storages[storageIndex]; } public void Set(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : unmanaged { Lookup().Set(entityA, entityB, relationData); } public TRelationKind Get(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged { return Lookup().Get(entityA, entityB); } public (bool, bool) Remove(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged { return Lookup().Remove(entityA, entityB); } public void UnrelateAll(int entityID) where TRelationKind : unmanaged { Lookup().UnrelateAll(entityID); } public ReverseSpanEnumerator<(Entity, Entity)> Relations() where TRelationKind : unmanaged { return Lookup().All(); } public bool Related(int idA, int idB) where TRelationKind : unmanaged { return Lookup().Has((idA, idB)); } public ReverseSpanEnumerator OutRelations(int entityID) where TRelationKind : unmanaged { return Lookup().OutRelations(entityID); } public Entity OutRelationSingleton(int entityID) where TRelationKind : unmanaged { return Lookup().OutFirst(entityID); } public Entity NthOutRelation(int entityID, int n) where TRelationKind : unmanaged { return Lookup().OutNth(entityID, n); } public int OutRelationCount(int entityID) where TRelationKind : unmanaged { return Lookup().OutRelationCount(entityID); } public bool HasOutRelation(int entityID) where TRelationKind : unmanaged { return Lookup().HasOutRelation(entityID); } public ReverseSpanEnumerator InRelations(int entityID) where TRelationKind : unmanaged { return Lookup().InRelations(entityID); } public Entity NthInRelation(int entityID, int n) where TRelationKind : unmanaged { return Lookup().InNth(entityID, n); } public Entity InRelationSingleton(int entityID) where TRelationKind : unmanaged { return Lookup().InFirst(entityID); } public bool HasInRelation(int entityID) where TRelationKind : unmanaged { return Lookup().HasInRelation(entityID); } public int InRelationCount(int entityID) where TRelationKind : unmanaged { return Lookup().InRelationCount(entityID); } // untyped methods used for destroying and snapshots public unsafe void Set(int entityA, int entityB, int relationTypeIndex, void* relationData) { storages[relationTypeIndex].Set(entityA, entityB, relationData); } public void UnrelateAll(int entityID, int relationTypeIndex) { storages[relationTypeIndex].UnrelateAll(entityID); } public void Clear() { for (var i = 0; i < storages.Length; i += 1) { if (storages[i] != null) { storages[i].Clear(); } } } public void CreateMissingStorages(RelationDepot other) { while (other.RelationTypeIndices.Count >= storages.Length) { Array.Resize(ref storages, storages.Length * 2); } while (other.RelationTypeIndices.Count >= other.storages.Length) { Array.Resize(ref other.storages, other.storages.Length * 2); } for (var i = 0; i < other.RelationTypeIndices.Count; i += 1) { if (storages[i] == null && other.storages[i] != null) { storages[i] = other.storages[i].CreateStorage(); } } } public unsafe void TransferStorage(Dictionary worldToTransferID, RelationDepot other) { for (var i = 0; i < storages.Length; i += 1) { if (storages[i] != null) { foreach (var (a, b) in storages[i].All()) { if (worldToTransferID.TryGetValue(a, out var otherA)) { if (worldToTransferID.TryGetValue(b, out var otherB)) { var storageIndex = storages[i].GetStorageIndex(a, b); var relationData = storages[i].Get(storageIndex); other.Set(otherA, otherB, i, relationData); other.EntityStorage.AddRelationKind(otherA, i); other.EntityStorage.AddRelationKind(otherB, i); } else { throw new InvalidOperationException($"Missing transfer entity! {EntityStorage.Tag(a.ID)} related to {EntityStorage.Tag(b.ID)}"); } } } } } } } }