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 | ||||
| 
 | ||||
| - thread safety | ||||
| - create ConcurrentHashSet based on ConcurrentDictionary | ||||
|  | @ -1,38 +1,40 @@ | |||
| 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 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 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 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 List<Guid> inactiveComponents = new List<Guid>(); | ||||
|         private readonly ConcurrentDictionary<Guid, byte> activeComponents = new ConcurrentDictionary<Guid, byte>(); | ||||
|         private readonly ConcurrentDictionary<Guid, byte> inactiveComponents = new ConcurrentDictionary<Guid, byte>(); | ||||
| 
 | ||||
|         private readonly HashSet<Guid> componentsMarkedForActivation = new HashSet<Guid>(); | ||||
|         private readonly HashSet<Guid> componentsMarkedForDeactivation = new HashSet<Guid>(); | ||||
|         private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>(); | ||||
|         private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForDeactivation = new ConcurrentDictionary<Guid, byte>(); | ||||
|         private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForRemoval = new ConcurrentDictionary<Guid, byte>(); | ||||
| 
 | ||||
|         private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>(); | ||||
|         private readonly ConcurrentDictionary<Guid, IComponent> pendingUpdates = new ConcurrentDictionary<Guid, IComponent>(); | ||||
| 
 | ||||
|         //shared references with EntityManager | ||||
|         private readonly HashSet<Guid> entitiesWithAddedComponents; | ||||
|         private readonly HashSet<Guid> entitiesWithRemovedComponents; | ||||
|         private readonly ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents; | ||||
|         private readonly ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents; | ||||
| 
 | ||||
|         public ComponentManager( | ||||
|             DrawLayerManager drawLayerManager, | ||||
|             HashSet<Guid> entitiesWithAddedComponents, | ||||
|             HashSet<Guid> entitiesWithRemovedComponents | ||||
|             ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents, | ||||
|             ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents | ||||
|         ) | ||||
|         { | ||||
|             this.drawLayerManager = drawLayerManager; | ||||
|  | @ -42,7 +44,7 @@ namespace Encompass | |||
| 
 | ||||
|         internal void RegisterEntity(Guid entityID) | ||||
|         { | ||||
|             entityIDToComponentIDs.Add(entityID, new HashSet<Guid>()); | ||||
|             entityIDToComponentIDs.TryAdd(entityID, new ConcurrentDictionary<Guid, byte>()); | ||||
|         } | ||||
| 
 | ||||
|         internal Guid NextID() | ||||
|  | @ -57,17 +59,17 @@ namespace Encompass | |||
| 
 | ||||
|             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; | ||||
| 
 | ||||
|             activeComponents.Add(componentID); | ||||
|             activeComponents.TryAdd(componentID, 0); | ||||
| 
 | ||||
|             entitiesWithAddedComponents.Add(entity.ID); | ||||
|             entitiesWithAddedComponents.TryAdd(entity.ID, 0); | ||||
| 
 | ||||
|             return componentID; | ||||
|         } | ||||
|  | @ -82,26 +84,26 @@ namespace Encompass | |||
|         internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID) | ||||
|         { | ||||
|             return entityIDToComponentIDs.ContainsKey(entityID) ? | ||||
|                 entityIDToComponentIDs[entityID] : | ||||
|                 entityIDToComponentIDs[entityID].Keys : | ||||
|                 Enumerable.Empty<Guid>(); | ||||
|         } | ||||
| 
 | ||||
|         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 | ||||
|         { | ||||
|             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>>(); | ||||
|         } | ||||
| 
 | ||||
|         internal IEnumerable<ValueTuple<Guid, IComponent>> GetActiveComponentsByType(Type 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>>(); | ||||
|         } | ||||
| 
 | ||||
|  | @ -156,7 +158,7 @@ namespace Encompass | |||
|                 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() | ||||
|  | @ -179,23 +181,23 @@ namespace Encompass | |||
| 
 | ||||
