using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Encompass { internal abstract class TypedComponentStore { public abstract int Count { get; } public abstract bool Has(int entity); public abstract bool Remove(int entity, int priority); public abstract void ForceRemove(int entity); public abstract void Clear(); public abstract void ClearPriorities(); } internal class TypedComponentStore : TypedComponentStore where TComponent : struct { private int _nextID = 0; private readonly Dictionary _entityIDToStorageIndex = new Dictionary(128); private readonly Dictionary _priorities = new Dictionary(128); private int[] _storageIndexToEntityID = new int[128]; private TComponent[] _components = new TComponent[128]; public override int Count { get => _entityIDToStorageIndex.Count; } public ref TComponent Get(int entityID) { if (!_entityIDToStorageIndex.ContainsKey(entityID)) { throw new Exceptions.NoComponentOfTypeOnEntityException("No component of type {0} exists on Entity with ID {1}", typeof(TComponent), entityID); } return ref _components[_entityIDToStorageIndex[entityID]]; } public void Set(int entityID, in TComponent component) { InternalSet(entityID, component); } public bool Set(int entityID, in TComponent component, int priority) { if (!_priorities.ContainsKey(entityID) || priority <= _priorities[entityID]) // if priorities are equal that means it's the same engine { InternalSet(entityID, component); _priorities[entityID] = priority; return true; } return false; } private void InternalSet(int entityID, in TComponent component) { if (!_entityIDToStorageIndex.ContainsKey(entityID)) { var index = _nextID++; if (index >= _components.Length) { System.Array.Resize(ref _components, _components.Length * 2); System.Array.Resize(ref _storageIndexToEntityID, _storageIndexToEntityID.Length * 2); } _entityIDToStorageIndex[entityID] = index; _storageIndexToEntityID[index] = entityID; } _components[_entityIDToStorageIndex[entityID]] = component; } public override bool Remove(int entityID, int priority) { if (!_priorities.ContainsKey(entityID) || priority <= _priorities[entityID]) // if priorities are equal that means it's the same engine { _priorities[entityID] = priority; ForceRemove(entityID); return true; } return false; } public override void ForceRemove(int entityID) { if (_entityIDToStorageIndex.ContainsKey(entityID)) { var storageIndex = _entityIDToStorageIndex[entityID]; _entityIDToStorageIndex.Remove(entityID); _priorities.Remove(entityID); // move a component into the hole to maintain contiguous memory if (_nextID > 1 && storageIndex != _nextID - 1) { var lastStorageIndex = _nextID - 1; var lastEntityID = _storageIndexToEntityID[lastStorageIndex]; _entityIDToStorageIndex[lastEntityID] = storageIndex; _storageIndexToEntityID[storageIndex] = lastEntityID; _components[storageIndex] = _components[lastStorageIndex]; } _nextID--; } } public override bool Has(int entityID) { return _entityIDToStorageIndex.ContainsKey(entityID); } public override void Clear() { _nextID = 0; _entityIDToStorageIndex.Clear(); _priorities.Clear(); } public override void ClearPriorities() { _priorities.Clear(); } public IEnumerable<(TComponent, int)> All() { for (var i = 0; i < _nextID; i++) { yield return (_components[i], _storageIndexToEntityID[i]); } } } }