diff --git a/src/Archetype.cs b/src/Archetype.cs deleted file mode 100644 index 791ba96..0000000 --- a/src/Archetype.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using MoonTools.ECS.Collections; - -namespace MoonTools.ECS; - -internal class Archetype -{ - public World World; - public ArchetypeSignature Signature; - public NativeArray Entities = new NativeArray(); - - public SortedDictionary AddEdges = - new SortedDictionary(); - public SortedDictionary RemoveEdges = - new SortedDictionary(); - - public int Count => Entities.Count; - - public Archetype(World world, ArchetypeSignature signature) - { - World = world; - Signature = signature; - } - - public int Append(Entity entity) - { - Entities.Append(entity); - return Entities.Count - 1; - } - - public void ClearAll() - { - for (int i = Entities.Count - 1; i >= 0; i -= 1) - { - World.Destroy(Entities[i]); - } - } -} diff --git a/src/ArchetypeEdge.cs b/src/ArchetypeEdge.cs deleted file mode 100644 index a10fb46..0000000 --- a/src/ArchetypeEdge.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace MoonTools.ECS; - -internal readonly record struct ArchetypeEdge(Archetype Add, Archetype Remove); diff --git a/src/ArchetypeRecord.cs b/src/ArchetypeRecord.cs deleted file mode 100644 index 6780c29..0000000 --- a/src/ArchetypeRecord.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace MoonTools.ECS; - -internal readonly record struct ArchetypeRecord(Archetype Archetype, int Row); diff --git a/src/ArchetypeSignature.cs b/src/ArchetypeSignature.cs deleted file mode 100644 index 5dac880..0000000 --- a/src/ArchetypeSignature.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using MoonTools.ECS.Collections; - -namespace MoonTools.ECS; - -internal class ArchetypeSignature : IEquatable -{ - public static ArchetypeSignature Empty = new ArchetypeSignature(0); - - IndexableSet Ids; - - public int Count => Ids.Count; - - public TypeId this[int i] => Ids[i]; - - public ArchetypeSignature() - { - Ids = new IndexableSet(); - } - - public ArchetypeSignature(int capacity) - { - Ids = new IndexableSet(capacity); - } - - // Maintains sorted order - public void Insert(TypeId componentId) - { - Ids.Add(componentId); - } - - public void Remove(TypeId componentId) - { - Ids.Remove(componentId); - } - - public bool Contains(TypeId componentId) - { - return Ids.Contains(componentId); - } - - public void CopyTo(ArchetypeSignature other) - { - foreach (var id in Ids.AsSpan()) - { - other.Ids.Add(id); - } - } - - public override bool Equals(object? obj) - { - return obj is ArchetypeSignature signature && Equals(signature); - } - - public bool Equals(ArchetypeSignature? other) - { - if (other == null) - { - return false; - } - - if (Ids.Count != other.Ids.Count) - { - return false; - } - - for (int i = 0; i < Ids.Count; i += 1) - { - if (Ids[i] != other.Ids[i]) - { - return false; - } - } - - return true; - } - - public override int GetHashCode() - { - var hashcode = 1; - - foreach (var id in Ids) - { - hashcode = HashCode.Combine(hashcode, id); - } - - return hashcode; - } -} diff --git a/src/IndexableSet.cs b/src/Collections/IndexableSet.cs similarity index 100% rename from src/IndexableSet.cs rename to src/Collections/IndexableSet.cs diff --git a/src/Collections/NativeArrayUntyped.cs b/src/Collections/NativeArrayUntyped.cs index 020c5a0..070effd 100644 --- a/src/Collections/NativeArrayUntyped.cs +++ b/src/Collections/NativeArrayUntyped.cs @@ -76,22 +76,6 @@ internal unsafe class NativeArray : IDisposable Count += 1; } - public void CopyElementToEnd(int index, NativeArray other) - { - if (other.Count >= other.Capacity) - { - other.Resize(); - } - - NativeMemory.Copy( - (void*) (Elements + (index * ElementSize)), - (void*) (other.Elements + (other.Count * ElementSize)), - (nuint) ElementSize - ); - - other.Count += 1; - } - public void CopyAllTo(NativeArray other) { if (Count >= other.Capacity) diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs index 4cfb619..2571446 100644 --- a/src/ComponentStorage.cs +++ b/src/ComponentStorage.cs @@ -9,13 +9,15 @@ namespace MoonTools.ECS internal readonly Dictionary EntityIDToStorageIndex = new Dictionary(16); internal readonly NativeArray Components; internal readonly NativeArray EntityIDs; + internal readonly TypeId TypeId; private bool disposed; - public ComponentStorage(int elementSize) + public ComponentStorage(TypeId typeId, int elementSize) { Components = new NativeArray(elementSize); EntityIDs = new NativeArray(); + TypeId = typeId; } public bool Any() diff --git a/src/Filter.cs b/src/Filter.cs index 7d42bac..a3b48ac 100644 --- a/src/Filter.cs +++ b/src/Filter.cs @@ -1,193 +1,77 @@ -using System; -using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS; // TODO: do we want to get fancy with queries beyond Include and Exclude? public class Filter { - private Archetype EmptyArchetype; - private HashSet Included; - private HashSet Excluded; + private World World; + internal FilterSignature Signature; - public EntityEnumerator Entities => new EntityEnumerator(this); - internal ArchetypeEnumerator Archetypes => new ArchetypeEnumerator(this); - public RandomEntityEnumerator EntitiesInRandomOrder => new RandomEntityEnumerator(this); + internal IndexableSet EntitySet = new IndexableSet(); - public bool Empty - { - get - { - var empty = true; + public ReverseSpanEnumerator Entities => EntitySet.GetEnumerator(); - foreach (var archetype in Archetypes) - { - if (archetype.Count > 0) - { - return false; - } - } - - return empty; - } - } - - public int Count - { - get - { - var count = 0; - - foreach (var archetype in Archetypes) - { - count += archetype.Count; - } - - return count; - } - } - - public Entity RandomEntity - { - get - { - var randomIndex = RandomManager.Next(Count); - return NthEntity(randomIndex); - } - } + public bool Empty => EntitySet.Count == 0; + public int Count => EntitySet.Count; // WARNING: this WILL crash if the index is out of range! - public Entity NthEntity(int index) + public Entity NthEntity(int index) => EntitySet[index]; + + // WARNING: this WILL crash if the filter is empty! + public Entity RandomEntity => EntitySet[RandomManager.Next(EntitySet.Count)]; + public RandomEntityEnumerator EntitiesInRandomOrder => new RandomEntityEnumerator(this); + + internal Filter(World world, FilterSignature signature) { - foreach (var archetype in Archetypes) - { - if (index < archetype.Count) - { - return archetype.Entities[index]; - } - - index -= archetype.Count; - } - - throw new InvalidOperationException("Filter index out of range!"); + World = world; + Signature = signature; } public void DestroyAllEntities() { - foreach (var archetype in Archetypes) + foreach (var entity in EntitySet) { - archetype.ClearAll(); + World.Destroy(entity); } } - internal Filter(Archetype emptyArchetype, HashSet included, HashSet excluded) + internal void Check(Entity entity) { - EmptyArchetype = emptyArchetype; - Included = included; - Excluded = excluded; - } - - internal ref struct ArchetypeEnumerator - { - private Archetype CurrentArchetype; - - // TODO: pool these - private Queue ArchetypeQueue = new Queue(); - private Queue ArchetypeSearchQueue = new Queue(); - private HashSet Explored = new HashSet(); - - public ArchetypeEnumerator GetEnumerator() => this; - - public ArchetypeEnumerator(Filter filter) + foreach (var type in Signature.Included) { - var empty = filter.EmptyArchetype; - ArchetypeSearchQueue.Enqueue(empty); - - // TODO: can we cache this search effectively? - while (ArchetypeSearchQueue.TryDequeue(out var current)) + if (!World.Has(entity, type)) { - // exclude the empty archetype - var satisfiesFilter = filter.Included.Count != 0; - - foreach (var componentId in filter.Included) - { - if (!current.Signature.Contains(componentId)) - { - satisfiesFilter = false; - } - } - - foreach (var componentId in filter.Excluded) - { - if (current.Signature.Contains(componentId)) - { - satisfiesFilter = false; - } - } - - if (satisfiesFilter) - { - ArchetypeQueue.Enqueue(current); - } - - // breadth-first search - // ignore excluded component edges - foreach (var (componentId, archetype) in current.AddEdges) - { - if (!Explored.Contains(archetype) && !filter.Excluded.Contains(componentId)) - { - Explored.Add(archetype); - ArchetypeSearchQueue.Enqueue(archetype); - } - } - - foreach (var (componentId, archetype) in current.RemoveEdges) - { - if (!Explored.Contains(archetype)) - { - Explored.Add(archetype); - ArchetypeSearchQueue.Enqueue(archetype); - } - } + EntitySet.Remove(entity); + return; } } - public bool MoveNext() + foreach (var type in Signature.Excluded) { - return ArchetypeQueue.TryDequeue(out CurrentArchetype!); - } - - public Archetype Current => CurrentArchetype; - } - - public ref struct EntityEnumerator - { - private Entity CurrentEntity; - - public EntityEnumerator GetEnumerator() => this; - - // TODO: pool this - Queue EntityQueue = new Queue(); - - internal EntityEnumerator(Filter filter) - { - var archetypeEnumerator = new ArchetypeEnumerator(filter); - - foreach (var archetype in archetypeEnumerator) + if (World.Has(entity, type)) { - foreach (var entity in archetype.Entities) - { - EntityQueue.Enqueue(entity); - } + EntitySet.Remove(entity); + return; } } - public bool MoveNext() - { - return EntityQueue.TryDequeue(out CurrentEntity); - } + EntitySet.Add(entity); + } - public Entity Current => CurrentEntity; + internal void AddEntity(in Entity entity) + { + EntitySet.Add(entity); + } + + internal void RemoveEntity(in Entity entity) + { + EntitySet.Remove(entity); + } + + internal void Clear() + { + EntitySet.Clear(); } public ref struct RandomEntityEnumerator diff --git a/src/FilterBuilder.cs b/src/FilterBuilder.cs index 2213a0a..316052e 100644 --- a/src/FilterBuilder.cs +++ b/src/FilterBuilder.cs @@ -1,21 +1,22 @@ using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { public struct FilterBuilder { World World; - HashSet Included; - HashSet Excluded; + IndexableSet Included; + IndexableSet Excluded; internal FilterBuilder(World world) { World = world; - Included = new HashSet(); - Excluded = new HashSet(); + Included = new IndexableSet(); + Excluded = new IndexableSet(); } - private FilterBuilder(World world, HashSet included, HashSet excluded) + private FilterBuilder(World world, IndexableSet included, IndexableSet excluded) { World = world; Included = included; @@ -24,19 +25,20 @@ namespace MoonTools.ECS public FilterBuilder Include() where T : unmanaged { - Included.Add(World.GetTypeId()); + Included.Add(World.GetComponentTypeId()); return new FilterBuilder(World, Included, Excluded); } public FilterBuilder Exclude() where T : unmanaged { - Excluded.Add(World.GetTypeId()); + Excluded.Add(World.GetComponentTypeId()); return new FilterBuilder(World, Included, Excluded); } public Filter Build() { - return new Filter(World.EmptyArchetype, Included, Excluded); + var signature = new FilterSignature(Included, Excluded); + return World.GetFilter(signature); } } } diff --git a/src/FilterSignature.cs b/src/FilterSignature.cs index 7e94922..e48fa83 100644 --- a/src/FilterSignature.cs +++ b/src/FilterSignature.cs @@ -1,15 +1,14 @@ using System; -using System.Collections.Generic; using MoonTools.ECS.Collections; namespace MoonTools.ECS { public struct FilterSignature : IEquatable { - public readonly IndexableSet Included; - public readonly IndexableSet Excluded; + public readonly IndexableSet Included; + public readonly IndexableSet Excluded; - public FilterSignature(IndexableSet included, IndexableSet excluded) + public FilterSignature(IndexableSet included, IndexableSet excluded) { Included = included; Excluded = excluded; diff --git a/src/IdAssigner.cs b/src/IdAssigner.cs index 4874fcc..0f11307 100644 --- a/src/IdAssigner.cs +++ b/src/IdAssigner.cs @@ -7,14 +7,17 @@ internal class IdAssigner uint Next; NativeArray AvailableIds = new NativeArray(); - public uint Assign() + public uint Assign(out bool recycled) { - if (!AvailableIds.TryPop(out var id)) + recycled = AvailableIds.TryPop(out var id); + + if (recycled) { - id = Next; - Next += 1; + return id; } + id = Next; + Next += 1; return id; } diff --git a/src/Snapshot.cs b/src/Snapshot.cs index 32c16b4..61bde14 100644 --- a/src/Snapshot.cs +++ b/src/Snapshot.cs @@ -9,51 +9,38 @@ public class Snapshot { private Dictionary ComponentSnapshots = new Dictionary(); - private Dictionary ArchetypeSnapshots = - new Dictionary(); + private Dictionary> Filters = new Dictionary>(); private Dictionary RelationSnapshots = new Dictionary(); - - private Dictionary EntityIndex = new Dictionary(); - private Dictionary> EntityRelationIndex = new Dictionary>(); + private Dictionary> EntityComponentIndex = + new Dictionary>(); + private Dictionary EntityTags = new Dictionary(); private IdAssigner EntityIdAssigner = new IdAssigner(); - public int Count - { - get - { - var count = 0; - - foreach (var snapshot in ArchetypeSnapshots.Values) - { - count += snapshot.Count; - } - - return count; - } - } - public void Restore(World world) { - // restore archetype storage - foreach (var (archetypeSignature, archetypeSnapshot) in ArchetypeSnapshots) - { - var archetype = world.ArchetypeIndex[archetypeSignature]; - archetypeSnapshot.Restore(archetype); - } + // restore id assigner state + EntityIdAssigner.CopyTo(world.EntityIdAssigner); - // restore entity index - world.EntityIndex.Clear(); - foreach (var (id, ArchetypeRecord) in EntityIndex) + // restore filter states + // this could be sped up if we figured out a direct IndexableSet copy + foreach (var (signature, entityList) in Filters) { - world.EntityIndex[id] = ArchetypeRecord; + var filter = world.FilterIndex[signature]; + + filter.Clear(); + + foreach (var entity in entityList) + { + filter.AddEntity(entity); + } } // restore components @@ -63,9 +50,6 @@ public class Snapshot componentSnapshot.Restore(componentStorage); } - // restore id assigner state - EntityIdAssigner.CopyTo(world.EntityIdAssigner); - // restore relation state foreach (var (typeId, relationSnapshot) in RelationSnapshots) { @@ -85,6 +69,19 @@ public class Snapshot } } + // restore entity component index state + // FIXME: arrghghhh this is so slow + foreach (var (id, componentTypeSet) in EntityComponentIndex) + { + world.EntityComponentIndex[id].Clear(); + + foreach (var typeId in componentTypeSet) + { + world.EntityComponentIndex[id].Add(typeId); + } + } + + // restore entity tags foreach (var (id, s) in EntityTags) { world.EntityTags[id] = s; @@ -96,17 +93,10 @@ public class Snapshot // copy id assigner state world.EntityIdAssigner.CopyTo(EntityIdAssigner); - // copy entity index - EntityIndex.Clear(); - foreach (var (id, ArchetypeRecord) in world.EntityIndex) + // copy filter states + foreach (var (_, filter) in world.FilterIndex) { - EntityIndex[id] = ArchetypeRecord; - } - - // copy archetypes - foreach (var archetype in world.ArchetypeIndex.Values) - { - TakeArchetypeSnapshot(archetype); + TakeFilterSnapshot(filter); } // copy components @@ -138,21 +128,44 @@ public class Snapshot } } + // copy entity component index + // FIXME: arghhhh this is so slow + foreach (var (id, componentTypeSet) in world.EntityComponentIndex) + { + if (!EntityComponentIndex.ContainsKey(id)) + { + EntityComponentIndex.Add(id, new IndexableSet()); + } + + EntityComponentIndex[id].Clear(); + + foreach (var typeId in componentTypeSet) + { + EntityComponentIndex[id].Add(typeId); + } + } + + // copy entity tags foreach (var (id, s) in world.EntityTags) { EntityTags[id] = s; } } - private void TakeArchetypeSnapshot(Archetype archetype) + private void TakeFilterSnapshot(Filter filter) { - if (!ArchetypeSnapshots.TryGetValue(archetype.Signature, out var archetypeSnapshot)) + if (!Filters.TryGetValue(filter.Signature, out var entities)) { - archetypeSnapshot = new ArchetypeSnapshot(); - ArchetypeSnapshots.Add(archetype.Signature, archetypeSnapshot); + entities = new List(); + Filters.Add(filter.Signature, entities); } - archetypeSnapshot.Take(archetype); + entities.Clear(); + + foreach (var entity in filter.EntitySet.AsSpan()) + { + entities.Add(entity); + } } private void TakeComponentSnapshot(TypeId typeId, ComponentStorage componentStorage) @@ -177,27 +190,6 @@ public class Snapshot snapshot.Take(relationStorage); } - private class ArchetypeSnapshot - { - private readonly NativeArray Entities; - public int Count => Entities.Count; - - public ArchetypeSnapshot() - { - Entities = new NativeArray(); - } - - public void Take(Archetype archetype) - { - archetype.Entities.CopyTo(Entities); - } - - public void Restore(Archetype archetype) - { - Entities.CopyTo(archetype.Entities); - } - } - private class ComponentSnapshot { private readonly Dictionary EntityIDToStorageIndex = new Dictionary(); diff --git a/src/World.cs b/src/World.cs index 7948022..0dcc689 100644 --- a/src/World.cs +++ b/src/World.cs @@ -5,7 +5,7 @@ using MoonTools.ECS.Collections; namespace MoonTools.ECS { - public class World + public class World : IDisposable { // Get TypeId from a Type private readonly Dictionary TypeToId = new Dictionary(); @@ -17,10 +17,9 @@ namespace MoonTools.ECS // Get element size from a TypeId private readonly Dictionary ElementSizes = new Dictionary(); - // Archetypes - internal readonly Dictionary ArchetypeIndex = new Dictionary(); - internal readonly Dictionary EntityIndex = new Dictionary(); - internal readonly Archetype EmptyArchetype; + // Filters + internal readonly Dictionary FilterIndex = new Dictionary(); + private readonly Dictionary> TypeToFilter = new Dictionary>(); // TODO: can we make the tag an native array of chars at some point? internal Dictionary EntityTags = new Dictionary(); @@ -35,14 +34,12 @@ namespace MoonTools.ECS public FilterBuilder FilterBuilder => new FilterBuilder(this); internal readonly Dictionary ComponentIndex = new Dictionary(); + internal Dictionary> EntityComponentIndex = new Dictionary>(); internal IdAssigner EntityIdAssigner = new IdAssigner(); private IdAssigner TypeIdAssigner = new IdAssigner(); - public World() - { - EmptyArchetype = CreateArchetype(ArchetypeSignature.Empty); - } + private bool IsDisposed; internal TypeId GetTypeId() where T : unmanaged { @@ -51,7 +48,7 @@ namespace MoonTools.ECS return TypeToId[typeof(T)]; } - var typeId = new TypeId(TypeIdAssigner.Assign()); + var typeId = new TypeId(TypeIdAssigner.Assign(out var _)); TypeToId.Add(typeof(T), typeId); ElementSizes.Add(typeId, Unsafe.SizeOf()); @@ -62,6 +59,20 @@ namespace MoonTools.ECS return typeId; } + internal TypeId GetComponentTypeId() where T : unmanaged + { + var typeId = GetTypeId(); + if (ComponentIndex.TryGetValue(typeId, out var componentStorage)) + { + return typeId; + } + + componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]); + ComponentIndex.Add(typeId, componentStorage); + TypeToFilter.Add(typeId, new List()); + return typeId; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private ComponentStorage GetComponentStorage() where T : unmanaged { @@ -71,29 +82,44 @@ namespace MoonTools.ECS return componentStorage; } - componentStorage = new ComponentStorage(ElementSizes[typeId]); + componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]); ComponentIndex.Add(typeId, componentStorage); + TypeToFilter.Add(typeId, new List()); return componentStorage; } - private Archetype CreateArchetype(ArchetypeSignature signature) + // FILTERS + + internal Filter GetFilter(FilterSignature signature) { - var archetype = new Archetype(this, signature); - ArchetypeIndex.Add(signature, archetype); - return archetype; + if (!FilterIndex.TryGetValue(signature, out var filter)) + { + filter = new Filter(this, signature); + + foreach (var typeId in signature.Included) + { + TypeToFilter[typeId].Add(filter); + } + + foreach (var typeId in signature.Excluded) + { + TypeToFilter[typeId].Add(filter); + } + } + + return filter; } // ENTITIES public Entity CreateEntity(string tag = "") { - var entity = new Entity(EntityIdAssigner.Assign()); - EntityIndex.Add(entity, new ArchetypeRecord(EmptyArchetype, EmptyArchetype.Count)); - EmptyArchetype.Append(entity); + var entity = new Entity(EntityIdAssigner.Assign(out var recycled)); - if (!EntityRelationIndex.ContainsKey(entity)) + if (!recycled) { EntityRelationIndex.Add(entity, new IndexableSet()); + EntityComponentIndex.Add(entity, new IndexableSet()); } EntityTags[entity] = tag; @@ -113,15 +139,16 @@ namespace MoonTools.ECS public void Destroy(in Entity entity) { - var record = EntityIndex[entity]; - var archetype = record.Archetype; - var row = record.Row; - // remove all components from storages - for (int i = 0; i < archetype.Signature.Count; i += 1) + foreach (var componentTypeIndex in EntityComponentIndex[entity]) { - var componentStorage = ComponentIndex[archetype.Signature[i]]; + var componentStorage = ComponentIndex[componentTypeIndex]; componentStorage.Remove(entity); + + foreach (var filter in TypeToFilter[componentTypeIndex]) + { + filter.RemoveEntity(entity); + } } // remove all relations from storage @@ -131,18 +158,9 @@ namespace MoonTools.ECS relationStorage.RemoveEntity(entity); } + EntityComponentIndex[entity].Clear(); EntityRelationIndex[entity].Clear(); - // remove from archetype - if (row != archetype.Count - 1) - { - var lastEntity = archetype.Entities[archetype.Count - 1]; - archetype.Entities[row] = lastEntity; - EntityIndex[lastEntity] = new ArchetypeRecord(archetype, row); - } - archetype.Entities.RemoveLastElement(); - EntityIndex.Remove(entity); - // recycle ID EntityIdAssigner.Unassign(entity.ID); } @@ -155,6 +173,11 @@ namespace MoonTools.ECS return storage.Has(entity); } + internal bool Has(in Entity entity, in TypeId typeId) + { + return EntityComponentIndex[entity].Contains(typeId); + } + public bool Some() where T : unmanaged { var storage = GetComponentStorage(); @@ -185,7 +208,12 @@ namespace MoonTools.ECS if (!componentStorage.Set(entity, component)) { - TransferArchetype(entity, FindArchetypeByAdd(entity)); + EntityComponentIndex[entity].Add(componentStorage.TypeId); + + foreach (var filter in TypeToFilter[componentStorage.TypeId]) + { + filter.Check(entity); + } } } @@ -195,89 +223,15 @@ namespace MoonTools.ECS if (componentStorage.Remove(entity)) { - TransferArchetype(entity, FindArchetypeByRemove(entity)); + EntityComponentIndex[entity].Remove(componentStorage.TypeId); + + foreach (var filter in TypeToFilter[componentStorage.TypeId]) + { + filter.Check(entity); + } } } - private Archetype FindArchetypeByAdd(in Entity entity) - { - var componentTypeId = TypeToId[typeof(T)]; - var record = EntityIndex[entity]; - var archetype = record.Archetype; - - if (archetype.AddEdges.TryGetValue(componentTypeId, out var nextArchetype)) - { - return nextArchetype; - } - - var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1); - archetype.Signature.CopyTo(nextSignature); - nextSignature.Insert(componentTypeId); - - if (!ArchetypeIndex.TryGetValue(nextSignature, out nextArchetype)) - { - nextArchetype = CreateArchetype(nextSignature); - } - - archetype.AddEdges.Add(componentTypeId, nextArchetype); - - if (!nextArchetype.RemoveEdges.ContainsKey(componentTypeId)) - { - nextArchetype.RemoveEdges.Add(componentTypeId, archetype); - } - - return nextArchetype; - } - - private Archetype FindArchetypeByRemove(in Entity entity) - { - var componentTypeId = TypeToId[typeof(T)]; - var record = EntityIndex[entity]; - var archetype = record.Archetype; - - if (archetype.RemoveEdges.TryGetValue(componentTypeId, out var nextArchetype)) - { - return nextArchetype; - } - - var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1); - archetype.Signature.CopyTo(nextSignature); - nextSignature.Remove(componentTypeId); - - if (!ArchetypeIndex.TryGetValue(nextSignature, out nextArchetype)) - { - nextArchetype = CreateArchetype(nextSignature); - } - - archetype.RemoveEdges.Add(componentTypeId, nextArchetype); - - if (!nextArchetype.AddEdges.ContainsKey(componentTypeId)) - { - nextArchetype.AddEdges.Add(componentTypeId, archetype); - } - - return nextArchetype; - } - - private void TransferArchetype(in Entity entity, Archetype nextArchetype) - { - var record = EntityIndex[entity]; - var archetype = record.Archetype; - var row = record.Row; - - // fill the gap - if (row != archetype.Count - 1) - { - var lastEntity = archetype.Entities[archetype.Count - 1]; - archetype.Entities[row] = lastEntity; - EntityIndex[lastEntity] = new ArchetypeRecord(archetype, row); - } - - archetype.Entities.RemoveLastElement(); - nextArchetype.Entities.Append(entity); - EntityIndex[entity] = new ArchetypeRecord(nextArchetype, nextArchetype.Count - 1); - } - // RELATIONS private RelationStorage RegisterRelationType(TypeId typeId) @@ -450,7 +404,7 @@ namespace MoonTools.ECS #if DEBUG public ComponentTypeEnumerator Debug_GetAllComponentTypes(Entity entity) { - return new ComponentTypeEnumerator(this, EntityIndex[entity]); + return new ComponentTypeEnumerator(this, EntityComponentIndex[entity]); } public IEnumerable Debug_GetEntities(Type componentType) @@ -473,29 +427,67 @@ namespace MoonTools.ECS public ref struct ComponentTypeEnumerator { private World World; - private ArchetypeRecord Record; + private IndexableSet Types; private int ComponentIndex; public ComponentTypeEnumerator GetEnumerator() => this; internal ComponentTypeEnumerator( World world, - ArchetypeRecord record + IndexableSet types ) { World = world; - Record = record; + Types = types; ComponentIndex = -1; } public bool MoveNext() { ComponentIndex += 1; - return ComponentIndex < Record.Archetype.Signature.Count; + return ComponentIndex < Types.Count; } - public unsafe Type Current => World.IdToType[Record.Archetype.Signature[ComponentIndex]]; + public unsafe Type Current => World.IdToType[Types[ComponentIndex]]; } - #endif + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + foreach (var componentStorage in ComponentIndex.Values) + { + componentStorage.Dispose(); + } + + foreach (var relationStorage in RelationIndex.Values) + { + relationStorage.Dispose(); + } + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + IsDisposed = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~World() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +#endif } }