|         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); | ||||
|             entitiesWithAddedComponents.Add(entityID); | ||||
|             entitiesWithAddedComponents.TryAdd(entityID, 0); | ||||
|         } | ||||
| 
 | ||||
|         internal void MarkForDeactivation(Guid componentID) | ||||
|         { | ||||
|             componentsMarkedForDeactivation.Add(componentID); | ||||
|             componentsMarkedForDeactivation.TryAdd(componentID, 0); | ||||
|         } | ||||
| 
 | ||||
|         internal void DeactivateMarkedComponents() | ||||
|         { | ||||
|             foreach (var componentID in componentsMarkedForDeactivation) | ||||
|             foreach (var componentID in componentsMarkedForDeactivation.Keys) | ||||
|             { | ||||
|                 Deactivate(componentID); | ||||
|             } | ||||
|  | @ -205,23 +207,23 @@ namespace Encompass | |||
| 
 | ||||
|         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); | ||||
|             entitiesWithRemovedComponents.Add(entityID); | ||||
|             entitiesWithRemovedComponents.TryAdd(entityID, 0); | ||||
|         } | ||||
| 
 | ||||
|         internal void MarkForRemoval(Guid componentID) | ||||
|         { | ||||
|             componentsMarkedForRemoval.Add(componentID); | ||||
|             componentsMarkedForRemoval.TryAdd(componentID, 0); | ||||
|         } | ||||
| 
 | ||||
|         internal void RemoveMarkedComponents() | ||||
|         { | ||||
|             foreach (var componentID in componentsMarkedForRemoval) | ||||
|             foreach (var componentID in componentsMarkedForRemoval.Keys) | ||||
|             { | ||||
|                 Remove(componentID); | ||||
|             } | ||||
|  | @ -234,28 +236,28 @@ namespace Encompass | |||
|             var component = IDToComponent[componentID]; | ||||
|             var type = componentIDToType[componentID]; | ||||
| 
 | ||||
|             activeComponents.Remove(componentID); | ||||
|             inactiveComponents.Remove(componentID); | ||||
|             activeComponents.TryRemove(componentID, out _); | ||||
|             inactiveComponents.TryRemove(componentID, out _); | ||||
| 
 | ||||
|             var entityID = componentIDToEntityID[componentID]; | ||||
|             if (entityIDToComponentIDs.ContainsKey(entityID)) | ||||
|             { | ||||
|                 entityIDToComponentIDs[entityID].Remove(componentID); | ||||
|                 entityIDToComponentIDs[entityID].TryRemove(componentID, out _); | ||||
|             } | ||||
| 
 | ||||
|             IDToComponent.Remove(componentID); | ||||
|             componentIDToType.Remove(componentID); | ||||
|             componentIDToEntityID.Remove(componentID); | ||||
|             typeToComponentIDs[type].Remove(componentID); | ||||
|             IDToComponent.TryRemove(componentID, out _); | ||||
|             componentIDToType.TryRemove(componentID, out _); | ||||
|             componentIDToEntityID.TryRemove(componentID, out _); | ||||
|             typeToComponentIDs[type].TryRemove(componentID, out _); | ||||
| 
 | ||||
|             drawLayerManager.UnRegisterComponentWithLayer(componentID); | ||||
| 
 | ||||
|             entitiesWithRemovedComponents.Add(entityID); | ||||
|             entitiesWithRemovedComponents.TryAdd(entityID, 0); | ||||
|         } | ||||
| 
 | ||||
