namespace MoonTools.ECS; internal abstract class ComponentStorage { public abstract void Remove(int entityID); } internal class ComponentStorage : ComponentStorage where TComponent : struct { private int nextID; private IDStorage idStorage = new IDStorage(); private readonly Dictionary entityIDToStorageIndex = new Dictionary(); private Entity[] storageIndexToEntities = new Entity[64]; private TComponent[] components = new TComponent[64]; public bool Any() { return nextID > 0; } public bool Has(int entityID) { return entityIDToStorageIndex.ContainsKey(entityID); } public ref readonly TComponent Get(int entityID) { return ref components[entityIDToStorageIndex[entityID]]; } public void Set(int entityID, in TComponent component) { if (!entityIDToStorageIndex.ContainsKey(entityID)) { var index = nextID; nextID += 1; if (index >= components.Length) { Array.Resize(ref components, components.Length * 2); Array.Resize(ref storageIndexToEntities, storageIndexToEntities.Length * 2); } entityIDToStorageIndex[entityID] = index; storageIndexToEntities[index] = new Entity(entityID); } components[entityIDToStorageIndex[entityID]] = component; } public override void Remove(int entityID) { if (entityIDToStorageIndex.ContainsKey(entityID)) { var storageIndex = entityIDToStorageIndex[entityID]; entityIDToStorageIndex.Remove(entityID); var lastElementIndex = nextID - 1; // move a component into the hole to maintain contiguous memory if (entityIDToStorageIndex.Count > 0 && storageIndex != lastElementIndex) { var lastEntity = storageIndexToEntities[lastElementIndex]; entityIDToStorageIndex[lastEntity.ID] = storageIndex; storageIndexToEntities[storageIndex] = lastEntity; components[storageIndex] = components[lastElementIndex]; } nextID -= 1; } } public void Clear() { nextID = 0; entityIDToStorageIndex.Clear(); } public ReadOnlySpan AllEntities() { return new ReadOnlySpan(storageIndexToEntities, 0, nextID); } public ReadOnlySpan AllComponents() { return new ReadOnlySpan(components, 0, nextID); } }