encompass-cs/encompass-cs/Collections/TypedComponentStore.cs

151 lines
5.1 KiB
C#

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<TComponent> : TypedComponentStore where TComponent : struct
{
private int _nextID = 0;
private readonly Dictionary<int, int> _entityIDToStorageIndex = new Dictionary<int, int>(128);
private readonly Dictionary<int, int> _priorities = new Dictionary<int, int>(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<Entity> AllEntities()
{
return new ReadOnlySpan<Entity>(_storageIndexToEntities, 0, _nextID);
}
public IEnumerable<Entity> AllEntitiesAsEnumerable()
{
return new ArraySegment<Entity>(_storageIndexToEntities, 0, _nextID);
}
public ReadOnlySpan<TComponent> AllComponents()
{
return new ReadOnlySpan<TComponent>(_components, 0, _nextID);
}
public IEnumerable<TComponent> AllComponentsAsEnumerable()
{
return new ArraySegment<TComponent>(_components, 0, _nextID);
}
}
}