diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 87433aa..50fb6d1 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Encompass.Exceptions; namespace Encompass { @@ -22,6 +23,8 @@ namespace Encompass private readonly HashSet componentsMarkedForDeactivation = new HashSet(); private readonly HashSet componentsMarkedForRemoval = new HashSet(); + private readonly Dictionary pendingUpdates = new Dictionary(); + //shared references with EntityManager private readonly HashSet entitiesWithAddedComponents; private readonly HashSet entitiesWithRemovedComponents; @@ -149,9 +152,24 @@ namespace Encompass return componentIDToEntityID[componentID]; } - internal void UpdateComponent(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent + internal void AddUpdateComponentOperation(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent { - IDToComponent[componentID] = newComponentValue; + if (pendingUpdates.ContainsKey(componentID)) + { + throw new RepeatUpdateComponentException("Component with ID {0} was updated multiple times this frame", componentID); + } + + pendingUpdates.Add(componentID, newComponentValue); + } + + internal void PerformComponentUpdates() + { + foreach (var idPair in pendingUpdates) + { + IDToComponent[idPair.Key] = idPair.Value; + } + + pendingUpdates.Clear(); } internal void MarkAllComponentsOnEntityForRemoval(Guid entityID) diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 219535c..568bc77 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -134,7 +134,7 @@ namespace Encompass throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } - componentManager.UpdateComponent(componentID, newComponent); + componentManager.AddUpdateComponentOperation(componentID, newComponent); } protected void UpdateComponent(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent diff --git a/encompass-cs/Exceptions/RepeatUpdateComponentException.cs b/encompass-cs/Exceptions/RepeatUpdateComponentException.cs new file mode 100644 index 0000000..4b178fb --- /dev/null +++ b/encompass-cs/Exceptions/RepeatUpdateComponentException.cs @@ -0,0 +1,12 @@ +using System; + +namespace Encompass.Exceptions +{ + public class RepeatUpdateComponentException : Exception + { + public RepeatUpdateComponentException( + string format, + params object[] args + ) : base(string.Format(format, args)) { } + } +} diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index f349877..9e4feb3 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -35,6 +35,7 @@ namespace Encompass messageManager.ClearMessages(); entityManager.DestroyMarkedEntities(); + componentManager.PerformComponentUpdates(); componentManager.ActivateMarkedComponents(); componentManager.DeactivateMarkedComponents(); componentManager.RemoveMarkedComponents(); diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 16c4b98..32ba70a 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -224,10 +224,11 @@ namespace Encompass renderManager ); + componentManager.PerformComponentUpdates(); componentManager.ActivateMarkedComponents(); componentManager.DeactivateMarkedComponents(); componentManager.RemoveMarkedComponents(); - + entityManager.CheckEntitiesWithAddedComponents(); entityManager.CheckEntitiesWithRemovedComponents(); diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 432555d..d975185 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -31,6 +31,7 @@ namespace Tests } } + public class ReadComponentTestEngine : Engine { public override void Update(double dt) @@ -126,16 +127,17 @@ namespace Tests component.myInt = 420; component.myString = "blaze it"; UpdateComponent(componentID, component); - - resultComponent = ReadComponent().Item2; } } + // this test needs to be improved... + [Test] public void UpdateComponent() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new UpdateComponentTestEngine()); + worldBuilder.AddEngine(new ReadComponentTestEngine()); var entity = worldBuilder.CreateEntity(); @@ -147,7 +149,8 @@ namespace Tests var world = worldBuilder.Build(); - world.Update(0.01f); + world.Update(0.01); + world.Update(0.01); Assert.AreEqual(420, resultComponent.myInt); Assert.AreEqual("blaze it", resultComponent.myString); @@ -642,5 +645,48 @@ namespace Tests Assert.Throws(() => worldBuilder.AddEngine(new EngineThatWritesComponentAndMessage())); } + + struct MockComponentUpdateMessage : IMessage + { + public Guid componentID; + public MockComponent mockComponent; + } + + [Reads(typeof(MockComponentUpdateMessage))] + [Writes(typeof(MockComponent))] + class RepeatUpdateEngine : Engine + { + public override void Update(double dt) + { + foreach (var mockComponentUpdateMessage in ReadMessages()) + { + UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent); + UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent); + } + } + } + + [Test] + public void EngineUpdatesComponentMultipleTimes() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(new RepeatUpdateEngine()); + + var entity = worldBuilder.CreateEntity(); + + MockComponent mockComponent; + mockComponent.myInt = 1; + mockComponent.myString = "5"; + + var mockComponentID = worldBuilder.AddComponent(entity, mockComponent); + + MockComponentUpdateMessage mockComponentUpdateMessage; + mockComponentUpdateMessage.componentID = mockComponentID; + mockComponentUpdateMessage.mockComponent = mockComponent; + worldBuilder.EmitMessage(mockComponentUpdateMessage); + + var world = worldBuilder.Build(); + Assert.Throws(() => world.Update(0.01)); + } } }