encompass-cs/encompass-cs/ComponentManager.cs

264 lines
10 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
// we use byte as a dummy argument to ConcurrentDictionary so we can treat it like a HashSet
namespace Encompass
{
internal class ComponentManager
{
private readonly DrawLayerManager drawLayerManager;
private readonly ConcurrentDictionary<Guid, Type> componentIDToType = new ConcurrentDictionary<Guid, Type>();
private readonly ConcurrentDictionary<Guid, IComponent> IDToComponent = new ConcurrentDictionary<Guid, IComponent>();
private readonly ConcurrentDictionary<Guid, ConcurrentDictionary<Guid, byte>> entityIDToComponentIDs = new ConcurrentDictionary<Guid, ConcurrentDictionary<Guid, byte>>();
private readonly ConcurrentDictionary<Guid, Guid> componentIDToEntityID = new ConcurrentDictionary<Guid, Guid>();
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Guid, byte>> typeToComponentIDs = new ConcurrentDictionary<Type, ConcurrentDictionary<Guid, byte>>();
private readonly ConcurrentDictionary<Guid, byte> activeComponents = new ConcurrentDictionary<Guid, byte>();
private readonly ConcurrentDictionary<Guid, byte> inactiveComponents = new ConcurrentDictionary<Guid, byte>();
private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForDeactivation = new ConcurrentDictionary<Guid, byte>();
private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForRemoval = new ConcurrentDictionary<Guid, byte>();
private readonly ConcurrentDictionary<Guid, IComponent> pendingUpdates = new ConcurrentDictionary<Guid, IComponent>();
//shared references with EntityManager
private readonly ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents;
private readonly ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents;
public ComponentManager(
DrawLayerManager drawLayerManager,
ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents,
ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents
)
{
this.drawLayerManager = drawLayerManager;
this.entitiesWithAddedComponents = entitiesWithAddedComponents;
this.entitiesWithRemovedComponents = entitiesWithRemovedComponents;
}
internal void RegisterEntity(Guid entityID)
{
entityIDToComponentIDs.TryAdd(entityID, new ConcurrentDictionary<Guid, byte>());
}
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.TryAdd(typeof(TComponent), new ConcurrentDictionary<Guid, byte>());
}
typeToComponentIDs[typeof(TComponent)].TryAdd(componentID, 0);
entityIDToComponentIDs[entity.ID].TryAdd(componentID, 0);
componentIDToEntityID[componentID] = entity.ID;
activeComponents.TryAdd(componentID, 0);
entitiesWithAddedComponents.TryAdd(entity.ID, 0);
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].Keys :
Enumerable.Empty<Guid>();
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntity(Guid entityID)
{
return GetComponentIDsByEntityID(entityID).Intersect(activeComponents.Keys).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id]));
}
internal IEnumerable<ValueTuple<Guid, Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
{
return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
typeToComponentIDs[typeof(TComponent)].Keys.Intersect(activeComponents.Keys).Select((id) => new ValueTuple<Guid, Guid, TComponent>(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) :
Enumerable.Empty<ValueTuple<Guid, Guid, TComponent>>();
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetActiveComponentsByType(Type type)
{
return typeToComponentIDs.ContainsKey(type) ?
typeToComponentIDs[type].Keys.Intersect(activeComponents.Keys).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
{
var entityComponentsByType = GetComponentsByEntity(entityID).Where((pair) => componentIDToType[pair.Item1] == typeof(TComponent)).Select((pair) => new ValueTuple<Guid, TComponent>(pair.Item1, (TComponent)pair.Item2));
var activeComponentsByType = GetActiveComponentsByType<TComponent>();
return activeComponentsByType.Select((triple) => (triple.Item2, triple.Item3)).Intersect(entityComponentsByType);
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntityAndType(Guid entityID, Type type)
{
var entityComponents = GetComponentsByEntity(entityID);
var activeComponentsByType = GetActiveComponentsByType(type);
return entityComponents.Intersect(activeComponentsByType);
}
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.TryAdd(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 Activate(Guid componentID)
{
if (inactiveComponents.TryRemove(componentID, out _))
{
activeComponents.TryAdd(componentID, 0);
}
var entityID = GetEntityIDByComponentID(componentID);
entitiesWithAddedComponents.TryAdd(entityID, 0);
}
internal void MarkForDeactivation(Guid componentID)
{
componentsMarkedForDeactivation.TryAdd(componentID, 0);
}
internal void DeactivateMarkedComponents()
{
foreach (var componentID in componentsMarkedForDeactivation.Keys)
{
Deactivate(componentID);
}
componentsMarkedForDeactivation.Clear();
}
private void Deactivate(Guid componentID)
{
if (activeComponents.TryRemove(componentID, out _))
{
inactiveComponents.TryAdd(componentID, 0);
}
var entityID = GetEntityIDByComponentID(componentID);
entitiesWithRemovedComponents.TryAdd(entityID, 0);
}
internal void MarkForRemoval(Guid componentID)
{
componentsMarkedForRemoval.TryAdd(componentID, 0);
}
internal void RemoveMarkedComponents()
{
foreach (var componentID in componentsMarkedForRemoval.Keys)
{
Remove(componentID);
}
componentsMarkedForRemoval.Clear();
}
private void Remove(Guid componentID)
{
var component = IDToComponent[componentID];
var type = componentIDToType[componentID];
activeComponents.TryRemove(componentID, out _);
inactiveComponents.TryRemove(componentID, out _);
var entityID = componentIDToEntityID[componentID];
if (entityIDToComponentIDs.ContainsKey(entityID))
{
entityIDToComponentIDs[entityID].TryRemove(componentID, out _);
}
IDToComponent.TryRemove(componentID, out _);
componentIDToType.TryRemove(componentID, out _);
componentIDToEntityID.TryRemove(componentID, out _);
typeToComponentIDs[type].TryRemove(componentID, out _);
drawLayerManager.UnRegisterComponentWithLayer(componentID);
entitiesWithRemovedComponents.TryAdd(entityID, 0);
}
public void RegisterDestroyedEntity(Guid entityID)
{
entityIDToComponentIDs.TryRemove(entityID, out _);
}
}
}