using System; 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 Entity[] _storageIndexToEntities = new Entity[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 ref TComponent Singular() { return ref _components[0]; } public ref Entity SingularEntity() { return ref _storageIndexToEntities[0]; } 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 _storageIndexToEntities, _storageIndexToEntities.Length * 2); } _entityIDToStorageIndex[entityID] = index; _storageIndexToEntities[index] = new Entity(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; ref readonly var lastEntity = ref _storageIndexToEntities[lastStorageIndex]; _entityIDToStorageIndex[lastEntity.ID] = storageIndex; _storageIndexToEntities[storageIndex] = lastEntity; _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 ReadOnlySpan AllEntities() { return new ReadOnlySpan(_storageIndexToEntities, 0, _nextID); } public IEnumerable AllEntitiesAsEnumerable() { return new ArraySegment(_storageIndexToEntities, 0, _nextID); } public ReadOnlySpan AllComponents() { return new ReadOnlySpan(_components, 0, _nextID); } public IEnumerable AllComponentsAsEnumerable() { return new ArraySegment(_components, 0, _nextID); } } }