thread safe data structures
parent
f3a7331ae9
commit
6aca668619
1
TODO
1
TODO
|
@ -4,3 +4,4 @@
|
||||||
- emit two packages: one for dev, which includes expensive runtime validation checks, and one for release, which disables them
|
- emit two packages: one for dev, which includes expensive runtime validation checks, and one for release, which disables them
|
||||||
|
|
||||||
- thread safety
|
- thread safety
|
||||||
|
- create ConcurrentHashSet based on ConcurrentDictionary
|
|
@ -1,38 +1,40 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Encompass.Exceptions;
|
using Encompass.Exceptions;
|
||||||
|
|
||||||
|
// we use byte as a dummy argument to ConcurrentDictionary so we can treat it like a HashSet
|
||||||
|
|
||||||
namespace Encompass
|
namespace Encompass
|
||||||
{
|
{
|
||||||
internal class ComponentManager
|
internal class ComponentManager
|
||||||
{
|
{
|
||||||
private readonly DrawLayerManager drawLayerManager;
|
private readonly DrawLayerManager drawLayerManager;
|
||||||
|
|
||||||
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>();
|
private readonly ConcurrentDictionary<Guid, Type> componentIDToType = new ConcurrentDictionary<Guid, Type>();
|
||||||
private readonly Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>();
|
private readonly ConcurrentDictionary<Guid, IComponent> IDToComponent = new ConcurrentDictionary<Guid, IComponent>();
|
||||||
private readonly Dictionary<Guid, HashSet<Guid>> entityIDToComponentIDs = new Dictionary<Guid, HashSet<Guid>>();
|
private readonly ConcurrentDictionary<Guid, ConcurrentDictionary<Guid, byte>> entityIDToComponentIDs = new ConcurrentDictionary<Guid, ConcurrentDictionary<Guid, byte>>();
|
||||||
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
|
private readonly ConcurrentDictionary<Guid, Guid> componentIDToEntityID = new ConcurrentDictionary<Guid, Guid>();
|
||||||
|
|
||||||
private readonly Dictionary<Type, HashSet<Guid>> typeToComponentIDs = new Dictionary<Type, HashSet<Guid>>();
|
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Guid, byte>> typeToComponentIDs = new ConcurrentDictionary<Type, ConcurrentDictionary<Guid, byte>>();
|
||||||
|
|
||||||
private readonly List<Guid> activeComponents = new List<Guid>();
|
private readonly ConcurrentDictionary<Guid, byte> activeComponents = new ConcurrentDictionary<Guid, byte>();
|
||||||
private readonly List<Guid> inactiveComponents = new List<Guid>();
|
private readonly ConcurrentDictionary<Guid, byte> inactiveComponents = new ConcurrentDictionary<Guid, byte>();
|
||||||
|
|
||||||
private readonly HashSet<Guid> componentsMarkedForActivation = new HashSet<Guid>();
|
private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForDeactivation = new ConcurrentDictionary<Guid, byte>();
|
||||||
private readonly HashSet<Guid> componentsMarkedForDeactivation = new HashSet<Guid>();
|
private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForRemoval = new ConcurrentDictionary<Guid, byte>();
|
||||||
private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>();
|
|
||||||
|
|
||||||
private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>();
|
private readonly ConcurrentDictionary<Guid, IComponent> pendingUpdates = new ConcurrentDictionary<Guid, IComponent>();
|
||||||
|
|
||||||
//shared references with EntityManager
|
//shared references with EntityManager
|
||||||
private readonly HashSet<Guid> entitiesWithAddedComponents;
|
private readonly ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents;
|
||||||
private readonly HashSet<Guid> entitiesWithRemovedComponents;
|
private readonly ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents;
|
||||||
|
|
||||||
public ComponentManager(
|
public ComponentManager(
|
||||||
DrawLayerManager drawLayerManager,
|
DrawLayerManager drawLayerManager,
|
||||||
HashSet<Guid> entitiesWithAddedComponents,
|
ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents,
|
||||||
HashSet<Guid> entitiesWithRemovedComponents
|
ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.drawLayerManager = drawLayerManager;
|
this.drawLayerManager = drawLayerManager;
|
||||||
|
@ -42,7 +44,7 @@ namespace Encompass
|
||||||
|
|
||||||
internal void RegisterEntity(Guid entityID)
|
internal void RegisterEntity(Guid entityID)
|
||||||
{
|
{
|
||||||
entityIDToComponentIDs.Add(entityID, new HashSet<Guid>());
|
entityIDToComponentIDs.TryAdd(entityID, new ConcurrentDictionary<Guid, byte>());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Guid NextID()
|
internal Guid NextID()
|
||||||
|
@ -57,17 +59,17 @@ namespace Encompass
|
||||||
|
|
||||||
if (!typeToComponentIDs.ContainsKey(typeof(TComponent)))
|
if (!typeToComponentIDs.ContainsKey(typeof(TComponent)))
|
||||||
{
|
{
|
||||||
typeToComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
|
typeToComponentIDs.TryAdd(typeof(TComponent), new ConcurrentDictionary<Guid, byte>());
|
||||||
}
|
}
|
||||||
|
|
||||||
typeToComponentIDs[typeof(TComponent)].Add(componentID);
|
typeToComponentIDs[typeof(TComponent)].TryAdd(componentID, 0);
|
||||||
|
|
||||||
entityIDToComponentIDs[entity.ID].Add(componentID);
|
entityIDToComponentIDs[entity.ID].TryAdd(componentID, 0);
|
||||||
componentIDToEntityID[componentID] = entity.ID;
|
componentIDToEntityID[componentID] = entity.ID;
|
||||||
|
|
||||||
activeComponents.Add(componentID);
|
activeComponents.TryAdd(componentID, 0);
|
||||||
|
|
||||||
entitiesWithAddedComponents.Add(entity.ID);
|
entitiesWithAddedComponents.TryAdd(entity.ID, 0);
|
||||||
|
|
||||||
return componentID;
|
return componentID;
|
||||||
}
|
}
|
||||||
|
@ -82,26 +84,26 @@ namespace Encompass
|
||||||
internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID)
|
internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID)
|
||||||
{
|
{
|
||||||
return entityIDToComponentIDs.ContainsKey(entityID) ?
|
return entityIDToComponentIDs.ContainsKey(entityID) ?
|
||||||
entityIDToComponentIDs[entityID] :
|
entityIDToComponentIDs[entityID].Keys :
|
||||||
Enumerable.Empty<Guid>();
|
Enumerable.Empty<Guid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntity(Guid entityID)
|
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntity(Guid entityID)
|
||||||
{
|
{
|
||||||
return GetComponentIDsByEntityID(entityID).Intersect(activeComponents).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id]));
|
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
|
internal IEnumerable<ValueTuple<Guid, Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
|
return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
|
||||||
typeToComponentIDs[typeof(TComponent)].Intersect(activeComponents).Select((id) => new ValueTuple<Guid, Guid, TComponent>(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) :
|
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>>();
|
Enumerable.Empty<ValueTuple<Guid, Guid, TComponent>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IEnumerable<ValueTuple<Guid, IComponent>> GetActiveComponentsByType(Type type)
|
internal IEnumerable<ValueTuple<Guid, IComponent>> GetActiveComponentsByType(Type type)
|
||||||
{
|
{
|
||||||
return typeToComponentIDs.ContainsKey(type) ?
|
return typeToComponentIDs.ContainsKey(type) ?
|
||||||
typeToComponentIDs[type].Intersect(activeComponents).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id])) :
|
typeToComponentIDs[type].Keys.Intersect(activeComponents.Keys).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id])) :
|
||||||
Enumerable.Empty<ValueTuple<Guid, IComponent>>();
|
Enumerable.Empty<ValueTuple<Guid, IComponent>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +158,7 @@ namespace Encompass
|
||||||
throw new RepeatUpdateComponentException("Component {0} with ID {1} was updated multiple times this frame", typeof(TComponent).Name, componentID);
|
throw new RepeatUpdateComponentException("Component {0} with ID {1} was updated multiple times this frame", typeof(TComponent).Name, componentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingUpdates.Add(componentID, newComponentValue);
|
pendingUpdates.TryAdd(componentID, newComponentValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void PerformComponentUpdates()
|
internal void PerformComponentUpdates()
|
||||||
|
@ -179,23 +181,23 @@ namespace Encompass
|
||||||
|
|
||||||
internal void Activate(Guid componentID)
|
internal void Activate(Guid componentID)
|
||||||
{
|
{
|
||||||
if (inactiveComponents.Remove(componentID))
|
if (inactiveComponents.TryRemove(componentID, out _))
|
||||||
{
|
{
|
||||||
activeComponents.Add(componentID);
|
activeComponents.TryAdd(componentID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var entityID = GetEntityIDByComponentID(componentID);
|
var entityID = GetEntityIDByComponentID(componentID);
|
||||||
entitiesWithAddedComponents.Add(entityID);
|
entitiesWithAddedComponents.TryAdd(entityID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void MarkForDeactivation(Guid componentID)
|
internal void MarkForDeactivation(Guid componentID)
|
||||||
{
|
{
|
||||||
componentsMarkedForDeactivation.Add(componentID);
|
componentsMarkedForDeactivation.TryAdd(componentID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void DeactivateMarkedComponents()
|
internal void DeactivateMarkedComponents()
|
||||||
{
|
{
|
||||||
foreach (var componentID in componentsMarkedForDeactivation)
|
foreach (var componentID in componentsMarkedForDeactivation.Keys)
|
||||||
{
|
{
|
||||||
Deactivate(componentID);
|
Deactivate(componentID);
|
||||||
}
|
}
|
||||||
|
@ -205,23 +207,23 @@ namespace Encompass
|
||||||
|
|
||||||
private void Deactivate(Guid componentID)
|
private void Deactivate(Guid componentID)
|
||||||
{
|
{
|
||||||
if (activeComponents.Remove(componentID))
|
if (activeComponents.TryRemove(componentID, out _))
|
||||||
{
|
{
|
||||||
inactiveComponents.Add(componentID);
|
inactiveComponents.TryAdd(componentID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var entityID = GetEntityIDByComponentID(componentID);
|
var entityID = GetEntityIDByComponentID(componentID);
|
||||||
entitiesWithRemovedComponents.Add(entityID);
|
entitiesWithRemovedComponents.TryAdd(entityID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void MarkForRemoval(Guid componentID)
|
internal void MarkForRemoval(Guid componentID)
|
||||||
{
|
{
|
||||||
componentsMarkedForRemoval.Add(componentID);
|
componentsMarkedForRemoval.TryAdd(componentID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveMarkedComponents()
|
internal void RemoveMarkedComponents()
|
||||||
{
|
{
|
||||||
foreach (var componentID in componentsMarkedForRemoval)
|
foreach (var componentID in componentsMarkedForRemoval.Keys)
|
||||||
{
|
{
|
||||||
Remove(componentID);
|
Remove(componentID);
|
||||||
}
|
}
|
||||||
|
@ -234,28 +236,28 @@ namespace Encompass
|
||||||
var component = IDToComponent[componentID];
|
var component = IDToComponent[componentID];
|
||||||
var type = componentIDToType[componentID];
|
var type = componentIDToType[componentID];
|
||||||
|
|
||||||
activeComponents.Remove(componentID);
|
activeComponents.TryRemove(componentID, out _);
|
||||||
inactiveComponents.Remove(componentID);
|
inactiveComponents.TryRemove(componentID, out _);
|
||||||
|
|
||||||
var entityID = componentIDToEntityID[componentID];
|
var entityID = componentIDToEntityID[componentID];
|
||||||
if (entityIDToComponentIDs.ContainsKey(entityID))
|
if (entityIDToComponentIDs.ContainsKey(entityID))
|
||||||
{
|
{
|
||||||
entityIDToComponentIDs[entityID].Remove(componentID);
|
entityIDToComponentIDs[entityID].TryRemove(componentID, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
IDToComponent.Remove(componentID);
|
IDToComponent.TryRemove(componentID, out _);
|
||||||
componentIDToType.Remove(componentID);
|
componentIDToType.TryRemove(componentID, out _);
|
||||||
componentIDToEntityID.Remove(componentID);
|
componentIDToEntityID.TryRemove(componentID, out _);
|
||||||
typeToComponentIDs[type].Remove(componentID);
|
typeToComponentIDs[type].TryRemove(componentID, out _);
|
||||||
|
|
||||||
drawLayerManager.UnRegisterComponentWithLayer(componentID);
|
drawLayerManager.UnRegisterComponentWithLayer(componentID);
|
||||||
|
|
||||||
entitiesWithRemovedComponents.Add(entityID);
|
entitiesWithRemovedComponents.TryAdd(entityID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterDestroyedEntity(Guid entityID)
|
public void RegisterDestroyedEntity(Guid entityID)
|
||||||
{
|
{
|
||||||
entityIDToComponentIDs.Remove(entityID);
|
entityIDToComponentIDs.TryRemove(entityID, out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -5,22 +6,22 @@ namespace Encompass
|
||||||
{
|
{
|
||||||
internal class EntityManager
|
internal class EntityManager
|
||||||
{
|
{
|
||||||
private readonly Dictionary<Guid, Entity> IDToEntity = new Dictionary<Guid, Entity>();
|
private readonly ConcurrentDictionary<Guid, Entity> IDToEntity = new ConcurrentDictionary<Guid, Entity>();
|
||||||
|
|
||||||
private readonly HashSet<Guid> entitiesMarkedForDestroy = new HashSet<Guid>();
|
private readonly HashSet<Guid> entitiesMarkedForDestroy = new HashSet<Guid>();
|
||||||
|
|
||||||
private readonly Dictionary<Type, HashSet<IEntityTracker>> componentTypeToEntityTrackers = new Dictionary<Type, HashSet<IEntityTracker>>();
|
private readonly Dictionary<Type, HashSet<IEntityTracker>> componentTypeToEntityTrackers = new Dictionary<Type, HashSet<IEntityTracker>>();
|
||||||
private readonly Dictionary<Guid, HashSet<IEntityTracker>> entityToEntityTrackers = new Dictionary<Guid, HashSet<IEntityTracker>>();
|
private readonly Dictionary<Guid, HashSet<IEntityTracker>> entityToEntityTrackers = new Dictionary<Guid, HashSet<IEntityTracker>>();
|
||||||
|
|
||||||
private readonly HashSet<Guid> entitiesWithAddedComponents;
|
private readonly ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents;
|
||||||
private readonly HashSet<Guid> entitiesWithRemovedComponents;
|
private readonly ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents;
|
||||||
|
|
||||||
private readonly ComponentManager componentManager;
|
private readonly ComponentManager componentManager;
|
||||||
|
|
||||||
public EntityManager(
|
public EntityManager(
|
||||||
ComponentManager componentManager,
|
ComponentManager componentManager,
|
||||||
HashSet<Guid> entitiesWithAddedComponents,
|
ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents,
|
||||||
HashSet<Guid> entitiesWithRemovedComponents
|
ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.componentManager = componentManager;
|
this.componentManager = componentManager;
|
||||||
|
@ -57,7 +58,7 @@ namespace Encompass
|
||||||
foreach (var entityID in entitiesMarkedForDestroy)
|
foreach (var entityID in entitiesMarkedForDestroy)
|
||||||
{
|
{
|
||||||
componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
|
componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
|
||||||
IDToEntity.Remove(entityID);
|
IDToEntity.TryRemove(entityID, out _);
|
||||||
entityToEntityTrackers.Remove(entityID);
|
entityToEntityTrackers.Remove(entityID);
|
||||||
componentManager.RegisterDestroyedEntity(entityID);
|
componentManager.RegisterDestroyedEntity(entityID);
|
||||||
}
|
}
|
||||||
|
@ -96,17 +97,17 @@ namespace Encompass
|
||||||
|
|
||||||
public void RegisterDirtyEntityWithAddedComponents(Guid entityID)
|
public void RegisterDirtyEntityWithAddedComponents(Guid entityID)
|
||||||
{
|
{
|
||||||
entitiesWithAddedComponents.Add(entityID);
|
entitiesWithAddedComponents.TryAdd(entityID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterDirtyEntityWithRemovedComponents(Guid entityID)
|
public void RegisterDirtyEntityWithRemovedComponents(Guid entityID)
|
||||||
{
|
{
|
||||||
entitiesWithRemovedComponents.Add(entityID);
|
entitiesWithRemovedComponents.TryAdd(entityID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckEntitiesWithAddedComponents()
|
public void CheckEntitiesWithAddedComponents()
|
||||||
{
|
{
|
||||||
foreach (var entityID in entitiesWithAddedComponents)
|
foreach (var entityID in entitiesWithAddedComponents.Keys)
|
||||||
{
|
{
|
||||||
CheckAndRegisterEntity(entityID);
|
CheckAndRegisterEntity(entityID);
|
||||||
}
|
}
|
||||||
|
@ -116,7 +117,7 @@ namespace Encompass
|
||||||
|
|
||||||
public void CheckEntitiesWithRemovedComponents()
|
public void CheckEntitiesWithRemovedComponents()
|
||||||
{
|
{
|
||||||
foreach (var entityID in entitiesWithRemovedComponents)
|
foreach (var entityID in entitiesWithRemovedComponents.Keys)
|
||||||
{
|
{
|
||||||
if (entityToEntityTrackers.ContainsKey(entityID))
|
if (entityToEntityTrackers.ContainsKey(entityID))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
@ -32,8 +33,8 @@ namespace Encompass
|
||||||
|
|
||||||
public WorldBuilder()
|
public WorldBuilder()
|
||||||
{
|
{
|
||||||
var entitiesWithAddedComponents = new HashSet<Guid>();
|
var entitiesWithAddedComponents = new ConcurrentDictionary<Guid, byte>();
|
||||||
var entitiesWithRemovedComponents = new HashSet<Guid>();
|
var entitiesWithRemovedComponents = new ConcurrentDictionary<Guid, byte>();
|
||||||
drawLayerManager = new DrawLayerManager();
|
drawLayerManager = new DrawLayerManager();
|
||||||
componentManager = new ComponentManager(drawLayerManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
|
componentManager = new ComponentManager(drawLayerManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
|
||||||
entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
|
entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
|
||||||
|
|
Loading…
Reference in New Issue