264 lines
10 KiB
C#
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 _);
|
|
}
|
|
}
|
|
}
|