|         public void RegisterDestroyedEntity(Guid entityID) | ||||
|         { | ||||
|             entityIDToComponentIDs.Remove(entityID); | ||||
|             entityIDToComponentIDs.TryRemove(entityID, out _); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| using System.Collections.Concurrent; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
|  | @ -5,22 +6,22 @@ namespace Encompass | |||
| { | ||||
|     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 Dictionary<Type, HashSet<IEntityTracker>> componentTypeToEntityTrackers = new Dictionary<Type, HashSet<IEntityTracker>>(); | ||||
|         private readonly Dictionary<Guid, HashSet<IEntityTracker>> entityToEntityTrackers = new Dictionary<Guid, HashSet<IEntityTracker>>(); | ||||
| 
 | ||||
|         private readonly HashSet<Guid> entitiesWithAddedComponents; | ||||
|         private readonly HashSet<Guid> entitiesWithRemovedComponents; | ||||
|         private readonly ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents; | ||||
|         private readonly ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents; | ||||
| 
 | ||||
|         private readonly ComponentManager componentManager; | ||||
| 
 | ||||
|         public EntityManager( | ||||
|             ComponentManager componentManager, | ||||
|             HashSet<Guid> entitiesWithAddedComponents, | ||||
|             HashSet<Guid> entitiesWithRemovedComponents | ||||
|             ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents, | ||||
|             ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents | ||||
|         ) | ||||
|         { | ||||
|             this.componentManager = componentManager; | ||||
|  | @ -57,7 +58,7 @@ namespace Encompass | |||
|             foreach (var entityID in entitiesMarkedForDestroy) | ||||
|             { | ||||
|                 componentManager.MarkAllComponentsOnEntityForRemoval(entityID); | ||||
|                 IDToEntity.Remove(entityID); | ||||
|                 IDToEntity.TryRemove(entityID, out _); | ||||
|                 entityToEntityTrackers.Remove(entityID); | ||||
|                 componentManager.RegisterDestroyedEntity(entityID); | ||||
|             } | ||||
|  | @ -96,17 +97,17 @@ namespace Encompass | |||
| 
 | ||||
|         public void RegisterDirtyEntityWithAddedComponents(Guid entityID) | ||||
|         { | ||||
|             entitiesWithAddedComponents.Add(entityID); | ||||
|             entitiesWithAddedComponents.TryAdd(entityID, 0); | ||||
|         } | ||||
| 
 | ||||
|         public void RegisterDirtyEntityWithRemovedComponents(Guid entityID) | ||||
|         { | ||||
|             entitiesWithRemovedComponents.Add(entityID); | ||||
|             entitiesWithRemovedComponents.TryAdd(entityID, 0); | ||||
|         } | ||||
| 
 | ||||
|         public void CheckEntitiesWithAddedComponents() | ||||
|         { | ||||
|             foreach (var entityID in entitiesWithAddedComponents) | ||||
|             foreach (var entityID in entitiesWithAddedComponents.Keys) | ||||
|             { | ||||
|                 CheckAndRegisterEntity(entityID); | ||||
|             } | ||||
|  | @ -116,7 +117,7 @@ namespace Encompass | |||
| 
 | ||||
|         public void CheckEntitiesWithRemovedComponents() | ||||
|         { | ||||
|             foreach (var entityID in entitiesWithRemovedComponents) | ||||
|             foreach (var entityID in entitiesWithRemovedComponents.Keys) | ||||
|             { | ||||
|                 if (entityToEntityTrackers.ContainsKey(entityID)) | ||||
|                 { | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| using System.Collections.Concurrent; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Reflection; | ||||
|  | @ -32,8 +33,8 @@ namespace Encompass | |||
| 
 | ||||
|         public WorldBuilder() | ||||
|         { | ||||
|             var entitiesWithAddedComponents = new HashSet<Guid>(); | ||||
|             var entitiesWithRemovedComponents = new HashSet<Guid>(); | ||||
|             var entitiesWithAddedComponents = new ConcurrentDictionary<Guid, byte>(); | ||||
|             var entitiesWithRemovedComponents = new ConcurrentDictionary<Guid, byte>(); | ||||
|             drawLayerManager = new DrawLayerManager(); | ||||
|             componentManager = new ComponentManager(drawLayerManager, entitiesWithAddedComponents, entitiesWithRemovedComponents); | ||||
|             entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue