From cb872b7c42e74a1a26db78c2cea42e99779dd93e Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sat, 15 Jun 2019 00:39:08 -0700 Subject: [PATCH] component update mechanism --- src/ComponentManager.cs | 44 ++++++++++++ src/Engine.cs | 19 ++++++ src/attributes/Mutates.cs | 15 ++++ src/encompass-cs.csproj | 2 + .../IllegalComponentMutationException.cs | 10 +++ test/EngineTest.cs | 68 +++++++++++++++++++ 6 files changed, 158 insertions(+) create mode 100644 src/attributes/Mutates.cs create mode 100644 src/exceptions/IllegalComponentMutationException.cs diff --git a/src/ComponentManager.cs b/src/ComponentManager.cs index fa43bc8..6b58dc1 100644 --- a/src/ComponentManager.cs +++ b/src/ComponentManager.cs @@ -5,6 +5,7 @@ using System.Linq; namespace Encompass { internal class ComponentManager { private Dictionary> entityIDToComponents = new Dictionary>(); + private Dictionary componentToEntityID = new Dictionary(); private Dictionary> activeComponents = new Dictionary>(); private Dictionary> inactiveComponents = new Dictionary>(); @@ -19,6 +20,7 @@ namespace Encompass { } entityIDToComponents[entityID].Add(component); + componentToEntityID[component] = entityID; if (!activeComponents.ContainsKey(typeof(TComponent))) { activeComponents.Add(typeof(TComponent), new List()); @@ -50,6 +52,37 @@ namespace Encompass { return GetComponentsByEntityAndType(entityID).Any(); } + /** Replaces the component with another. */ + internal void UpdateComponent(TComponent originalComponent, TComponent newComponent) where TComponent : struct, IComponent { + var entityID = componentToEntityID[originalComponent]; + + entityIDToComponents[entityID].Remove(originalComponent); + entityIDToComponents[entityID].Add(newComponent); + + componentToEntityID.Remove(originalComponent); + componentToEntityID.Add(newComponent, entityID); + + if (activeComponents[originalComponent.GetType()].Remove(originalComponent)) { + activeComponents[originalComponent.GetType()].Add(newComponent); + } + + if (inactiveComponents[originalComponent.GetType()].Remove(originalComponent)) { + inactiveComponents[originalComponent.GetType()].Add(newComponent); + } + + if (componentsToActivate.Remove(originalComponent)) { + componentsToActivate.Add(newComponent); + } + + if (componentsToDeactivate.Remove(originalComponent)) { + componentsToDeactivate.Add(newComponent); + } + + if (componentsToRemove.Remove(originalComponent)) { + componentsToRemove.Add(newComponent); + } + } + internal void RemoveAllComponentsFromEntity(uint entityID) { var components = GetComponentsByEntity(entityID); @@ -65,6 +98,10 @@ namespace Encompass { componentsToActivate.Add(component); } + internal void MarkForDeactivation(IComponent component) { + componentsToDeactivate.Add(component); + } + internal void MarkForRemoval(IComponent component) { componentsToRemove.Add(component); } @@ -78,6 +115,13 @@ namespace Encompass { componentsToActivate.Clear(); } + internal void DeactivateComponents() { + foreach (var component in componentsToDeactivate) { + activeComponents[component.GetType()].Remove(component); + inactiveComponents[component.GetType()].Add(component); + } + } + internal void RemoveComponents() { foreach (var component in componentsToRemove) { activeComponents[component.GetType()].Remove(component); diff --git a/src/Engine.cs b/src/Engine.cs index d951e4d..0400795 100644 --- a/src/Engine.cs +++ b/src/Engine.cs @@ -1,10 +1,21 @@ +using System; +using System.Reflection; using System.Collections.Generic; namespace Encompass { public abstract class Engine { + public readonly List mutateComponentTypes = new List(); + private EntityManager entityManager; private ComponentManager componentManager; + public Engine() { + var mutatesAttribute = this.GetType().GetCustomAttribute(false); + if (mutatesAttribute != null) { + mutateComponentTypes = mutatesAttribute.mutateComponentTypes; + } + } + internal void AssignEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } @@ -26,5 +37,13 @@ namespace Encompass { protected TComponent ReadComponent() where TComponent : struct, IComponent { return this.componentManager.GetActiveComponentByType(); } + + protected void UpdateComponent(TComponent originalComponent, TComponent newComponent) where TComponent : struct, IComponent { + if (mutateComponentTypes.Contains(typeof(TComponent))) { + this.componentManager.UpdateComponent(originalComponent, newComponent); + } else { + throw new IllegalComponentMutationException("Engine {0} tried to mutate undeclared Component {1}", this.GetType().ToString(), typeof(TComponent).ToString()); + } + } } } diff --git a/src/attributes/Mutates.cs b/src/attributes/Mutates.cs new file mode 100644 index 0000000..ec542bf --- /dev/null +++ b/src/attributes/Mutates.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace Encompass { + [System.AttributeUsage(System.AttributeTargets.Class)] + public class Mutates : System.Attribute + { + public readonly List mutateComponentTypes; + + public Mutates(params Type[] mutateComponentTypes) + { + this.mutateComponentTypes = new List(mutateComponentTypes); + } + } +} diff --git a/src/encompass-cs.csproj b/src/encompass-cs.csproj index 8b5d141..a625a72 100644 --- a/src/encompass-cs.csproj +++ b/src/encompass-cs.csproj @@ -13,5 +13,7 @@ + + \ No newline at end of file diff --git a/src/exceptions/IllegalComponentMutationException.cs b/src/exceptions/IllegalComponentMutationException.cs new file mode 100644 index 0000000..4303dc9 --- /dev/null +++ b/src/exceptions/IllegalComponentMutationException.cs @@ -0,0 +1,10 @@ +using System; + +namespace Encompass { + public class IllegalComponentMutationException : Exception { + public IllegalComponentMutationException( + string format, + params object[] args + ) : base(string.Format(format, args)) { } + } +} diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 5469933..ae43df8 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -98,5 +98,73 @@ namespace Tests { Assert.Throws(() => world.Update(0.01f)); } + + [Mutates(typeof(MockComponent))] + public class UpdateComponentTestEngine : Engine + { + public override void Update(float dt) + { + var originalComponent = this.ReadComponent(); + var newComponent = this.ReadComponent(); + newComponent.myInt = 420; + newComponent.myString = "blaze it"; + this.UpdateComponent(originalComponent, newComponent); + component = this.ReadComponent(); + } + } + + [Test] + public void UpdateComponent() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(); + + var entity = worldBuilder.CreateEntity(); + + MockComponent mockComponent; + mockComponent.myInt = 0; + mockComponent.myString = "hello"; + + entity.AddComponent(mockComponent); + + var world = worldBuilder.Build(); + + world.Update(0.01f); + + Assert.AreEqual(420, component.myInt); + Assert.AreEqual("blaze it", component.myString); + } + + public class UndeclaredUpdateComponentTestEngine : Engine + { + public override void Update(float dt) + { + var originalComponent = this.ReadComponent(); + var newComponent = this.ReadComponent(); + newComponent.myInt = 420; + newComponent.myString = "blaze it"; + this.UpdateComponent(originalComponent, newComponent); + component = this.ReadComponent(); + } + } + + [Test] + public void UpdateUndeclaredComponent() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(); + + var entity = worldBuilder.CreateEntity(); + + MockComponent mockComponent; + mockComponent.myInt = 0; + mockComponent.myString = "hello"; + + entity.AddComponent(mockComponent); + + var world = worldBuilder.Build(); + + Assert.Throws(() => world.Update(0.01f)); + } } }