allow only one component per type per entity

pull/5/head
Evan Hemsley 2019-08-10 17:34:00 -07:00
parent 314a064b4e
commit c1e206ef49
11 changed files with 221 additions and 269 deletions

View File

@ -15,12 +15,12 @@ namespace Encompass
private readonly Dictionary<Guid, PooledSet<Guid>> entityIDToComponentIDs = new Dictionary<Guid, PooledSet<Guid>>(); private readonly Dictionary<Guid, PooledSet<Guid>> entityIDToComponentIDs = new Dictionary<Guid, PooledSet<Guid>>();
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>(); private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
private readonly Dictionary<Guid, PooledDictionary<Type, HashSet<Guid>>> entityIDToComponentTypeToComponentIDs = new Dictionary<Guid, PooledDictionary<Type, HashSet<Guid>>>(); private readonly Dictionary<Guid, PooledDictionary<Type, Guid>> entityIDToComponentTypeToComponentID = new Dictionary<Guid, PooledDictionary<Type, Guid>>();
private readonly Dictionary<Type, HashSet<Guid>> typeToComponentIDs = new Dictionary<Type, HashSet<Guid>>(); private readonly Dictionary<Type, HashSet<Guid>> typeToComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly List<(Entity, Type, Guid, IComponent)> componentAddData = new List<(Entity, Type, Guid, IComponent)>();
private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>(); private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>();
private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>(); private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>();
public ComponentManager(DrawLayerManager drawLayerManager) public ComponentManager(DrawLayerManager drawLayerManager)
@ -31,44 +31,62 @@ namespace Encompass
internal void RegisterEntity(Guid entityID) internal void RegisterEntity(Guid entityID)
{ {
entityIDToComponentIDs.Add(entityID, new PooledSet<Guid>()); entityIDToComponentIDs.Add(entityID, new PooledSet<Guid>());
entityIDToComponentTypeToComponentIDs.Add(entityID, new PooledDictionary<Type, HashSet<Guid>>(ClearMode.Never)); entityIDToComponentTypeToComponentID.Add(entityID, new PooledDictionary<Type, Guid>());
} }
internal Guid NextID() private Guid NextID()
{ {
return Guid.NewGuid(); return Guid.NewGuid();
} }
internal Guid AddComponent<TComponent>(Entity entity, Guid componentID, TComponent component) where TComponent : struct, IComponent internal Guid MarkComponentForAdd<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
var id = NextID();
componentAddData.Add((entity, typeof(TComponent), id, component));
return id;
}
internal Guid MarkDrawComponentForAdd<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent
{
var id = MarkComponentForAdd(entity, component);
drawLayerManager.RegisterComponentWithLayer(id, layer);
return id;
}
internal void AddComponent(Entity entity, Type type, Guid componentID, IComponent component)
{ {
IDToComponent[componentID] = component; IDToComponent[componentID] = component;
componentIDToType[componentID] = typeof(TComponent); componentIDToType[componentID] = type;
if (!typeToComponentIDs.ContainsKey(typeof(TComponent))) if (!typeToComponentIDs.ContainsKey(type))
{ {
typeToComponentIDs.Add(typeof(TComponent), new HashSet<Guid>()); typeToComponentIDs.Add(type, new HashSet<Guid>());
} }
typeToComponentIDs[typeof(TComponent)].Add(componentID); typeToComponentIDs[type].Add(componentID);
entityIDToComponentIDs[entity.ID].Add(componentID); entityIDToComponentIDs[entity.ID].Add(componentID);
if (!entityIDToComponentTypeToComponentIDs[entity.ID].ContainsKey(typeof(TComponent))) { if (!entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(type))
entityIDToComponentTypeToComponentIDs[entity.ID].Add(typeof(TComponent), new HashSet<Guid>()); {
entityIDToComponentTypeToComponentID[entity.ID][type] = componentID;
}
else
{
throw new MultipleComponentOfSameTypeException("Entity {0} cannot have multiple components of type {1}", entity.ID, type.Name);
} }
entityIDToComponentTypeToComponentIDs[entity.ID][typeof(TComponent)].Add(componentID);
componentIDToEntityID[componentID] = entity.ID; componentIDToEntityID[componentID] = entity.ID;
return componentID;
} }
internal Guid AddDrawComponent<TComponent>(Entity entity, Guid componentID, TComponent component, int layer = 0) where TComponent : struct, IComponent internal void AddMarkedComponents()
{ {
AddComponent(entity, componentID, component); foreach (var (entity, type, componentID, component) in componentAddData)
drawLayerManager.RegisterComponentWithLayer(componentID, layer); {
return componentID; AddComponent(entity, type, componentID, component);
} }
componentAddData.Clear();
}
internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID) internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID)
{ {
@ -88,23 +106,19 @@ namespace Encompass
return Enumerable.Empty<(Guid, TComponent)>(); return Enumerable.Empty<(Guid, TComponent)>();
} }
internal IEnumerable<ValueTuple<Guid, TComponent>> GetComponentsByEntityAndType<TComponent>(Guid entityID) where TComponent : struct, IComponent internal (Guid, TComponent) GetComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (entityIDToComponentTypeToComponentIDs.ContainsKey(entityID) && entityIDToComponentTypeToComponentIDs[entityID].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) if (entityIDToComponentTypeToComponentID.ContainsKey(entity.ID) && entityIDToComponentTypeToComponentID[entity.ID].TryGetValue(typeof(TComponent), out Guid id))
{ {
return idSet.Select(id => (id, (TComponent)IDToComponent[id])); return (id, (TComponent)IDToComponent[id]);
}
return Enumerable.Empty<(Guid, TComponent)>();
} }
internal bool EntityHasComponentOfType<TComponent>(Guid entityID) where TComponent : struct, IComponent throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID);
{
if (entityIDToComponentTypeToComponentIDs.ContainsKey(entityID) && entityIDToComponentTypeToComponentIDs[entityID].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Count > 0;
} }
return false; internal bool EntityHasComponentOfType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return (entityIDToComponentTypeToComponentID.ContainsKey(entity.ID) && entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(typeof(TComponent)));
} }
internal bool ComponentOfTypeExists<TComponent>() where TComponent : struct, IComponent internal bool ComponentOfTypeExists<TComponent>() where TComponent : struct, IComponent
@ -185,6 +199,11 @@ namespace Encompass
entityIDToComponentIDs[entityID].Remove(componentID); entityIDToComponentIDs[entityID].Remove(componentID);
} }
if (entityIDToComponentTypeToComponentID.ContainsKey(entityID))
{
entityIDToComponentTypeToComponentID[entityID].Remove(type);
}
IDToComponent.Remove(componentID); IDToComponent.Remove(componentID);
componentIDToType.Remove(componentID); componentIDToType.Remove(componentID);
componentIDToEntityID.Remove(componentID); componentIDToEntityID.Remove(componentID);
@ -198,12 +217,8 @@ namespace Encompass
entityIDToComponentIDs[entityID].Dispose(); entityIDToComponentIDs[entityID].Dispose();
entityIDToComponentIDs.Remove(entityID); entityIDToComponentIDs.Remove(entityID);
foreach (var set in entityIDToComponentTypeToComponentIDs[entityID].Values) entityIDToComponentTypeToComponentID[entityID].Dispose();
{ entityIDToComponentTypeToComponentID.Remove(entityID);
set.Clear();
}
entityIDToComponentTypeToComponentIDs[entityID].Dispose();
entityIDToComponentTypeToComponentIDs.Remove(entityID);
} }
} }
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Collections.Pooled; using Collections.Pooled;
using Encompass.Exceptions;
namespace Encompass namespace Encompass
{ {
@ -13,39 +14,27 @@ namespace Encompass
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToPendingComponentIDs = new Dictionary<Type, HashSet<Guid>>(); private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToPendingComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToComponentIDs = new Dictionary<Type, HashSet<Guid>>(); private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly Dictionary<Entity, PooledDictionary<Type, HashSet<Guid>>> entityToTypeToExistingComponentIDs = new Dictionary<Entity, PooledDictionary<Type, HashSet<Guid>>>(); private readonly Dictionary<Entity, PooledDictionary<Type, Guid>> entityToTypeToExistingComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>();
private readonly Dictionary<Entity, PooledDictionary<Type, HashSet<Guid>>> entityToTypeToPendingComponentIDs = new Dictionary<Entity, PooledDictionary<Type, HashSet<Guid>>>(); private readonly Dictionary<Entity, PooledDictionary<Type, Guid>> entityToTypeToPendingComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>();
private readonly Dictionary<Entity, PooledDictionary<Type, HashSet<Guid>>> entityToTypeToComponentIDs = new Dictionary<Entity, PooledDictionary<Type, HashSet<Guid>>>(); private readonly Dictionary<Entity, PooledDictionary<Type, Guid>> entityToTypeToComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>();
internal void RegisterEntity(Entity entity) internal void RegisterEntity(Entity entity)
{ {
entityToTypeToComponentIDs[entity] = new PooledDictionary<Type, HashSet<Guid>>(ClearMode.Never); entityToTypeToComponentID[entity] = new PooledDictionary<Type, Guid>();
entityToTypeToPendingComponentIDs[entity] = new PooledDictionary<Type, HashSet<Guid>>(ClearMode.Never); entityToTypeToPendingComponentID[entity] = new PooledDictionary<Type, Guid>();
entityToTypeToExistingComponentIDs[entity] = new PooledDictionary<Type, HashSet<Guid>>(ClearMode.Never); entityToTypeToExistingComponentID[entity] = new PooledDictionary<Type, Guid>();
} }
internal void RegisterDestroyedEntity(Entity entity) internal void RegisterDestroyedEntity(Entity entity)
{ {
foreach (var set in entityToTypeToComponentIDs[entity].Values) entityToTypeToComponentID[entity].Dispose();
{ entityToTypeToComponentID.Remove(entity);
set.Clear();
}
entityToTypeToComponentIDs[entity].Dispose();
entityToTypeToComponentIDs.Remove(entity);
foreach (var set in entityToTypeToPendingComponentIDs[entity].Values) entityToTypeToPendingComponentID[entity].Dispose();
{ entityToTypeToPendingComponentID.Remove(entity);
set.Clear();
}
entityToTypeToPendingComponentIDs[entity].Dispose();
entityToTypeToPendingComponentIDs.Remove(entity);
foreach (var set in entityToTypeToExistingComponentIDs[entity].Values) entityToTypeToExistingComponentID[entity].Dispose();
{ entityToTypeToExistingComponentID.Remove(entity);
set.Clear();
}
entityToTypeToExistingComponentIDs[entity].Dispose();
entityToTypeToExistingComponentIDs.Remove(entity);
} }
internal void ClearMessages() internal void ClearMessages()
@ -67,28 +56,19 @@ namespace Encompass
set.Clear(); set.Clear();
} }
foreach (var dictionary in entityToTypeToExistingComponentIDs.Values) foreach (var dictionary in entityToTypeToExistingComponentID.Values)
{ {
foreach (var set in dictionary.Values) dictionary.Clear();
{
set.Clear();
}
} }
foreach (var dictionary in entityToTypeToPendingComponentIDs.Values) foreach (var dictionary in entityToTypeToPendingComponentID.Values)
{ {
foreach (var set in dictionary.Values) dictionary.Clear();
{
set.Clear();
}
} }
foreach (var dictionary in entityToTypeToComponentIDs.Values) foreach (var dictionary in entityToTypeToComponentID.Values)
{ {
foreach (var set in dictionary.Values) dictionary.Clear();
{
set.Clear();
}
} }
} }
@ -103,12 +83,14 @@ namespace Encompass
componentMessageTypeToExistingComponentIDs[typeof(TComponent)].Add(componentMessage.componentID); componentMessageTypeToExistingComponentIDs[typeof(TComponent)].Add(componentMessage.componentID);
if (!entityToTypeToExistingComponentIDs[componentMessage.entity].ContainsKey(typeof(TComponent))) if (!entityToTypeToExistingComponentID[componentMessage.entity].ContainsKey(typeof(TComponent)))
{ {
entityToTypeToExistingComponentIDs[componentMessage.entity].Add(typeof(TComponent), new HashSet<Guid>()); entityToTypeToExistingComponentID[componentMessage.entity].Add(typeof(TComponent), componentMessage.componentID);
}
else
{
throw new MultipleComponentOfSameTypeException("Entity {0} cannot have multiple components of type {1}", componentMessage.entity.ID, typeof(TComponent).Name);
} }
entityToTypeToExistingComponentIDs[componentMessage.entity][typeof(TComponent)].Add(componentMessage.componentID);
} }
internal void AddPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> pendingComponentMessage) where TComponent : struct, IComponent internal void AddPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> pendingComponentMessage) where TComponent : struct, IComponent
@ -122,12 +104,14 @@ namespace Encompass
componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Add(pendingComponentMessage.componentID); componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Add(pendingComponentMessage.componentID);
if (!entityToTypeToPendingComponentIDs[pendingComponentMessage.entity].ContainsKey(typeof(TComponent))) if (!entityToTypeToPendingComponentID[pendingComponentMessage.entity].ContainsKey(typeof(TComponent)))
{ {
entityToTypeToPendingComponentIDs[pendingComponentMessage.entity].Add(typeof(TComponent), new HashSet<Guid>()); entityToTypeToPendingComponentID[pendingComponentMessage.entity].Add(typeof(TComponent), pendingComponentMessage.componentID);
}
else
{
throw new MultipleComponentOfSameTypeException("Entity {0} cannot have multiple components of type {1}", pendingComponentMessage.entity.ID, typeof(TComponent).Name);
} }
entityToTypeToPendingComponentIDs[pendingComponentMessage.entity][typeof(TComponent)].Add(pendingComponentMessage.componentID);
} }
private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, Guid componentID, TComponent component) where TComponent: struct, IComponent private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, Guid componentID, TComponent component) where TComponent: struct, IComponent
@ -140,12 +124,14 @@ namespace Encompass
} }
componentMessageTypeToComponentIDs[typeof(TComponent)].Add(componentID); componentMessageTypeToComponentIDs[typeof(TComponent)].Add(componentID);
if (!entityToTypeToComponentIDs[entity].ContainsKey(typeof(TComponent))) if (!entityToTypeToComponentID[entity].ContainsKey(typeof(TComponent)))
{ {
entityToTypeToComponentIDs[entity].Add(typeof(TComponent), new HashSet<Guid>()); entityToTypeToComponentID[entity].Add(typeof(TComponent), componentID);
}
else
{
throw new MultipleComponentOfSameTypeException("Entity {0} cannot have multiple components of type {1}", entity.ID, typeof(TComponent).Name);
} }
entityToTypeToComponentIDs[entity][typeof(TComponent)].Add(componentID);
} }
// general component reads by type // general component reads by type
@ -231,83 +217,45 @@ namespace Encompass
// read components by entity and type // read components by entity and type
internal IEnumerable<(Guid, TComponent)> ReadExistingAndPendingComponentsByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent internal (Guid, TComponent) ReadExistingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (entityToTypeToComponentIDs.ContainsKey(entity) && entityToTypeToComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) if (entityToTypeToExistingComponentID.ContainsKey(entity) && entityToTypeToExistingComponentID[entity].TryGetValue(typeof(TComponent), out Guid id))
{ {
return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); return (id, (TComponent)componentIDToComponent[id]);
}
else
{
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID);
}
} }
return Enumerable.Empty<(Guid, TComponent)>(); internal (Guid, TComponent) ReadPendingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
}
internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (entityToTypeToExistingComponentIDs.ContainsKey(entity) && entityToTypeToExistingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) if (entityToTypeToPendingComponentID.ContainsKey(entity) && entityToTypeToPendingComponentID[entity].TryGetValue(typeof(TComponent), out Guid id))
{ {
return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); return (id, (TComponent)componentIDToComponent[id]);
} }
else
return Enumerable.Empty<(Guid, TComponent)>();
}
internal IEnumerable<(Guid, TComponent)> ReadPendingComponentsByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (entityToTypeToPendingComponentIDs.ContainsKey(entity) && entityToTypeToPendingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID);
{
return idSet.Select(id => (id, (TComponent)componentIDToComponent[id]));
} }
return Enumerable.Empty<(Guid, TComponent)>();
}
// singular read components by entity and type
internal (Guid, TComponent) ReadFirstExistingOrPendingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadExistingAndPendingComponentsByEntityAndType<TComponent>(entity).First();
}
internal (Guid, TComponent) ReadFirstExistingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadExistingComponentsByEntityAndType<TComponent>(entity).First();
}
internal (Guid, TComponent) ReadFirstPendingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadPendingComponentsByEntityAndType<TComponent>(entity).First();
} }
// check if entity has component of type // check if entity has component of type
internal bool HasExistingOrPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent internal bool HasExistingOrPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (entityToTypeToComponentIDs.TryGetValue(entity, out _) && entityToTypeToComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) return entityToTypeToComponentID.ContainsKey(entity) && entityToTypeToComponentID[entity].ContainsKey(typeof(TComponent));
{
return idSet.Count > 0;
}
return false;
} }
internal bool HasExistingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent internal bool HasExistingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (entityToTypeToExistingComponentIDs.TryGetValue(entity, out _) && entityToTypeToExistingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) return entityToTypeToExistingComponentID.ContainsKey(entity) && entityToTypeToExistingComponentID[entity].ContainsKey(typeof(TComponent));
{
return idSet.Count > 0;
}
return false;
} }
internal bool HasPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent internal bool HasPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (entityToTypeToPendingComponentIDs.TryGetValue(entity, out _) && entityToTypeToPendingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) return entityToTypeToPendingComponentID.ContainsKey(entity) && entityToTypeToPendingComponentID[entity].ContainsKey(typeof(TComponent));
{
return idSet.Count > 0;
}
return false;
} }
} }
} }

