diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 4ab7580..4c77e95 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -15,12 +15,12 @@ namespace Encompass private readonly Dictionary> entityIDToComponentIDs = new Dictionary>(); private readonly Dictionary componentIDToEntityID = new Dictionary(); - private readonly Dictionary>> entityIDToComponentTypeToComponentIDs = new Dictionary>>(); + private readonly Dictionary> entityIDToComponentTypeToComponentID = new Dictionary>(); private readonly Dictionary> typeToComponentIDs = new Dictionary>(); + private readonly List<(Entity, Type, Guid, IComponent)> componentAddData = new List<(Entity, Type, Guid, IComponent)>(); private readonly HashSet componentsMarkedForRemoval = new HashSet(); - private readonly Dictionary pendingUpdates = new Dictionary(); public ComponentManager(DrawLayerManager drawLayerManager) @@ -31,44 +31,62 @@ namespace Encompass internal void RegisterEntity(Guid entityID) { entityIDToComponentIDs.Add(entityID, new PooledSet()); - entityIDToComponentTypeToComponentIDs.Add(entityID, new PooledDictionary>(ClearMode.Never)); + entityIDToComponentTypeToComponentID.Add(entityID, new PooledDictionary()); } - internal Guid NextID() + private Guid NextID() { return Guid.NewGuid(); } - internal Guid AddComponent(Entity entity, Guid componentID, TComponent component) where TComponent : struct, IComponent + internal Guid MarkComponentForAdd(Entity entity, TComponent component) where TComponent : struct, IComponent + { + var id = NextID(); + componentAddData.Add((entity, typeof(TComponent), id, component)); + return id; + } + + internal Guid MarkDrawComponentForAdd(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; - componentIDToType[componentID] = typeof(TComponent); + componentIDToType[componentID] = type; - if (!typeToComponentIDs.ContainsKey(typeof(TComponent))) + if (!typeToComponentIDs.ContainsKey(type)) { - typeToComponentIDs.Add(typeof(TComponent), new HashSet()); + typeToComponentIDs.Add(type, new HashSet()); } - typeToComponentIDs[typeof(TComponent)].Add(componentID); + typeToComponentIDs[type].Add(componentID); entityIDToComponentIDs[entity.ID].Add(componentID); - if (!entityIDToComponentTypeToComponentIDs[entity.ID].ContainsKey(typeof(TComponent))) { - entityIDToComponentTypeToComponentIDs[entity.ID].Add(typeof(TComponent), new HashSet()); + if (!entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(type)) + { + 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; - - return componentID; } - internal Guid AddDrawComponent(Entity entity, Guid componentID, TComponent component, int layer = 0) where TComponent : struct, IComponent + internal void AddMarkedComponents() { - AddComponent(entity, componentID, component); - drawLayerManager.RegisterComponentWithLayer(componentID, layer); - return componentID; - } + foreach (var (entity, type, componentID, component) in componentAddData) + { + AddComponent(entity, type, componentID, component); + } + componentAddData.Clear(); + } internal IEnumerable GetComponentIDsByEntityID(Guid entityID) { @@ -88,23 +106,19 @@ namespace Encompass return Enumerable.Empty<(Guid, TComponent)>(); } - internal IEnumerable> GetComponentsByEntityAndType(Guid entityID) where TComponent : struct, IComponent + internal (Guid, TComponent) GetComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - if (entityIDToComponentTypeToComponentIDs.ContainsKey(entityID) && entityIDToComponentTypeToComponentIDs[entityID].TryGetValue(typeof(TComponent), out HashSet 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)>(); + + throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID); } - internal bool EntityHasComponentOfType(Guid entityID) where TComponent : struct, IComponent + internal bool EntityHasComponentOfType(Entity entity) where TComponent : struct, IComponent { - if (entityIDToComponentTypeToComponentIDs.ContainsKey(entityID) && entityIDToComponentTypeToComponentIDs[entityID].TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; + return (entityIDToComponentTypeToComponentID.ContainsKey(entity.ID) && entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(typeof(TComponent))); } internal bool ComponentOfTypeExists() where TComponent : struct, IComponent @@ -185,6 +199,11 @@ namespace Encompass entityIDToComponentIDs[entityID].Remove(componentID); } + if (entityIDToComponentTypeToComponentID.ContainsKey(entityID)) + { + entityIDToComponentTypeToComponentID[entityID].Remove(type); + } + IDToComponent.Remove(componentID); componentIDToType.Remove(componentID); componentIDToEntityID.Remove(componentID); @@ -198,12 +217,8 @@ namespace Encompass entityIDToComponentIDs[entityID].Dispose(); entityIDToComponentIDs.Remove(entityID); - foreach (var set in entityIDToComponentTypeToComponentIDs[entityID].Values) - { - set.Clear(); - } - entityIDToComponentTypeToComponentIDs[entityID].Dispose(); - entityIDToComponentTypeToComponentIDs.Remove(entityID); + entityIDToComponentTypeToComponentID[entityID].Dispose(); + entityIDToComponentTypeToComponentID.Remove(entityID); } } } diff --git a/encompass-cs/ComponentMessageManager.cs b/encompass-cs/ComponentMessageManager.cs index 8d6cbe6..0a110df 100644 --- a/encompass-cs/ComponentMessageManager.cs +++ b/encompass-cs/ComponentMessageManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Collections.Pooled; +using Encompass.Exceptions; namespace Encompass { @@ -13,39 +14,27 @@ namespace Encompass private readonly Dictionary> componentMessageTypeToPendingComponentIDs = new Dictionary>(); private readonly Dictionary> componentMessageTypeToComponentIDs = new Dictionary>(); - private readonly Dictionary>> entityToTypeToExistingComponentIDs = new Dictionary>>(); - private readonly Dictionary>> entityToTypeToPendingComponentIDs = new Dictionary>>(); - private readonly Dictionary>> entityToTypeToComponentIDs = new Dictionary>>(); + private readonly Dictionary> entityToTypeToExistingComponentID = new Dictionary>(); + private readonly Dictionary> entityToTypeToPendingComponentID = new Dictionary>(); + private readonly Dictionary> entityToTypeToComponentID = new Dictionary>(); internal void RegisterEntity(Entity entity) { - entityToTypeToComponentIDs[entity] = new PooledDictionary>(ClearMode.Never); - entityToTypeToPendingComponentIDs[entity] = new PooledDictionary>(ClearMode.Never); - entityToTypeToExistingComponentIDs[entity] = new PooledDictionary>(ClearMode.Never); + entityToTypeToComponentID[entity] = new PooledDictionary(); + entityToTypeToPendingComponentID[entity] = new PooledDictionary(); + entityToTypeToExistingComponentID[entity] = new PooledDictionary(); } internal void RegisterDestroyedEntity(Entity entity) { - foreach (var set in entityToTypeToComponentIDs[entity].Values) - { - set.Clear(); - } - entityToTypeToComponentIDs[entity].Dispose(); - entityToTypeToComponentIDs.Remove(entity); + entityToTypeToComponentID[entity].Dispose(); + entityToTypeToComponentID.Remove(entity); - foreach (var set in entityToTypeToPendingComponentIDs[entity].Values) - { - set.Clear(); - } - entityToTypeToPendingComponentIDs[entity].Dispose(); - entityToTypeToPendingComponentIDs.Remove(entity); + entityToTypeToPendingComponentID[entity].Dispose(); + entityToTypeToPendingComponentID.Remove(entity); - foreach (var set in entityToTypeToExistingComponentIDs[entity].Values) - { - set.Clear(); - } - entityToTypeToExistingComponentIDs[entity].Dispose(); - entityToTypeToExistingComponentIDs.Remove(entity); + entityToTypeToExistingComponentID[entity].Dispose(); + entityToTypeToExistingComponentID.Remove(entity); } internal void ClearMessages() @@ -67,28 +56,19 @@ namespace Encompass set.Clear(); } - foreach (var dictionary in entityToTypeToExistingComponentIDs.Values) + foreach (var dictionary in entityToTypeToExistingComponentID.Values) { - foreach (var set in dictionary.Values) - { - set.Clear(); - } + dictionary.Clear(); } - foreach (var dictionary in entityToTypeToPendingComponentIDs.Values) + foreach (var dictionary in entityToTypeToPendingComponentID.Values) { - foreach (var set in dictionary.Values) - { - set.Clear(); - } + dictionary.Clear(); } - foreach (var dictionary in entityToTypeToComponentIDs.Values) + foreach (var dictionary in entityToTypeToComponentID.Values) { - foreach (var set in dictionary.Values) - { - set.Clear(); - } + dictionary.Clear(); } } @@ -103,12 +83,14 @@ namespace Encompass 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()); + 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(PendingComponentMessage pendingComponentMessage) where TComponent : struct, IComponent @@ -122,12 +104,14 @@ namespace Encompass 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()); + 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(Entity entity, Guid componentID, TComponent component) where TComponent: struct, IComponent @@ -140,12 +124,14 @@ namespace Encompass } componentMessageTypeToComponentIDs[typeof(TComponent)].Add(componentID); - if (!entityToTypeToComponentIDs[entity].ContainsKey(typeof(TComponent))) + if (!entityToTypeToComponentID[entity].ContainsKey(typeof(TComponent))) { - entityToTypeToComponentIDs[entity].Add(typeof(TComponent), new HashSet()); + 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 @@ -231,83 +217,45 @@ namespace Encompass // read components by entity and type - internal IEnumerable<(Guid, TComponent)> ReadExistingAndPendingComponentsByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal (Guid, TComponent) ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - if (entityToTypeToComponentIDs.ContainsKey(entity) && entityToTypeToComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet 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]); } - - return Enumerable.Empty<(Guid, TComponent)>(); - } - - internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByEntityAndType(Entity entity) where TComponent : struct, IComponent - { - if (entityToTypeToExistingComponentIDs.ContainsKey(entity) && entityToTypeToExistingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet idSet)) + else { - return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); + throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID); } - - return Enumerable.Empty<(Guid, TComponent)>(); } - internal IEnumerable<(Guid, TComponent)> ReadPendingComponentsByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal (Guid, TComponent) ReadPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - if (entityToTypeToPendingComponentIDs.ContainsKey(entity) && entityToTypeToPendingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet 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 + { + throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID); } - - return Enumerable.Empty<(Guid, TComponent)>(); - } - - // singular read components by entity and type - - internal (Guid, TComponent) ReadFirstExistingOrPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent - { - return ReadExistingAndPendingComponentsByEntityAndType(entity).First(); - } - - internal (Guid, TComponent) ReadFirstExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent - { - return ReadExistingComponentsByEntityAndType(entity).First(); - } - - internal (Guid, TComponent) ReadFirstPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent - { - return ReadPendingComponentsByEntityAndType(entity).First(); } // check if entity has component of type internal bool HasExistingOrPendingComponent(Entity entity) where TComponent : struct, IComponent { - if (entityToTypeToComponentIDs.TryGetValue(entity, out _) && entityToTypeToComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; + return entityToTypeToComponentID.ContainsKey(entity) && entityToTypeToComponentID[entity].ContainsKey(typeof(TComponent)); } internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent { - if (entityToTypeToExistingComponentIDs.TryGetValue(entity, out _) && entityToTypeToExistingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; + return entityToTypeToExistingComponentID.ContainsKey(entity) && entityToTypeToExistingComponentID[entity].ContainsKey(typeof(TComponent)); } internal bool HasPendingComponent(Entity entity) where TComponent : struct, IComponent { - if (entityToTypeToPendingComponentIDs.TryGetValue(entity, out _) && entityToTypeToPendingComponentIDs[entity].TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; + return entityToTypeToPendingComponentID.ContainsKey(entity) && entityToTypeToPendingComponentID[entity].ContainsKey(typeof(TComponent)); } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index f61b9f4..e0114d7 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -119,9 +119,7 @@ namespace Encompass protected Guid AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - var componentID = componentManager.NextID(); - - componentManager.AddComponent(entity, componentID, component); + var componentID = componentManager.MarkComponentForAdd(entity, component); if (sendTypes.Contains(typeof(PendingComponentMessage))) { @@ -138,9 +136,7 @@ namespace Encompass protected Guid AddDrawComponent(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent { - var componentID = componentManager.NextID(); - - componentManager.AddDrawComponent(entity, componentID, component, layer); + var componentID = componentManager.MarkDrawComponentForAdd(entity, component, layer); if (sendTypes.Contains(typeof(PendingComponentMessage))) { @@ -221,43 +217,21 @@ namespace Encompass } } - protected IEnumerable> GetComponents(Entity entity) where TComponent : struct, IComponent - { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); - if (existingRead && pendingRead) - { - return componentMessageManager.ReadExistingAndPendingComponentsByEntityAndType(entity); - } - else if (existingRead) - { - return componentMessageManager.ReadExistingComponentsByEntityAndType(entity); - } - else if (pendingRead) - { - return componentMessageManager.ReadPendingComponentsByEntityAndType(entity); - } - else - { - throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); - } - } - protected ValueTuple GetComponent(Entity entity) where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (existingRead && pendingRead) { - return componentMessageManager.ReadFirstExistingOrPendingComponentByEntityAndType(entity); + return componentMessageManager.ReadPendingComponentByEntityAndType(entity); } else if (existingRead) { - return componentMessageManager.ReadFirstExistingComponentByEntityAndType(entity); + return componentMessageManager.ReadExistingComponentByEntityAndType(entity); } else if (pendingRead) { - return componentMessageManager.ReadFirstPendingComponentByEntityAndType(entity); + return componentMessageManager.ReadPendingComponentByEntityAndType(entity); } else { diff --git a/encompass-cs/Exceptions/MultipleComponentOfSameTypeException.cs b/encompass-cs/Exceptions/MultipleComponentOfSameTypeException.cs new file mode 100644 index 0000000..1c725b6 --- /dev/null +++ b/encompass-cs/Exceptions/MultipleComponentOfSameTypeException.cs @@ -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)) { } + } +} diff --git a/encompass-cs/Exceptions/NoComponentOfTypeOnEntityException.cs b/encompass-cs/Exceptions/NoComponentOfTypeOnEntityException.cs new file mode 100644 index 0000000..8b6ebf8 --- /dev/null +++ b/encompass-cs/Exceptions/NoComponentOfTypeOnEntityException.cs @@ -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)) { } + } +} diff --git a/encompass-cs/Renderer.cs b/encompass-cs/Renderer.cs index 975a750..aa83f96 100644 --- a/encompass-cs/Renderer.cs +++ b/encompass-cs/Renderer.cs @@ -49,19 +49,14 @@ namespace Encompass return ReadComponents().First(); } - protected IEnumerable> GetComponents(Entity entity) where TComponent : struct, IComponent - { - return componentManager.GetComponentsByEntityAndType(entity.ID); - } - protected ValueTuple GetComponent(Entity entity) where TComponent : struct, IComponent { - return GetComponents(entity).First(); + return componentManager.GetComponentByEntityAndType(entity); } protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { - return componentManager.EntityHasComponentOfType(entity.ID); + return componentManager.EntityHasComponentOfType(entity); } protected bool SomeComponent() where TComponent : struct, IComponent diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index c862cbf..abf97c7 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -41,6 +41,7 @@ namespace Encompass componentManager.PerformComponentUpdates(); componentManager.RemoveMarkedComponents(); + componentManager.AddMarkedComponents(); } public void Draw() diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index a44b323..ad75fdd 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -47,14 +47,12 @@ namespace Encompass public Guid AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - var componentID = componentManager.NextID(); - return componentManager.AddComponent(entity, componentID, component); + return componentManager.MarkComponentForAdd(entity, component); } public Guid AddDrawComponent(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent { - var componentID = componentManager.NextID(); - return componentManager.AddDrawComponent(entity, componentID, component, layer); + return componentManager.MarkDrawComponentForAdd(entity, component, layer); } internal void RegisterComponent(Type componentType) @@ -256,6 +254,7 @@ namespace Encompass componentManager.PerformComponentUpdates(); componentManager.RemoveMarkedComponents(); + componentManager.AddMarkedComponents(); return world; } diff --git a/test/ComponentTest.cs b/test/ComponentTest.cs index 82977a7..2d14aba 100644 --- a/test/ComponentTest.cs +++ b/test/ComponentTest.cs @@ -5,6 +5,7 @@ using Encompass; using System.Collections.Generic; using System; using System.Linq; +using Encompass.Exceptions; namespace Tests { @@ -81,6 +82,78 @@ namespace Tests 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(() => worldBuilder.Build()); + } + + [Reads(typeof(MockComponent))] + class MultipleAddEngine : Engine + { + public override void Update(double dt) + { + foreach (var (mockComponentID, mockComponent) in ReadComponents()) + { + 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(() => world.Update(0.01)); + } + + [Reads(typeof(MockComponent))] + class AddAndRemoveComponentEngine : Engine + { + public override void Update(double dt) + { + foreach (var (mockComponentID, mockComponent) in ReadComponents()) + { + 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 { public Entity entity; @@ -154,89 +227,6 @@ namespace Tests 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()) - { - var results = GetComponents(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] public void GetComponent() { diff --git a/test/EngineTest.cs b/test/EngineTest.cs index a9b0d54..d40c3de 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -49,6 +49,7 @@ namespace Tests worldBuilder.AddEngine(new ReadComponentsTestEngine()); var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); MockComponent mockComponent; mockComponent.myInt = 0; @@ -59,7 +60,7 @@ namespace Tests mockComponentB.myString = "howdy"; var componentAID = worldBuilder.AddComponent(entity, mockComponent); - var componentBID = worldBuilder.AddComponent(entity, mockComponentB); + var componentBID = worldBuilder.AddComponent(entityB, mockComponentB); var world = worldBuilder.Build(); @@ -98,6 +99,7 @@ namespace Tests worldBuilder.AddEngine(new ReadComponentTestEngine()); var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); MockComponent mockComponent; mockComponent.myInt = 0; @@ -108,7 +110,7 @@ namespace Tests mockComponentB.myString = "howdy"; worldBuilder.AddComponent(entity, mockComponent); - worldBuilder.AddComponent(entity, mockComponentB); + worldBuilder.AddComponent(entityB, mockComponentB); var world = worldBuilder.Build(); @@ -390,7 +392,9 @@ namespace Tests var entity = worldBuilder.CreateEntity(); worldBuilder.AddComponent(entity, componentA); - worldBuilder.AddComponent(entity, componentB); + + var entityB = worldBuilder.CreateEntity(); + worldBuilder.AddComponent(entityB, componentB); var world = worldBuilder.Build(); world.Update(0.01f); diff --git a/test/GeneralRendererTest.cs b/test/GeneralRendererTest.cs index 99360be..bd4b4ca 100644 --- a/test/GeneralRendererTest.cs +++ b/test/GeneralRendererTest.cs @@ -53,7 +53,9 @@ namespace Tests var entity = worldBuilder.CreateEntity(); 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(); world.Update(0.01f);