encompass-cs/encompass-cs/ComponentManager.cs

210 lines
8.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
internal class ComponentManager
{
private readonly DrawLayerManager drawLayerManager;
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>();
private readonly Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>();
private readonly Dictionary<Guid, HashSet<Guid>> entityIDToComponentIDs = new Dictionary<Guid, HashSet<Guid>>();
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
private readonly Dictionary<Guid, Dictionary<Type, HashSet<Guid>>> entityIDToComponentTypeToComponentIDs = new Dictionary<Guid, Dictionary<Type, HashSet<Guid>>>();
private readonly Dictionary<Type, HashSet<Guid>> typeToComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>();
private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>();
public ComponentManager(DrawLayerManager drawLayerManager)
{
this.drawLayerManager = drawLayerManager;
}
internal void RegisterEntity(Guid entityID)
{
entityIDToComponentIDs.Add(entityID, new HashSet<Guid>());
entityIDToComponentTypeToComponentIDs.Add(entityID, new Dictionary<Type, HashSet<Guid>>());
}
internal Guid NextID()
{
return Guid.NewGuid();
}
internal Guid AddComponent<TComponent>(Entity entity, Guid componentID, TComponent component) where TComponent : struct, IComponent
{
IDToComponent[componentID] = component;
componentIDToType[componentID] = typeof(TComponent);
if (!typeToComponentIDs.ContainsKey(typeof(TComponent)))
{
typeToComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
}
typeToComponentIDs[typeof(TComponent)].Add(componentID);
entityIDToComponentIDs[entity.ID].Add(componentID);
if (!entityIDToComponentTypeToComponentIDs[entity.ID].ContainsKey(typeof(TComponent))) {
entityIDToComponentTypeToComponentIDs[entity.ID].Add(typeof(TComponent), new HashSet<Guid>());
}
entityIDToComponentTypeToComponentIDs[entity.ID][typeof(TComponent)].Add(componentID);
componentIDToEntityID[componentID] = entity.ID;
return componentID;
}
internal Guid AddDrawComponent<TComponent>(Entity entity, Guid componentID, TComponent component, int layer = 0) where TComponent : struct, IComponent
{
AddComponent(entity, componentID, component);
drawLayerManager.RegisterComponentWithLayer(componentID, layer);
return componentID;
}
internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID)
{
return entityIDToComponentIDs.ContainsKey(entityID) ?
entityIDToComponentIDs[entityID] :
Enumerable.Empty<Guid>();
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntity(Guid entityID)
{
return GetComponentIDsByEntityID(entityID).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id]));
}
internal IEnumerable<ValueTuple<Guid, Guid, TComponent>> GetComponentsByType<TComponent>() where TComponent : struct, IComponent
{
return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
typeToComponentIDs[typeof(TComponent)].Select((id) => new ValueTuple<Guid, Guid, TComponent>(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) :
Enumerable.Empty<ValueTuple<Guid, Guid, TComponent>>();
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByType(Type type)
{
return typeToComponentIDs.ContainsKey(type) ?
typeToComponentIDs[type].Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id])) :
Enumerable.Empty<ValueTuple<Guid, IComponent>>();
}
internal IEnumerable<ValueTuple<Guid, TComponent>> GetComponentsByEntityAndType<TComponent>(Guid entityID) where TComponent : struct, IComponent
{
return entityIDToComponentTypeToComponentIDs[entityID].ContainsKey(typeof(TComponent)) ?
entityIDToComponentTypeToComponentIDs[entityID][typeof(TComponent)].Select(id => (id, (TComponent)GetComponentByID(id))) :
Enumerable.Empty<(Guid, TComponent)>();
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntityAndType(Guid entityID, Type type)
{
return entityIDToComponentTypeToComponentIDs[entityID].ContainsKey(type) ?
entityIDToComponentTypeToComponentIDs[entityID][type].Select(id => (id, GetComponentByID(id))) :
Enumerable.Empty<(Guid, IComponent)>();
}
internal IEnumerable<Type> GetAllComponentTypesOfEntity(Guid entityID)
{
return GetComponentIDsByEntityID(entityID).Select((id) => componentIDToType[id]);
}
internal bool EntityHasComponentOfType<TComponent>(Guid entityID) where TComponent : struct, IComponent
{
return GetComponentsByEntityAndType<TComponent>(entityID).Any();
}
internal bool EntityHasComponentOfType(Guid entityID, Type type)
{
return GetComponentsByEntityAndType(entityID, type).Any();
}
internal IComponent GetComponentByID(Guid componentID)
{
return IDToComponent[componentID];
}
internal Type GetComponentTypeByID(Guid componentID)
{
return componentIDToType[componentID];
}
internal Guid GetEntityIDByComponentID(Guid componentID)
{
return componentIDToEntityID[componentID];
}
internal void AddUpdateComponentOperation<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
{
if (pendingUpdates.ContainsKey(componentID))
{
throw new RepeatUpdateComponentException("Component {0} with ID {1} was updated multiple times this frame", typeof(TComponent).Name, componentID);
}
pendingUpdates.Add(componentID, newComponentValue);
}
internal void PerformComponentUpdates()
{
foreach (var idPair in pendingUpdates)
{
IDToComponent[idPair.Key] = idPair.Value;
}
pendingUpdates.Clear();
}
internal void MarkAllComponentsOnEntityForRemoval(Guid entityID)
{
foreach (var componentID in GetComponentIDsByEntityID(entityID))
{
MarkForRemoval(componentID);
}
}
internal void MarkForRemoval(Guid componentID)
{
componentsMarkedForRemoval.Add(componentID);
}
internal void RemoveMarkedComponents()
{
foreach (var componentID in componentsMarkedForRemoval)
{
Remove(componentID);
}
componentsMarkedForRemoval.Clear();
}
private void Remove(Guid componentID)
{
var component = IDToComponent[componentID];
var type = componentIDToType[componentID];
var entityID = componentIDToEntityID[componentID];
if (entityIDToComponentIDs.ContainsKey(entityID))
{
entityIDToComponentIDs[entityID].Remove(componentID);
}
IDToComponent.Remove(componentID);
componentIDToType.Remove(componentID);
componentIDToEntityID.Remove(componentID);
typeToComponentIDs[type].Remove(componentID);
drawLayerManager.UnRegisterComponentWithLayer(componentID);
}
public void RegisterDestroyedEntity(Guid entityID)
{
entityIDToComponentIDs.Remove(entityID);
entityIDToComponentTypeToComponentIDs.Remove(entityID);
}
}
}