View File

@ -119,9 +119,7 @@ namespace Encompass
protected Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent protected Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{ {
var componentID = componentManager.NextID(); var componentID = componentManager.MarkComponentForAdd(entity, component);
componentManager.AddComponent(entity, componentID, component);
if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>))) if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>)))
{ {
@ -138,9 +136,7 @@ namespace Encompass
protected Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent protected Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent
{ {
var componentID = componentManager.NextID(); var componentID = componentManager.MarkDrawComponentForAdd(entity, component, layer);
componentManager.AddDrawComponent(entity, componentID, component, layer);
if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>))) if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>)))
{ {
@ -221,43 +217,21 @@ namespace Encompass
} }
} }
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadExistingAndPendingComponentsByEntityAndType<TComponent>(entity);
}
else if (existingRead)
{
return componentMessageManager.ReadExistingComponentsByEntityAndType<TComponent>(entity);
}
else if (pendingRead)
{
return componentMessageManager.ReadPendingComponentsByEntityAndType<TComponent>(entity);
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead) if (existingRead && pendingRead)
{ {
return componentMessageManager.ReadFirstExistingOrPendingComponentByEntityAndType<TComponent>(entity); return componentMessageManager.ReadPendingComponentByEntityAndType<TComponent>(entity);
} }
else if (existingRead) else if (existingRead)
{ {
return componentMessageManager.ReadFirstExistingComponentByEntityAndType<TComponent>(entity); return componentMessageManager.ReadExistingComponentByEntityAndType<TComponent>(entity);
} }
else if (pendingRead) else if (pendingRead)
{ {
return componentMessageManager.ReadFirstPendingComponentByEntityAndType<TComponent>(entity); return componentMessageManager.ReadPendingComponentByEntityAndType<TComponent>(entity);
} }
else else
{ {

View File

@ -0,0 +1,12 @@
using System;
namespace Encompass.Exceptions
{
public class MultipleComponentOfSameTypeException : Exception
{
public MultipleComponentOfSameTypeException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Encompass.Exceptions
{
public class NoComponentOfTypeOnEntityException : Exception
{
public NoComponentOfTypeOnEntityException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -49,19 +49,14 @@ namespace Encompass
return ReadComponents<TComponent>().First(); return ReadComponents<TComponent>().First();
} }
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return componentManager.GetComponentsByEntityAndType<TComponent>(entity.ID);
}
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
return GetComponents<TComponent>(entity).First(); return componentManager.GetComponentByEntityAndType<TComponent>(entity);
} }
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
return componentManager.EntityHasComponentOfType<TComponent>(entity.ID); return componentManager.EntityHasComponentOfType<TComponent>(entity);
} }
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent

View File

@ -41,6 +41,7 @@ namespace Encompass
componentManager.PerformComponentUpdates(); componentManager.PerformComponentUpdates();
componentManager.RemoveMarkedComponents(); componentManager.RemoveMarkedComponents();
componentManager.AddMarkedComponents();
} }
public void Draw() public void Draw()

View File

@ -47,14 +47,12 @@ namespace Encompass
public Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent public Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{ {
var componentID = componentManager.NextID(); return componentManager.MarkComponentForAdd(entity, component);
return componentManager.AddComponent(entity, componentID, component);
} }
public Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent public Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent
{ {
var componentID = componentManager.NextID(); return componentManager.MarkDrawComponentForAdd(entity, component, layer);
return componentManager.AddDrawComponent(entity, componentID, component, layer);
} }
internal void RegisterComponent(Type componentType) internal void RegisterComponent(Type componentType)
@ -256,6 +254,7 @@ namespace Encompass
componentManager.PerformComponentUpdates(); componentManager.PerformComponentUpdates();
componentManager.RemoveMarkedComponents(); componentManager.RemoveMarkedComponents();
componentManager.AddMarkedComponents();
return world; return world;
} }

View File

@ -5,6 +5,7 @@ using Encompass;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using System.Linq; using System.Linq;
using Encompass.Exceptions;
namespace Tests namespace Tests
{ {
@ -81,6 +82,78 @@ namespace Tests
world.Update(0.01); world.Update(0.01);
} }
[Test]
public void AddMultipleComponentOfSameTypeToEntity()
{
var worldBuilder = new WorldBuilder();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, mockComponent);
worldBuilder.AddComponent(entity, mockComponent);
Assert.Throws<MultipleComponentOfSameTypeException>(() => worldBuilder.Build());
}
[Reads(typeof(MockComponent))]
class MultipleAddEngine : Engine
{
public override void Update(double dt)
{
foreach (var (mockComponentID, mockComponent) in ReadComponents<MockComponent>())
{
var entity = GetEntityByComponentID(mockComponentID);
AddComponent(entity, new MockComponent());
}
}
}
[Test]
public void EngineAddMultipleComponentOfSameTypeToEntity()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new MultipleAddEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, new MockComponent());
var world = worldBuilder.Build();
Assert.Throws<MultipleComponentOfSameTypeException>(() => world.Update(0.01));
}
[Reads(typeof(MockComponent))]
class AddAndRemoveComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var (mockComponentID, mockComponent) in ReadComponents<MockComponent>())
{
var entity = GetEntityByComponentID(mockComponentID);
AddComponent(entity, mockComponent);
RemoveComponent(mockComponentID);
}
}
}
[Test]
public void AddMultipleComponentSameFrameAsRemove()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AddAndRemoveComponentEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, new MockComponent());
var world = worldBuilder.Build();
Assert.DoesNotThrow(() => world.Update(0.01));
}
struct AddMockComponentMessage : IMessage struct AddMockComponentMessage : IMessage
{ {
public Entity entity; public Entity entity;
@ -154,89 +227,6 @@ namespace Tests
world.Update(0.01); world.Update(0.01);
} }
[Receives(typeof(EntityMessage))]
[Reads(typeof(MockComponent))]
class GetMockComponentsEngine : Engine
{
private Entity entity;
private Guid componentAID;
private Guid componentBID;
private Guid componentCID;
private MockComponent componentA;
private MockComponent componentB;
private MockComponent componentC;
public GetMockComponentsEngine(
Entity entity,
Guid componentAID,
MockComponent componentA,
Guid componentBID,
MockComponent componentB,
Guid componentCID,
MockComponent componentC
) {
this.entity = entity;
this.componentAID = componentAID;
this.componentA = componentA;
this.componentBID = componentBID;
this.componentB = componentB;
this.componentCID = componentCID;
this.componentC = componentC;
}
public override void Update(double dt)
{
foreach (var entityMessage in ReadMessages<EntityMessage>())
{
var results = GetComponents<MockComponent>(entityMessage.entity);
results.Should().Contain((componentAID, componentA));
results.Should().Contain((componentBID, componentB));
results.Should().Contain((componentCID, componentC));
}
}
}
[Test]
public void GetComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponentA;
mockComponentA.myInt = 3;
mockComponentA.myString = "hello";
MockComponent mockComponentB;
mockComponentB.myInt = 5;
mockComponentB.myString = "wassup";
MockComponent mockComponentC;
mockComponentC.myInt = 1;
mockComponentC.myString = "howdy";
var componentAID = worldBuilder.AddComponent(entity, mockComponentA);
var componentBID = worldBuilder.AddComponent(entity, mockComponentB);
var componentCID = worldBuilder.AddComponent(entity, mockComponentC);
worldBuilder.AddEngine(new GetMockComponentsEngine(
entity,
componentAID,
mockComponentA,
componentBID,
mockComponentB,
componentCID,
mockComponentC
));
EntityMessage entityMessage;
entityMessage.entity = entity;
worldBuilder.SendMessage(entityMessage);
var world = worldBuilder.Build();
world.Update(0.01);
}
[Test] [Test]
public void GetComponent() public void GetComponent()
{ {

View File

@ -49,6 +49,7 @@ namespace Tests
worldBuilder.AddEngine(new ReadComponentsTestEngine()); worldBuilder.AddEngine(new ReadComponentsTestEngine());
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent; MockComponent mockComponent;
mockComponent.myInt = 0; mockComponent.myInt = 0;
@ -59,7 +60,7 @@ namespace Tests
mockComponentB.myString = "howdy"; mockComponentB.myString = "howdy";
var componentAID = worldBuilder.AddComponent(entity, mockComponent); var componentAID = worldBuilder.AddComponent(entity, mockComponent);
var componentBID = worldBuilder.AddComponent(entity, mockComponentB); var componentBID = worldBuilder.AddComponent(entityB, mockComponentB);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -98,6 +99,7 @@ namespace Tests
worldBuilder.AddEngine(new ReadComponentTestEngine()); worldBuilder.AddEngine(new ReadComponentTestEngine());
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent; MockComponent mockComponent;
mockComponent.myInt = 0; mockComponent.myInt = 0;
@ -108,7 +110,7 @@ namespace Tests
mockComponentB.myString = "howdy"; mockComponentB.myString = "howdy";
worldBuilder.AddComponent(entity, mockComponent); worldBuilder.AddComponent(entity, mockComponent);
worldBuilder.AddComponent(entity, mockComponentB); worldBuilder.AddComponent(entityB, mockComponentB);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -390,7 +392,9 @@ namespace Tests
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, componentA); worldBuilder.AddComponent(entity, componentA);
worldBuilder.AddComponent(entity, componentB);
var entityB = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entityB, componentB);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);

View File

@ -53,7 +53,9 @@ namespace Tests
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
var componentID = worldBuilder.AddComponent(entity, aComponent); var componentID = worldBuilder.AddComponent(entity, aComponent);
var componentTwoID = worldBuilder.AddComponent(entity, aComponentTwo);
var entityB = worldBuilder.CreateEntity();
var componentTwoID = worldBuilder.AddComponent(entityB, aComponentTwo);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);