From 494a5832403b5c585fd28b2cfba1e9dc43e42840 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 20 Dec 2019 11:10:42 -0800 Subject: [PATCH 01/37] started system for efficiently doing entity queries --- encompass-cs/Collections/ComponentStore.cs | 19 +++++- encompass-cs/ComponentBitSet.cs | 76 ++++++++++++++++++++++ encompass-cs/ComponentManager.cs | 11 ++++ encompass-cs/ComponentUpdateManager.cs | 8 +++ encompass-cs/Engine.cs | 5 ++ encompass-cs/WorldBuilder.cs | 7 +- test/EngineTest.cs | 38 +++++++++++ 7 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 encompass-cs/ComponentBitSet.cs diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 3283baf..f5c3b31 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -6,6 +6,7 @@ namespace Encompass internal class ComponentStore { private Dictionary Stores = new Dictionary(512); + private ComponentBitSet componentBitSet = new ComponentBitSet(); public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable() { @@ -19,12 +20,17 @@ namespace Encompass { if (!Stores.ContainsKey(typeof(TComponent))) { - System.Console.WriteLine("register component type in component store"); var store = new TypedComponentStore(); Stores.Add(typeof(TComponent), store); + componentBitSet.RegisterType(); } } + public void FinishRegistering() + { + componentBitSet.FinishRegistering(); + } + private TypedComponentStore Lookup() where TComponent : struct, IComponent { //RegisterComponentType(); @@ -41,6 +47,11 @@ namespace Encompass return Stores.ContainsKey(type) && Stores[type].Has(entity); } + public IEnumerable EntitiesWithComponents(IEnumerable types) + { + return componentBitSet.EntitiesWithComponents(types); + } + public TComponent Get(Entity entity) where TComponent : struct, IComponent { return Lookup().Get(entity); @@ -49,15 +60,18 @@ namespace Encompass public void Set(Entity entity, TComponent component) where TComponent : struct, IComponent { Lookup().Set(entity, component); + componentBitSet.Set(entity); } public bool Set(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { + componentBitSet.Set(entity); return Lookup().Set(entity, component, priority); } public void Remove(Entity entity) where TComponent : struct, IComponent { + componentBitSet.RemoveComponent(entity); Lookup().Remove(entity); } @@ -67,6 +81,7 @@ namespace Encompass { entry.Remove(entity); } + componentBitSet.DestroyEntity(entity); } public bool Any() where TComponent : struct, IComponent @@ -97,6 +112,7 @@ namespace Encompass public void ClearAll() { + componentBitSet.Clear(); foreach (var store in Stores.Values) { store.Clear(); @@ -106,6 +122,7 @@ namespace Encompass public void SwapWith(ComponentStore other) { (Stores, other.Stores) = (other.Stores, Stores); + (componentBitSet, other.componentBitSet) = (other.componentBitSet, componentBitSet); } } } diff --git a/encompass-cs/ComponentBitSet.cs b/encompass-cs/ComponentBitSet.cs new file mode 100644 index 0000000..ea44dc4 --- /dev/null +++ b/encompass-cs/ComponentBitSet.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Encompass +{ + internal class ComponentBitSet + { + Dictionary entities = new Dictionary(); + Dictionary typeToIndex = new Dictionary(); + BitArray queryArray; + + public void RegisterType() where TComponent : struct, IComponent + { + typeToIndex.Add(typeof(TComponent), typeToIndex.Count); + foreach (var kvp in entities) + { + kvp.Value.Length = typeToIndex.Count; + } + } + + public void FinishRegistering() + { + queryArray = new BitArray(typeToIndex.Count); + } + + public void Clear() + { + entities.Clear(); + } + + public void AddEntity(Entity entity) + { + var bitArray = new BitArray(typeToIndex.Count); + entities.Add(entity, bitArray); // this is gonna create garbage!! fuck!! + } + + public void Set(Entity entity) where TComponent : struct, IComponent + { + if (!entities.ContainsKey(entity)) { AddEntity(entity); } + entities[entity].Set(typeToIndex[typeof(TComponent)], true); + } + + public void RemoveComponent(Entity entity) where TComponent : struct, IComponent + { + entities[entity].Set(typeToIndex[typeof(TComponent)], false); + } + + public void DestroyEntity(Entity entity) + { + entities.Remove(entity); + } + + public IEnumerable EntitiesWithComponents(IEnumerable types) + { + foreach (var kvp in entities) + { + queryArray.SetAll(false); + foreach (var type in types) + { + queryArray.Set(typeToIndex[type], true); + } + queryArray.And(kvp.Value); + var hasComponents = true; + foreach (var type in types) + { + if (!queryArray.Get(typeToIndex[type])) { + hasComponents = false; + break; + } + } + if (hasComponents) { yield return kvp.Key; } + } + } + } +} diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index cf105c5..bc8f304 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Encompass @@ -21,6 +22,11 @@ namespace Encompass componentStore.RegisterComponentType(); } + public void FinishRegistering() + { + componentStore.FinishRegistering(); + } + internal void SetComponentStore(ComponentStore componentStore) { this.componentStore.SwapWith(componentStore); @@ -90,5 +96,10 @@ namespace Encompass componentUpdateManager.Remove(entity); drawLayerManager.UnRegisterComponentWithLayer(entity); } + + public IEnumerable EntitiesWithComponents(IEnumerable types) + { + return componentStore.EntitiesWithComponents(types); + } } } diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 5897b13..5c01b96 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -20,6 +20,14 @@ namespace Encompass UpToDateComponentStore.RegisterComponentType(); } + public void FinishRegistering() + { + existingAndPendingComponentStore.FinishRegistering(); + existingComponentStore.FinishRegistering(); + pendingComponentStore.FinishRegistering(); + UpToDateComponentStore.FinishRegistering(); + } + internal void Clear() { existingAndPendingComponentStore.ClearAll(); diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 56a5c1a..e89ea34 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -611,5 +611,10 @@ namespace Encompass { timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } + + public IEnumerable EntitiesWithComponents(IEnumerable types) + { + return componentManager.EntitiesWithComponents(types); + } } } diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 90b1eea..664ee8b 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -363,6 +363,11 @@ namespace Encompass PreloadJIT(componentTypesToRegister, messageTypes); + componentManager.FinishRegistering(); + componentUpdateManager.FinishRegistering(); + startingComponentStoreForComponentManager.FinishRegistering(); + startingComponentStoreForComponentUpdateManager.FinishRegistering(); + foreach (var engine in engineGraph.TopologicalSort()) { engineOrder.Add(engine); @@ -387,7 +392,7 @@ namespace Encompass /// /// This is necessary because Encompass heavily uses generic methods with value types, /// so the first time any code path runs the JIT gets smashed. This method warms up the runtime. - /// It does so by grabbing all component and message types known to the WorldBuilder and + /// It does so by grabbing all component and message types known to the WorldBuilder and /// executing every possible generic method that could be executed with those types. /// private void PreloadJIT(IEnumerable componentTypes, IEnumerable messageTypes) diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 1edd31f..0a0464c 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1087,5 +1087,43 @@ namespace Tests undilatedDeltaTime.Should().Be(0.5); } + + struct MockComponentB : IComponent { } + + static Entity[] queriedEntities; + + class EntityQueryEngine : Engine + { + public override void Update(double dt) + { + queriedEntities = EntitiesWithComponents(new Type[] { typeof(MockComponent), typeof(MockComponentB) }).ToArray(); + } + } + + [Test] + public void EntitiesWithComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + var entityC = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entity, new MockComponent()); + worldBuilder.SetComponent(entity, new MockComponentB()); + + worldBuilder.SetComponent(entityB, new MockComponent()); + worldBuilder.SetComponent(entityB, new MockComponentB()); + + worldBuilder.SetComponent(entityC, new MockComponentB()); + + worldBuilder.AddEngine(new EntityQueryEngine()); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB }); + } } } From 642f8df4d52aae980ea03f8e2c82c63e4425dfe2 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 20 Dec 2019 11:28:56 -0800 Subject: [PATCH 02/37] pooling bit arrays --- encompass-cs/Collections/BitArrayPool.cs | 33 ++++++++++++++++++++++ encompass-cs/Collections/ComponentStore.cs | 2 +- encompass-cs/ComponentBitSet.cs | 17 ++++++++--- 3 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 encompass-cs/Collections/BitArrayPool.cs diff --git a/encompass-cs/Collections/BitArrayPool.cs b/encompass-cs/Collections/BitArrayPool.cs new file mode 100644 index 0000000..0f0165f --- /dev/null +++ b/encompass-cs/Collections/BitArrayPool.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Encompass +{ + internal class BitArrayPool + { + private Stack bitArrays; + + public BitArrayPool(int capacity) + { + bitArrays = new Stack(capacity); + + for (var i = 0; i < capacity; i++) + { + bitArrays.Push(new BitArray(128)); + } + } + + public BitArray Obtain(int size) + { + var bitArray = bitArrays.Pop(); + bitArray.Length = size; + bitArray.SetAll(false); + return bitArray; + } + + public void Free(BitArray bitArray) + { + bitArrays.Push(bitArray); + } + } +} diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index f5c3b31..88c4d12 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -81,7 +81,7 @@ namespace Encompass { entry.Remove(entity); } - componentBitSet.DestroyEntity(entity); + componentBitSet.RemoveEntity(entity); } public bool Any() where TComponent : struct, IComponent diff --git a/encompass-cs/ComponentBitSet.cs b/encompass-cs/ComponentBitSet.cs index ea44dc4..38c0000 100644 --- a/encompass-cs/ComponentBitSet.cs +++ b/encompass-cs/ComponentBitSet.cs @@ -6,6 +6,7 @@ namespace Encompass { internal class ComponentBitSet { + BitArrayPool bitArrayPool = new BitArrayPool(32768); // todo: set entity cap Dictionary entities = new Dictionary(); Dictionary typeToIndex = new Dictionary(); BitArray queryArray; @@ -26,13 +27,17 @@ namespace Encompass public void Clear() { + foreach (var kvp in entities) + { + bitArrayPool.Free(kvp.Value); + } entities.Clear(); } public void AddEntity(Entity entity) { - var bitArray = new BitArray(typeToIndex.Count); - entities.Add(entity, bitArray); // this is gonna create garbage!! fuck!! + var bitArray = bitArrayPool.Obtain(typeToIndex.Count); + entities.Add(entity, bitArray); } public void Set(Entity entity) where TComponent : struct, IComponent @@ -46,9 +51,13 @@ namespace Encompass entities[entity].Set(typeToIndex[typeof(TComponent)], false); } - public void DestroyEntity(Entity entity) + public void RemoveEntity(Entity entity) { - entities.Remove(entity); + if (entities.ContainsKey(entity)) + { + bitArrayPool.Free(entities[entity]); + entities.Remove(entity); + } } public IEnumerable EntitiesWithComponents(IEnumerable types) From 1f0009b9fbfc8bb4132929869add777bb7c04064 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 20 Dec 2019 12:52:38 -0800 Subject: [PATCH 03/37] integer IDs and entity capacity --- encompass-cs/Entity.cs | 11 ++---- encompass-cs/EntityManager.cs | 39 ++++++++++++------- .../Exceptions/EntityOverflowException.cs | 12 ++++++ encompass-cs/IDManager.cs | 28 +++++++++++++ encompass-cs/WorldBuilder.cs | 8 ++-- test/EntityTest.cs | 16 +++++++- 6 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 encompass-cs/Exceptions/EntityOverflowException.cs create mode 100644 encompass-cs/IDManager.cs diff --git a/encompass-cs/Entity.cs b/encompass-cs/Entity.cs index a08954c..7287499 100644 --- a/encompass-cs/Entity.cs +++ b/encompass-cs/Entity.cs @@ -8,21 +8,16 @@ namespace Encompass /// public struct Entity : IEquatable { - public readonly Guid ID; + public readonly int ID; - internal Entity(Guid id) + internal Entity(int id) { ID = id; } public override bool Equals(object obj) { - if (obj is Entity entity) - { - return Equals(entity); - } - - return false; + return obj is Entity entity && Equals(entity); } public bool Equals(Entity other) diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index 919d4c1..a264046 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -1,30 +1,45 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using Encompass.Exceptions; namespace Encompass { internal class EntityManager { - private readonly HashSet IDs = new HashSet(); + private readonly int entityCapacity; + private readonly IDManager idManager = new IDManager(); + private readonly HashSet IDs = new HashSet(); private readonly HashSet entitiesMarkedForDestroy = new HashSet(); private readonly ComponentManager componentManager; - public EntityManager(ComponentManager componentManager) + public EntityManager(ComponentManager componentManager, int entityCapacity) { this.componentManager = componentManager; + this.entityCapacity = entityCapacity; + } + + private int NextID() + { + return idManager.NextID(); } public Entity CreateEntity() { - var id = NextID(); - var entity = new Entity(id); - IDs.Add(id); - return entity; + if (IDs.Count < entityCapacity) + { + var id = NextID(); + var entity = new Entity(id); + IDs.Add(id); + return entity; + } + else + { + throw new EntityOverflowException("The number of entities has exceeded the entity capacity of {0}", entityCapacity); + } } - public bool EntityExists(Guid id) + public bool EntityExists(int id) { return IDs.Contains(id); } @@ -40,14 +55,10 @@ namespace Encompass { componentManager.MarkAllComponentsOnEntityForRemoval(entity); IDs.Remove(entity.ID); + idManager.Free(entity.ID); } entitiesMarkedForDestroy.Clear(); } - - private Guid NextID() - { - return Guid.NewGuid(); - } } } diff --git a/encompass-cs/Exceptions/EntityOverflowException.cs b/encompass-cs/Exceptions/EntityOverflowException.cs new file mode 100644 index 0000000..37b942c --- /dev/null +++ b/encompass-cs/Exceptions/EntityOverflowException.cs @@ -0,0 +1,12 @@ +using System; + +namespace Encompass.Exceptions +{ + public class EntityOverflowException : Exception + { + public EntityOverflowException( + string format, + params object[] args + ) : base(string.Format(format, args)) { } + } +} diff --git a/encompass-cs/IDManager.cs b/encompass-cs/IDManager.cs new file mode 100644 index 0000000..01bb529 --- /dev/null +++ b/encompass-cs/IDManager.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Encompass +{ + internal class IDManager + { + int nextID = 0; + + private Stack availableIDs = new Stack(); + + public int NextID() + { + if (availableIDs.Count > 0) + { + return availableIDs.Pop(); + } + else + { + return nextID++; + } + } + + public void Free(int ID) + { + availableIDs.Push(ID); + } + } +} diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 664ee8b..cae7d70 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -18,6 +18,7 @@ namespace Encompass /// public class WorldBuilder { + private readonly int entityCapacity; private readonly List engines = new List(); private readonly DirectedGraph engineGraph = GraphBuilder.DirectedGraph(); private readonly ComponentStore startingComponentStoreForComponentManager = new ComponentStore(); @@ -39,14 +40,15 @@ namespace Encompass private readonly HashSet messageTypes = new HashSet(); - public WorldBuilder() + public WorldBuilder(int entityCapacity = 32768) { + this.entityCapacity = entityCapacity; drawLayerManager = new DrawLayerManager(); timeManager = new TimeManager(); componentUpdateManager = new ComponentUpdateManager(); componentManager = new ComponentManager(drawLayerManager, componentUpdateManager); messageManager = new MessageManager(timeManager); - entityManager = new EntityManager(componentManager); + entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(drawLayerManager); } @@ -402,7 +404,7 @@ namespace Encompass var dummyDrawLayerManager = new DrawLayerManager(); var dummyComponentUpdateManager = new ComponentUpdateManager(); var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager); - var dummyEntityManager = new EntityManager(dummyComponentManager); + var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyRenderManager = new RenderManager(dummyDrawLayerManager); var prepEngineOrder = new List(); diff --git a/test/EntityTest.cs b/test/EntityTest.cs index 89b0520..afced33 100644 --- a/test/EntityTest.cs +++ b/test/EntityTest.cs @@ -2,6 +2,7 @@ using NUnit.Framework; using FluentAssertions; using Encompass; +using Encompass.Exceptions; namespace Tests { @@ -20,5 +21,18 @@ namespace Tests Assert.AreEqual(entity, entity); Assert.IsTrue(entity == copyEntity); } + + [Test] + public void EntityOverflowException() + { + var worldBuilder = new WorldBuilder(16); + + for (var i = 0; i < 16; i++) + { + worldBuilder.CreateEntity(); + } + + Assert.Throws(() => worldBuilder.CreateEntity()); + } } -} \ No newline at end of file +} From 728109bfc6612fb3fd1b011cb235b728acbc4d57 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sat, 21 Dec 2019 17:58:07 -0800 Subject: [PATCH 04/37] beginning query syntax system --- encompass-cs/Engine.cs | 12 ++++---- encompass-cs/EntitySetQuery.cs | 48 ++++++++++++++++++++++++++++++++ encompass-cs/encompass-cs.csproj | 11 ++++---- test/EngineTest.cs | 2 +- 4 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 encompass-cs/EntitySetQuery.cs diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index e89ea34..cd17789 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -561,7 +561,7 @@ namespace Encompass /// The time that will elapse before time is fully dilated, in real time. /// The length of real time that time will be fully dilated. /// The time that will elapse before time is fully undilated. - public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime) + protected void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime) { timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime); } @@ -576,7 +576,7 @@ namespace Encompass /// An easing function for the easing in of time dilation. /// The length of real time that time will be fully dilated. /// The time that will elapse before time is fully undilated. - public void ActivateTimeDilation(double factor, double easeInTime, System.Func easeInFunction, double activeTime, double easeOutTime) + protected void ActivateTimeDilation(double factor, double easeInTime, System.Func easeInFunction, double activeTime, double easeOutTime) { timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime); } @@ -591,7 +591,7 @@ namespace Encompass /// The length of real time that time will be fully dilated. /// The time that will elapse before time is fully undilated. /// An easing function for the easing out of time dilation. - public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func easeOutFunction) + protected void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func easeOutFunction) { timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction); } @@ -607,14 +607,14 @@ namespace Encompass /// The length of real time that time will be fully dilated. /// The time that will elapse before time is fully undilated. /// An easing function for the easing out of time dilation. - public void ActivateTimeDilation(double factor, double easeInTime, System.Func easeInFunction, double activeTime, double easeOutTime, System.Func easeOutFunction) + protected void ActivateTimeDilation(double factor, double easeInTime, System.Func easeInFunction, double activeTime, double easeOutTime, System.Func easeOutFunction) { timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } - public IEnumerable EntitiesWithComponents(IEnumerable types) + protected EntitySetQuery QueryEntities() { - return componentManager.EntitiesWithComponents(types); + return new EntitySetQuery(componentManager); } } } diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs new file mode 100644 index 0000000..25d5b7e --- /dev/null +++ b/encompass-cs/EntitySetQuery.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Encompass +{ + public struct EntitySetQuery : IEnumerable + { + private EntitySetQuery(ComponentManager componentManager, ImmutableArray includes, ImmutableArray excludes) + { + ComponentManager = componentManager; + Includes = includes; + Excludes = excludes; + } + + internal EntitySetQuery(ComponentManager componentManager) + { + ComponentManager = componentManager; + Includes = ImmutableArray.Create(); + Excludes = ImmutableArray.Create(); + } + + private ComponentManager ComponentManager { get; } + private ImmutableArray Includes { get; } + private ImmutableArray Excludes { get; } + + public EntitySetQuery With() where TComponent : struct, IComponent + { + return new EntitySetQuery(ComponentManager, Includes.Add(typeof(TComponent)), Excludes); + } + + public EntitySetQuery Without() where TComponent : struct, IComponent + { + return new EntitySetQuery(ComponentManager, Includes, Excludes.Add(typeof(TComponent))); + } + + public IEnumerator GetEnumerator() + { + return ComponentManager.EntitiesWithComponents(Includes).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/encompass-cs/encompass-cs.csproj b/encompass-cs/encompass-cs.csproj index be72ace..cb51bab 100644 --- a/encompass-cs/encompass-cs.csproj +++ b/encompass-cs/encompass-cs.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 Encompass @@ -9,7 +9,7 @@ Moonside Games Encompass ECS https://github.com/encompass-ecs - + Evan Hemsley 2019 Encompass is an engine-agnostic Hyper ECS framework to help you code games, or other kinds of simulations. true @@ -19,10 +19,11 @@ True - + - + + - + \ No newline at end of file diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 0a0464c..a82af05 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1096,7 +1096,7 @@ namespace Tests { public override void Update(double dt) { - queriedEntities = EntitiesWithComponents(new Type[] { typeof(MockComponent), typeof(MockComponentB) }).ToArray(); + queriedEntities = QueryEntities().With().With().ToArray(); } } From 50974c181a6c51bfdd992b64742a5fafc7a476b1 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 22 Dec 2019 01:15:58 -0800 Subject: [PATCH 05/37] advanced query system --- encompass-cs/Collections/ComponentStore.cs | 13 +- encompass-cs/ComponentBitSet.cs | 49 +++---- encompass-cs/ComponentManager.cs | 10 +- encompass-cs/ComponentUpdateManager.cs | 77 +++++++++- encompass-cs/DrawLayerManager.cs | 15 +- encompass-cs/Engine.cs | 5 +- encompass-cs/EntityManager.cs | 11 +- encompass-cs/EntitySetQuery.cs | 34 ++++- encompass-cs/WorldBuilder.cs | 40 +++-- test/EngineTest.cs | 162 ++++++++++++++++++--- 10 files changed, 317 insertions(+), 99 deletions(-) diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 88c4d12..4a95184 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; namespace Encompass @@ -6,7 +7,12 @@ namespace Encompass internal class ComponentStore { private Dictionary Stores = new Dictionary(512); - private ComponentBitSet componentBitSet = new ComponentBitSet(); + private ComponentBitSet componentBitSet; + + public ComponentStore(Dictionary typeToIndex) + { + componentBitSet = new ComponentBitSet(typeToIndex); + } public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable() { @@ -22,7 +28,6 @@ namespace Encompass { var store = new TypedComponentStore(); Stores.Add(typeof(TComponent), store); - componentBitSet.RegisterType(); } } @@ -47,9 +52,9 @@ namespace Encompass return Stores.ContainsKey(type) && Stores[type].Has(entity); } - public IEnumerable EntitiesWithComponents(IEnumerable types) + public BitArray EntityBitArray(Entity entity) { - return componentBitSet.EntitiesWithComponents(types); + return componentBitSet.EntityBitArray(entity); } public TComponent Get(Entity entity) where TComponent : struct, IComponent diff --git a/encompass-cs/ComponentBitSet.cs b/encompass-cs/ComponentBitSet.cs index 38c0000..7ec25f4 100644 --- a/encompass-cs/ComponentBitSet.cs +++ b/encompass-cs/ComponentBitSet.cs @@ -8,21 +8,25 @@ namespace Encompass { BitArrayPool bitArrayPool = new BitArrayPool(32768); // todo: set entity cap Dictionary entities = new Dictionary(); - Dictionary typeToIndex = new Dictionary(); - BitArray queryArray; + Dictionary TypeToIndex { get; set; } + BitArray withQueryArray; + BitArray withoutQueryArray; + BitArray emptyArray; - public void RegisterType() where TComponent : struct, IComponent + public ComponentBitSet(Dictionary typeToIndex) { - typeToIndex.Add(typeof(TComponent), typeToIndex.Count); - foreach (var kvp in entities) - { - kvp.Value.Length = typeToIndex.Count; - } + TypeToIndex = typeToIndex; } public void FinishRegistering() { - queryArray = new BitArray(typeToIndex.Count); + withQueryArray = new BitArray(TypeToIndex.Count); + withoutQueryArray = new BitArray(TypeToIndex.Count); + emptyArray = new BitArray(TypeToIndex.Count); + foreach (var kvp in entities) + { + kvp.Value.Length = TypeToIndex.Count; + } } public void Clear() @@ -36,19 +40,19 @@ namespace Encompass public void AddEntity(Entity entity) { - var bitArray = bitArrayPool.Obtain(typeToIndex.Count); + var bitArray = bitArrayPool.Obtain(TypeToIndex.Count); entities.Add(entity, bitArray); } public void Set(Entity entity) where TComponent : struct, IComponent { if (!entities.ContainsKey(entity)) { AddEntity(entity); } - entities[entity].Set(typeToIndex[typeof(TComponent)], true); + entities[entity].Set(TypeToIndex[typeof(TComponent)], true); } public void RemoveComponent(Entity entity) where TComponent : struct, IComponent { - entities[entity].Set(typeToIndex[typeof(TComponent)], false); + entities[entity].Set(TypeToIndex[typeof(TComponent)], false); } public void RemoveEntity(Entity entity) @@ -60,26 +64,9 @@ namespace Encompass } } - public IEnumerable EntitiesWithComponents(IEnumerable types) + public BitArray EntityBitArray(Entity entity) { - foreach (var kvp in entities) - { - queryArray.SetAll(false); - foreach (var type in types) - { - queryArray.Set(typeToIndex[type], true); - } - queryArray.And(kvp.Value); - var hasComponents = true; - foreach (var type in types) - { - if (!queryArray.Get(typeToIndex[type])) { - hasComponents = false; - break; - } - } - if (hasComponents) { yield return kvp.Key; } - } + return entities.ContainsKey(entity) ? entities[entity] : emptyArray; } } } diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index bc8f304..30d024b 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -8,13 +8,14 @@ namespace Encompass private readonly DrawLayerManager drawLayerManager; private readonly ComponentUpdateManager componentUpdateManager; - private readonly ComponentStore componentStore = new ComponentStore(); + private readonly ComponentStore componentStore; private readonly HashSet entitiesMarkedForRemoval = new HashSet(); - public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager) + public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager, Dictionary typeToIndex) { this.drawLayerManager = drawLayerManager; this.componentUpdateManager = componentUpdateManager; + componentStore = new ComponentStore(typeToIndex); } public void RegisterComponentType() where TComponent : struct, IComponent @@ -96,10 +97,5 @@ namespace Encompass componentUpdateManager.Remove(entity); drawLayerManager.UnRegisterComponentWithLayer(entity); } - - public IEnumerable EntitiesWithComponents(IEnumerable types) - { - return componentStore.EntitiesWithComponents(types); - } } } diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 5c01b96..3b270db 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -1,16 +1,27 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; namespace Encompass { internal class ComponentUpdateManager { - private readonly ComponentStore existingAndPendingComponentStore = new ComponentStore(); - private readonly ComponentStore existingComponentStore = new ComponentStore(); - private readonly ComponentStore pendingComponentStore = new ComponentStore(); + private readonly ComponentStore existingAndPendingComponentStore; + private readonly ComponentStore existingComponentStore; + private readonly ComponentStore pendingComponentStore; private readonly Dictionary> typeToEntityToPendingComponentPriority = new Dictionary>(128); + private Dictionary typeToIndex; + public ComponentStore UpToDateComponentStore { get; private set; } - public ComponentStore UpToDateComponentStore { get; private set; } = new ComponentStore(); + public ComponentUpdateManager(Dictionary typeToIndex) + { + existingAndPendingComponentStore = new ComponentStore(typeToIndex); + existingComponentStore = new ComponentStore(typeToIndex); + pendingComponentStore = new ComponentStore(typeToIndex); + UpToDateComponentStore = new ComponentStore(typeToIndex); + this.typeToIndex = typeToIndex; + } public void RegisterComponentType() where TComponent : struct, IComponent { @@ -183,5 +194,63 @@ namespace Encompass { UpToDateComponentStore.Remove(entity); } + + internal IEnumerable QueryEntities(IEnumerable entities, HashSet readTypes, HashSet readPendingTypes, IEnumerable withTypes, IEnumerable withoutTypes) + { + var pendingMask = new BitArray(typeToIndex.Count); + foreach (var type in readPendingTypes) + { + pendingMask.Set(typeToIndex[type], true); + } + + var readMask = new BitArray(typeToIndex.Count); + foreach (var type in readTypes) + { + readMask.Set(typeToIndex[type], true); + } + + var withMask = new BitArray(typeToIndex.Count); + foreach (var type in withTypes) + { + withMask.Set(typeToIndex[type], true); + } + + var withoutMask = new BitArray(typeToIndex.Count); + foreach (var type in withoutTypes) + { + withoutMask.Set(typeToIndex[type], true); + } + + foreach (var entity in entities) + { + var pendingEntity = pendingComponentStore.EntityBitArray(entity); + var existingEntity = existingComponentStore.EntityBitArray(entity); + + if (VerifyTypes(pendingEntity, existingEntity, readMask, pendingMask, withMask, withoutMask)) { yield return entity; } + } + } + + internal bool VerifyTypes(BitArray pendingEntity, BitArray existingEntity, BitArray readMask, BitArray pendingMask, BitArray withMask, BitArray withoutMask) + { + var arrayA = new BitArray(typeToIndex.Count); + var arrayB = new BitArray(typeToIndex.Count); + var arrayC = new BitArray(typeToIndex.Count); + + var notWithMask = new BitArray(typeToIndex.Count); + notWithMask.Or(withMask).Not(); + + arrayA.Or(pendingMask).And(withMask).And(pendingEntity); + arrayB.Or(readMask).And(withMask).And(existingEntity); + arrayA.Or(arrayB).Or(notWithMask); + + arrayB.SetAll(false); + arrayB.Or(pendingMask).And(withoutMask).And(pendingEntity).Not(); + arrayC.Or(readMask).And(withoutMask).And(existingEntity).Not(); + arrayB.And(arrayC); + + arrayA.And(arrayB); + + return !arrayA.Cast().Contains(false); + } } } diff --git a/encompass-cs/DrawLayerManager.cs b/encompass-cs/DrawLayerManager.cs index 7c9a68f..65cde48 100644 --- a/encompass-cs/DrawLayerManager.cs +++ b/encompass-cs/DrawLayerManager.cs @@ -13,11 +13,12 @@ namespace Encompass private readonly Dictionary> layerIndexToGeneralRenderers = new Dictionary>(512); private readonly Dictionary> typeToEntityToLayer = new Dictionary>(512); - + private Dictionary typeToIndex; public IEnumerable LayerOrder { get { return layerOrder.Values; } } - public DrawLayerManager() + public DrawLayerManager(Dictionary typeToIndex) { + this.typeToIndex = typeToIndex; RegisterDrawLayer(0); } @@ -27,7 +28,15 @@ namespace Encompass { layerOrder.Add(layer, layer); layerIndexToGeneralRenderers.Add(layer, new HashSet()); - layerIndexToComponentStore.Add(layer, new ComponentStore()); + layerIndexToComponentStore.Add(layer, new ComponentStore(typeToIndex)); + } + } + + public void FinishRegistering() + { + foreach (var store in layerIndexToComponentStore.Values) + { + store.FinishRegistering(); } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index cd17789..457c6f2 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -612,9 +612,12 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } + /// + /// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria. + /// protected EntitySetQuery QueryEntities() { - return new EntitySetQuery(componentManager); + return new EntitySetQuery(entityManager, componentUpdateManager, readTypes, readPendingTypes); } } } diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index a264046..36650c5 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -7,12 +7,17 @@ namespace Encompass { private readonly int entityCapacity; private readonly IDManager idManager = new IDManager(); - private readonly HashSet IDs = new HashSet(); + private readonly Dictionary IDs = new Dictionary(32768); private readonly HashSet entitiesMarkedForDestroy = new HashSet(); private readonly ComponentManager componentManager; + public IEnumerable Entities + { + get { return IDs.Values; } + } + public EntityManager(ComponentManager componentManager, int entityCapacity) { this.componentManager = componentManager; @@ -30,7 +35,7 @@ namespace Encompass { var id = NextID(); var entity = new Entity(id); - IDs.Add(id); + IDs.Add(id, entity); return entity; } else @@ -41,7 +46,7 @@ namespace Encompass public bool EntityExists(int id) { - return IDs.Contains(id); + return IDs.ContainsKey(id); } public void MarkForDestroy(Entity entity) diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 25d5b7e..3fd2e59 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -5,39 +5,57 @@ using System.Collections.Immutable; namespace Encompass { + /// + /// EntitySetQuery is used to efficiently obtain a set of Entities that have all required Components and do not have any forbidden Components. + /// public struct EntitySetQuery : IEnumerable { - private EntitySetQuery(ComponentManager componentManager, ImmutableArray includes, ImmutableArray excludes) + private EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet readTypes, HashSet readPendingTypes, ImmutableArray includes, ImmutableArray excludes) { - ComponentManager = componentManager; + EntityManager = entityManager; + ComponentUpdateManager = componentUpdateManager; + ReadTypes = readTypes; + ReadPendingTypes = readPendingTypes; Includes = includes; Excludes = excludes; } - internal EntitySetQuery(ComponentManager componentManager) + internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet readTypes, HashSet readPendingTypes) { - ComponentManager = componentManager; + EntityManager = entityManager; + ComponentUpdateManager = componentUpdateManager; + ReadTypes = readTypes; + ReadPendingTypes = readPendingTypes; Includes = ImmutableArray.Create(); Excludes = ImmutableArray.Create(); } - private ComponentManager ComponentManager { get; } + private EntityManager EntityManager { get; } + private ComponentUpdateManager ComponentUpdateManager { get; } + private HashSet ReadTypes { get; } + private HashSet ReadPendingTypes { get; } private ImmutableArray Includes { get; } private ImmutableArray Excludes { get; } + /// + /// Designates that the given component type is required. + /// public EntitySetQuery With() where TComponent : struct, IComponent { - return new EntitySetQuery(ComponentManager, Includes.Add(typeof(TComponent)), Excludes); + return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes.Add(typeof(TComponent)), Excludes); } + /// + /// Designates that the given component type is forbidden. + /// public EntitySetQuery Without() where TComponent : struct, IComponent { - return new EntitySetQuery(ComponentManager, Includes, Excludes.Add(typeof(TComponent))); + return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes, Excludes.Add(typeof(TComponent))); } public IEnumerator GetEnumerator() { - return ComponentManager.EntitiesWithComponents(Includes).GetEnumerator(); + return ComponentUpdateManager.QueryEntities(EntityManager.Entities, ReadTypes, ReadPendingTypes, Includes, Excludes).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index cae7d70..1e88a88 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -21,8 +21,8 @@ namespace Encompass private readonly int entityCapacity; private readonly List engines = new List(); private readonly DirectedGraph engineGraph = GraphBuilder.DirectedGraph(); - private readonly ComponentStore startingComponentStoreForComponentManager = new ComponentStore(); - private readonly ComponentStore startingComponentStoreForComponentUpdateManager = new ComponentStore(); + private readonly ComponentStore startingComponentStoreForComponentManager; + private readonly ComponentStore startingComponentStoreForComponentUpdateManager; private readonly ComponentManager componentManager; private readonly EntityManager entityManager; @@ -40,16 +40,21 @@ namespace Encompass private readonly HashSet messageTypes = new HashSet(); + private readonly Dictionary typeToIndex = new Dictionary(); + public WorldBuilder(int entityCapacity = 32768) { this.entityCapacity = entityCapacity; - drawLayerManager = new DrawLayerManager(); + drawLayerManager = new DrawLayerManager(typeToIndex); timeManager = new TimeManager(); - componentUpdateManager = new ComponentUpdateManager(); - componentManager = new ComponentManager(drawLayerManager, componentUpdateManager); + componentUpdateManager = new ComponentUpdateManager(typeToIndex); + componentManager = new ComponentManager(drawLayerManager, componentUpdateManager, typeToIndex); messageManager = new MessageManager(timeManager); entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(drawLayerManager); + + startingComponentStoreForComponentManager = new ComponentStore(typeToIndex); + startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex); } /// @@ -84,7 +89,9 @@ namespace Encompass RegisterComponentType(); componentTypesToRegister.Add(typeof(TComponent)); + startingComponentStoreForComponentManager.FinishRegistering(); startingComponentStoreForComponentManager.Set(entity, component); + startingComponentStoreForComponentUpdateManager.FinishRegistering(); startingComponentStoreForComponentUpdateManager.Set(entity, component); if (component is IDrawableComponent drawableComponent) @@ -96,10 +103,14 @@ namespace Encompass internal void RegisterComponentType() where TComponent : struct, IComponent { - componentManager.RegisterComponentType(); - componentUpdateManager.RegisterComponentType(); - startingComponentStoreForComponentManager.RegisterComponentType(); - startingComponentStoreForComponentUpdateManager.RegisterComponentType(); + if (!typeToIndex.ContainsKey(typeof(TComponent))) + { + typeToIndex.Add(typeof(TComponent), typeToIndex.Count); + componentManager.RegisterComponentType(); + componentUpdateManager.RegisterComponentType(); + startingComponentStoreForComponentManager.RegisterComponentType(); + startingComponentStoreForComponentUpdateManager.RegisterComponentType(); + } } internal void RegisterMessageTypes(IEnumerable types) @@ -363,13 +374,14 @@ namespace Encompass engineOrder.Add(emitterEngine); } - PreloadJIT(componentTypesToRegister, messageTypes); - componentManager.FinishRegistering(); componentUpdateManager.FinishRegistering(); + drawLayerManager.FinishRegistering(); startingComponentStoreForComponentManager.FinishRegistering(); startingComponentStoreForComponentUpdateManager.FinishRegistering(); + PreloadJIT(componentTypesToRegister, messageTypes); + foreach (var engine in engineGraph.TopologicalSort()) { engineOrder.Add(engine); @@ -401,9 +413,9 @@ namespace Encompass { var dummyTimeManager = new TimeManager(); var dummyMessageManager = new MessageManager(dummyTimeManager); - var dummyDrawLayerManager = new DrawLayerManager(); - var dummyComponentUpdateManager = new ComponentUpdateManager(); - var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager); + var dummyDrawLayerManager = new DrawLayerManager(typeToIndex); + var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex); + var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyRenderManager = new RenderManager(dummyDrawLayerManager); diff --git a/test/EngineTest.cs b/test/EngineTest.cs index a82af05..7d5b73d 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1088,42 +1088,156 @@ namespace Tests undilatedDeltaTime.Should().Be(0.5); } - struct MockComponentB : IComponent { } - - static Entity[] queriedEntities; - - class EntityQueryEngine : Engine + public class QueryTests { - public override void Update(double dt) + struct MockComponentB : IComponent { } + struct MockComponentC : IComponent { } + struct MockComponentD : IComponent { } + + [Reads(typeof(MockComponent), typeof(MockComponentB))] + class EntityQueryWithComponentsEngine : Engine { - queriedEntities = QueryEntities().With().With().ToArray(); + private List entities; + + public EntityQueryWithComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().With().With()); + } } - } - [Test] - public void EntitiesWithComponents() - { - var worldBuilder = new WorldBuilder(); + [Test] + public void EntitiesWithComponents() + { + var worldBuilder = new WorldBuilder(); - var entity = worldBuilder.CreateEntity(); - var entityB = worldBuilder.CreateEntity(); - var entityC = worldBuilder.CreateEntity(); + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + var entityC = worldBuilder.CreateEntity(); - worldBuilder.SetComponent(entity, new MockComponent()); - worldBuilder.SetComponent(entity, new MockComponentB()); + worldBuilder.SetComponent(entity, new MockComponent()); + worldBuilder.SetComponent(entity, new MockComponentB()); - worldBuilder.SetComponent(entityB, new MockComponent()); - worldBuilder.SetComponent(entityB, new MockComponentB()); + worldBuilder.SetComponent(entityB, new MockComponent()); + worldBuilder.SetComponent(entityB, new MockComponentB()); - worldBuilder.SetComponent(entityC, new MockComponentB()); + worldBuilder.SetComponent(entityC, new MockComponentB()); - worldBuilder.AddEngine(new EntityQueryEngine()); + var queriedEntities = new List(); + worldBuilder.AddEngine(new EntityQueryWithComponentsEngine(queriedEntities)); - var world = worldBuilder.Build(); + var world = worldBuilder.Build(); - world.Update(0.01); + world.Update(0.01); - queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB }); + queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB }); + } + + [Reads(typeof(MockComponent))] + class EntityQueryWithoutComponentsEngine : Engine + { + private List entities; + + public EntityQueryWithoutComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().Without()); + } + } + + [Test] + public void EntitiesWithoutComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + var entityC = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entity, new MockComponent()); + worldBuilder.SetComponent(entity, new MockComponentB()); + + worldBuilder.SetComponent(entityB, new MockComponent()); + worldBuilder.SetComponent(entityB, new MockComponentB()); + + worldBuilder.SetComponent(entityC, new MockComponentB()); + + var queriedEntities = new List(); + worldBuilder.AddEngine(new EntityQueryWithoutComponentsEngine(queriedEntities)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityC }); + } + + [Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))] + class EntityQueryWithandWithoutComponentsEngine : Engine + { + private List entities; + + public EntityQueryWithandWithoutComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().With() + .With() + .Without()); + } + } + + [Test] + public void EntitiesWithAndWithoutComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + var entityC = worldBuilder.CreateEntity(); + var entityD = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entity, new MockComponent()); + worldBuilder.SetComponent(entity, new MockComponentB()); + worldBuilder.SetComponent(entity, new MockComponentD()); + + worldBuilder.SetComponent(entityB, new MockComponent()); + + worldBuilder.SetComponent(entityC, new MockComponent()); + worldBuilder.SetComponent(entityC, new MockComponentB()); + worldBuilder.SetComponent(entityC, new MockComponentC()); + worldBuilder.SetComponent(entityC, new MockComponentD()); + + worldBuilder.SetComponent(entityD, new MockComponent()); + worldBuilder.SetComponent(entityD, new MockComponentB()); + worldBuilder.SetComponent(entityD, new MockComponentC()); + + var queriedEntities = new List(); + worldBuilder.AddEngine(new EntityQueryWithandWithoutComponentsEngine(queriedEntities)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD }); + } } } } From 120fb738d7deccb70a5127d8f5973f080ef4e3c8 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 22 Dec 2019 11:44:35 -0800 Subject: [PATCH 06/37] add more query tests --- test/EngineTest.cs | 211 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 7d5b73d..b36541d 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1238,6 +1238,217 @@ namespace Tests queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD }); } + + [Reads(typeof(MockComponent))] + [WritesPending(typeof(MockComponentB))] + [Writes(typeof(MockComponentB), 0)] + class AddPendingComponentEngine : Engine + { + public override void Update(double dt) + { + foreach (var entity in QueryEntities().With()) + { + SetComponent(entity, new MockComponentB()); + } + } + } + + [ReadsPending(typeof(MockComponentB))] + class EntityQueryWithPendingComponentsEngine : Engine + { + private List entities; + + public EntityQueryWithPendingComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().With()); + } + } + + [Test] + public void EntitiesWithPendingComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entity, new MockComponent()); + + var queriedEntities = new List(); + worldBuilder.AddEngine(new AddPendingComponentEngine()); + worldBuilder.AddEngine(new EntityQueryWithPendingComponentsEngine(queriedEntities)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entity }); + } + + [ReadsPending(typeof(MockComponentB))] + class EntityQueryWithoutPendingComponentsEngine : Engine + { + private List entities; + + public EntityQueryWithoutPendingComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().Without()); + } + } + + [Test] + public void EntitiesWithoutPendingComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entity, new MockComponent()); + + var queriedEntities = new List(); + worldBuilder.AddEngine(new AddPendingComponentEngine()); + worldBuilder.AddEngine(new EntityQueryWithoutPendingComponentsEngine(queriedEntities)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB }); + } + + [Reads(typeof(MockComponentC), typeof(MockComponentD))] + [WritesPending(typeof(MockComponent), typeof(MockComponentB))] + [Writes(typeof(MockComponent), 0)] + [Writes(typeof(MockComponentB), 0)] + class ConditionallyAddPendingComponentsEngine : Engine + { + public override void Update(double dt) + { + foreach (var entity in QueryEntities().With()) + { + SetComponent(entity, new MockComponent()); + } + + foreach (var entity in QueryEntities().With()) + { + SetComponent(entity, new MockComponent()); + SetComponent(entity, new MockComponentB()); + } + } + } + + [ReadsPending(typeof(MockComponent), typeof(MockComponentB))] + class EntityQueryWithAndWithoutPendingComponentsEngine : Engine + { + private List entities; + + public EntityQueryWithAndWithoutPendingComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().With().Without()); + } + } + + [Test] + public void EntitiesWithAndWithoutPendingComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + var entityC = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entityB, new MockComponentC()); + worldBuilder.SetComponent(entityC, new MockComponentD()); + + var queriedEntities = new List(); + worldBuilder.AddEngine(new ConditionallyAddPendingComponentsEngine()); + worldBuilder.AddEngine(new EntityQueryWithAndWithoutPendingComponentsEngine(queriedEntities)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB }); + } + + [Reads(typeof(MockComponentC))] + [WritesPending(typeof(MockComponentB))] + [Writes(typeof(MockComponentB), 0)] + class ConditionallyAddPendingComponentEngine : Engine + { + public override void Update(double dt) + { + foreach (var entity in QueryEntities().With()) + { + SetComponent(entity, new MockComponentB()); + } + } + } + + [ReadsPending(typeof(MockComponentB))] + [Reads(typeof(MockComponent))] + class EntityQueryWithPendingAndNonPendingComponents : Engine + { + private List entities; + + public EntityQueryWithPendingAndNonPendingComponents(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().With().With()); + } + } + + [Test] + public void EntitiesWithPendingAndNonPendingComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + var entityC = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entityB, new MockComponent()); + worldBuilder.SetComponent(entityB, new MockComponentC()); + worldBuilder.SetComponent(entityC, new MockComponentD()); + + var queriedEntities = new List(); + worldBuilder.AddEngine(new ConditionallyAddPendingComponentEngine()); + worldBuilder.AddEngine(new EntityQueryWithPendingAndNonPendingComponents(queriedEntities)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB }); + } } } } From 5299d6ddad7a8b11e34ad6387fefe35f9bfe7fc7 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 22 Dec 2019 11:54:37 -0800 Subject: [PATCH 07/37] remove bitarray allocations on query --- encompass-cs/ComponentUpdateManager.cs | 61 +++++++++++++++++--------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 3b270db..f8f38d5 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -14,6 +14,16 @@ namespace Encompass private Dictionary typeToIndex; public ComponentStore UpToDateComponentStore { get; private set; } + // bitarray references to avoid garbage collection + BitArray pendingMask; + BitArray readMask; + BitArray withMask; + BitArray withoutMask; + BitArray notWithMask; + BitArray solverArrayA; + BitArray solverArrayB; + BitArray solverArrayC; + public ComponentUpdateManager(Dictionary typeToIndex) { existingAndPendingComponentStore = new ComponentStore(typeToIndex); @@ -37,6 +47,15 @@ namespace Encompass existingComponentStore.FinishRegistering(); pendingComponentStore.FinishRegistering(); UpToDateComponentStore.FinishRegistering(); + + pendingMask = new BitArray(typeToIndex.Count); + readMask = new BitArray(typeToIndex.Count); + withMask = new BitArray(typeToIndex.Count); + withoutMask = new BitArray(typeToIndex.Count); + notWithMask = new BitArray(typeToIndex.Count); + solverArrayA = new BitArray(typeToIndex.Count); + solverArrayB = new BitArray(typeToIndex.Count); + solverArrayC = new BitArray(typeToIndex.Count); } internal void Clear() @@ -197,60 +216,60 @@ namespace Encompass internal IEnumerable QueryEntities(IEnumerable entities, HashSet readTypes, HashSet readPendingTypes, IEnumerable withTypes, IEnumerable withoutTypes) { - var pendingMask = new BitArray(typeToIndex.Count); + pendingMask.SetAll(false); foreach (var type in readPendingTypes) { pendingMask.Set(typeToIndex[type], true); } - var readMask = new BitArray(typeToIndex.Count); + readMask.SetAll(false); foreach (var type in readTypes) { readMask.Set(typeToIndex[type], true); } - var withMask = new BitArray(typeToIndex.Count); + withMask.SetAll(false); foreach (var type in withTypes) { withMask.Set(typeToIndex[type], true); } - var withoutMask = new BitArray(typeToIndex.Count); + withoutMask.SetAll(false); foreach (var type in withoutTypes) { withoutMask.Set(typeToIndex[type], true); } + notWithMask.SetAll(false); + notWithMask.Or(withMask).Not(); + foreach (var entity in entities) { var pendingEntity = pendingComponentStore.EntityBitArray(entity); var existingEntity = existingComponentStore.EntityBitArray(entity); - if (VerifyTypes(pendingEntity, existingEntity, readMask, pendingMask, withMask, withoutMask)) { yield return entity; } + if (VerifyTypes(pendingEntity, existingEntity, readMask, pendingMask, withMask, withoutMask, notWithMask)) { yield return entity; } } } - internal bool VerifyTypes(BitArray pendingEntity, BitArray existingEntity, BitArray readMask, BitArray pendingMask, BitArray withMask, BitArray withoutMask) + internal bool VerifyTypes(BitArray pendingEntity, BitArray existingEntity, BitArray readMask, BitArray pendingMask, BitArray withMask, BitArray withoutMask, BitArray notWithMask) { - var arrayA = new BitArray(typeToIndex.Count); - var arrayB = new BitArray(typeToIndex.Count); - var arrayC = new BitArray(typeToIndex.Count); + solverArrayA.SetAll(false); + solverArrayB.SetAll(false); + solverArrayC.SetAll(false); - var notWithMask = new BitArray(typeToIndex.Count); - notWithMask.Or(withMask).Not(); + solverArrayA.Or(pendingMask).And(withMask).And(pendingEntity); + solverArrayB.Or(readMask).And(withMask).And(existingEntity); + solverArrayA.Or(solverArrayB).Or(notWithMask); - arrayA.Or(pendingMask).And(withMask).And(pendingEntity); - arrayB.Or(readMask).And(withMask).And(existingEntity); - arrayA.Or(arrayB).Or(notWithMask); + solverArrayB.SetAll(false); + solverArrayB.Or(pendingMask).And(withoutMask).And(pendingEntity).Not(); + solverArrayC.Or(readMask).And(withoutMask).And(existingEntity).Not(); + solverArrayB.And(solverArrayC); - arrayB.SetAll(false); - arrayB.Or(pendingMask).And(withoutMask).And(pendingEntity).Not(); - arrayC.Or(readMask).And(withoutMask).And(existingEntity).Not(); - arrayB.And(arrayC); + solverArrayA.And(solverArrayB); - arrayA.And(arrayB); - - return !arrayA.Cast().Contains(false); + return !solverArrayA.Cast().Contains(false); } } } From 570b6832addf04c245477de6904bc823e3f156ad Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 22 Dec 2019 12:00:59 -0800 Subject: [PATCH 08/37] check entity in store before remove component --- encompass-cs/ComponentBitSet.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/encompass-cs/ComponentBitSet.cs b/encompass-cs/ComponentBitSet.cs index 7ec25f4..3b40794 100644 --- a/encompass-cs/ComponentBitSet.cs +++ b/encompass-cs/ComponentBitSet.cs @@ -52,7 +52,10 @@ namespace Encompass public void RemoveComponent(Entity entity) where TComponent : struct, IComponent { - entities[entity].Set(TypeToIndex[typeof(TComponent)], false); + if (entities.ContainsKey(entity)) + { + entities[entity].Set(TypeToIndex[typeof(TComponent)], false); + } } public void RemoveEntity(Entity entity) From 9a1a210039ed7331f72009b322b71f9db156ac33 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 22 Dec 2019 16:21:07 -0800 Subject: [PATCH 09/37] implement internal BitSet1024 --- encompass-cs/Collections/BitSet1024.cs | 178 ++++++++++++++++++++ encompass-cs/Collections/ComponentBitSet.cs | 60 +++++++ encompass-cs/Collections/ComponentStore.cs | 6 +- encompass-cs/ComponentBitSet.cs | 75 --------- encompass-cs/ComponentUpdateManager.cs | 64 +++---- test/BitSet1024Test.cs | 62 +++++++ 6 files changed, 322 insertions(+), 123 deletions(-) create mode 100644 encompass-cs/Collections/BitSet1024.cs create mode 100644 encompass-cs/Collections/ComponentBitSet.cs delete mode 100644 encompass-cs/ComponentBitSet.cs create mode 100644 test/BitSet1024Test.cs diff --git a/encompass-cs/Collections/BitSet1024.cs b/encompass-cs/Collections/BitSet1024.cs new file mode 100644 index 0000000..239cd79 --- /dev/null +++ b/encompass-cs/Collections/BitSet1024.cs @@ -0,0 +1,178 @@ +using System.Numerics; + +namespace Encompass.Collections +{ + public static class BitSet1024Builder + { + public static BitSet1024 Zeroes() + { + return new BitSet1024(Vector256Builder.Zeroes(), Vector256Builder.Zeroes(), Vector256Builder.Zeroes(), Vector256Builder.Zeroes()); + } + + public static BitSet1024 Ones() + { + return new BitSet1024(Vector256Builder.Ones(), Vector256Builder.Ones(), Vector256Builder.Ones(), Vector256Builder.Ones()); + } + } + + public static class Vector256Builder + { + static readonly uint[] zeroes = new uint[8]; // max size of a Vector is 8 uints + static readonly uint[] ones = new uint[8] { uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue }; + static uint[] builderInts = new uint[8]; + + private static void ResetBuilder() + { + for (var i = 0; i < 8; i++) + { + builderInts[i] = 0; + } + } + + public static Vector Zeroes() + { + return new Vector(zeroes); + } + + public static Vector Ones() + { + return new Vector(ones); + } + + public static Vector Build(int index) + { + if (index > 255) { throw new System.ArgumentOutOfRangeException(nameof(index)); } + ResetBuilder(); + builderInts[index / 32] |= (uint)(1 << (index % 32)); + return new Vector(builderInts); + } + } + + public struct BitSet1024 + { + public Vector A { get; } + public Vector B { get; } + public Vector C { get; } + public Vector D { get; } + + internal BitSet1024(Vector a, Vector b, Vector c, Vector d) + { + A = a; + B = b; + C = c; + D = d; + } + + public BitSet1024 And(BitSet1024 other) + { + return new BitSet1024(A & other.A, B & other.B, C & other.C, D & other.D); + } + + public BitSet1024 Or(BitSet1024 other) + { + return new BitSet1024(A | other.A, B | other.B, C | other.C, D | other.D); + } + + public BitSet1024 Not() + { + return new BitSet1024(~A, ~B, ~C, ~D); + } + + public BitSet1024 Set(int index) + { + if (index < 256) + { + return new BitSet1024(A | Vector256Builder.Build(index % 256), B, C, D); + } + else if (index < 512) + { + return new BitSet1024(A, B | Vector256Builder.Build(index % 256), C, D); + } + else if (index < 768) + { + return new BitSet1024(A, B, C | Vector256Builder.Build(index % 256), D); + } + else if (index < 1024) + { + return new BitSet1024(A, B, C, D | Vector256Builder.Build(index % 256)); + } + else + { + throw new System.ArgumentOutOfRangeException(nameof(index)); + } + } + + public BitSet1024 UnSet(int index) + { + if (index < 256) + { + return new BitSet1024(A & ~Vector256Builder.Build(index % 256), B, C, D); + } + else if (index < 512) + { + return new BitSet1024(A, B & ~Vector256Builder.Build(index % 256), C, D); + } + else if (index < 768) + { + return new BitSet1024(A, B, C & ~Vector256Builder.Build(index % 256), D); + } + else if (index < 1024) + { + return new BitSet1024(A, B, C, D & ~Vector256Builder.Build(index % 256)); + } + else + { + throw new System.ArgumentOutOfRangeException(nameof(index)); + } + } + + public bool Get(int index) + { + var vectorIndex = index % 256; + if (index < 256) + { + return (A[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else if (index < 512) + { + return (B[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else if (index < 768) + { + return (C[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else if (index < 1024) + { + return (D[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else + { + throw new System.ArgumentOutOfRangeException(nameof(index)); + } + } + + public bool AllTrue() + { + for (var i = 0; i < 8; i++) + { + if (A[i] != uint.MaxValue) { return false; } + if (B[i] != uint.MaxValue) { return false; } + if (C[i] != uint.MaxValue) { return false; } + if (D[i] != uint.MaxValue) { return false; } + } + return true; + } + + public bool AllFalse() + { + for (var i = 0; i < 8; i++) + { + if (A[i] != 0) { return false; } + if (B[i] != 0) { return false; } + if (C[i] != 0) { return false; } + if (D[i] != 0) { return false; } + } + return true; + } + } +} diff --git a/encompass-cs/Collections/ComponentBitSet.cs b/encompass-cs/Collections/ComponentBitSet.cs new file mode 100644 index 0000000..63433dd --- /dev/null +++ b/encompass-cs/Collections/ComponentBitSet.cs @@ -0,0 +1,60 @@ +using Encompass.Collections; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Encompass +{ + internal class ComponentBitSet + { + Dictionary entities = new Dictionary(); + Dictionary TypeToIndex { get; } + + public ComponentBitSet(Dictionary typeToIndex) + { + TypeToIndex = typeToIndex; + } + + public void FinishRegistering() + { + + } + + public void Clear() + { + entities.Clear(); + } + + public void AddEntity(Entity entity) + { + entities.Add(entity, BitSet1024Builder.Zeroes()); + } + + public void Set(Entity entity) where TComponent : struct, IComponent + { + if (!entities.ContainsKey(entity)) { AddEntity(entity); } + entities[entity] = entities[entity].Set(TypeToIndex[typeof(TComponent)]); + } + + public void RemoveComponent(Entity entity) where TComponent : struct, IComponent + { + if (entities.ContainsKey(entity)) + { + entities[entity] = entities[entity].UnSet(TypeToIndex[typeof(TComponent)]); + } + } + + public void RemoveEntity(Entity entity) + { + if (entities.ContainsKey(entity)) + { + entities.Remove(entity); + } + } + + public BitSet1024 EntityBitArray(Entity entity) + { + return entities.ContainsKey(entity) ? entities[entity] : BitSet1024Builder.Zeroes(); + } + } +} diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 4a95184..6e44131 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -1,5 +1,5 @@ -using System; -using System.Collections; +using Encompass.Collections; +using System; using System.Collections.Generic; namespace Encompass @@ -52,7 +52,7 @@ namespace Encompass return Stores.ContainsKey(type) && Stores[type].Has(entity); } - public BitArray EntityBitArray(Entity entity) + public BitSet1024 EntityBitArray(Entity entity) { return componentBitSet.EntityBitArray(entity); } diff --git a/encompass-cs/ComponentBitSet.cs b/encompass-cs/ComponentBitSet.cs deleted file mode 100644 index 3b40794..0000000 --- a/encompass-cs/ComponentBitSet.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace Encompass -{ - internal class ComponentBitSet - { - BitArrayPool bitArrayPool = new BitArrayPool(32768); // todo: set entity cap - Dictionary entities = new Dictionary(); - Dictionary TypeToIndex { get; set; } - BitArray withQueryArray; - BitArray withoutQueryArray; - BitArray emptyArray; - - public ComponentBitSet(Dictionary typeToIndex) - { - TypeToIndex = typeToIndex; - } - - public void FinishRegistering() - { - withQueryArray = new BitArray(TypeToIndex.Count); - withoutQueryArray = new BitArray(TypeToIndex.Count); - emptyArray = new BitArray(TypeToIndex.Count); - foreach (var kvp in entities) - { - kvp.Value.Length = TypeToIndex.Count; - } - } - - public void Clear() - { - foreach (var kvp in entities) - { - bitArrayPool.Free(kvp.Value); - } - entities.Clear(); - } - - public void AddEntity(Entity entity) - { - var bitArray = bitArrayPool.Obtain(TypeToIndex.Count); - entities.Add(entity, bitArray); - } - - public void Set(Entity entity) where TComponent : struct, IComponent - { - if (!entities.ContainsKey(entity)) { AddEntity(entity); } - entities[entity].Set(TypeToIndex[typeof(TComponent)], true); - } - - public void RemoveComponent(Entity entity) where TComponent : struct, IComponent - { - if (entities.ContainsKey(entity)) - { - entities[entity].Set(TypeToIndex[typeof(TComponent)], false); - } - } - - public void RemoveEntity(Entity entity) - { - if (entities.ContainsKey(entity)) - { - bitArrayPool.Free(entities[entity]); - entities.Remove(entity); - } - } - - public BitArray EntityBitArray(Entity entity) - { - return entities.ContainsKey(entity) ? entities[entity] : emptyArray; - } - } -} diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index f8f38d5..95dae3c 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -1,4 +1,5 @@ -using System; +using Encompass.Collections; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -14,16 +15,6 @@ namespace Encompass private Dictionary typeToIndex; public ComponentStore UpToDateComponentStore { get; private set; } - // bitarray references to avoid garbage collection - BitArray pendingMask; - BitArray readMask; - BitArray withMask; - BitArray withoutMask; - BitArray notWithMask; - BitArray solverArrayA; - BitArray solverArrayB; - BitArray solverArrayC; - public ComponentUpdateManager(Dictionary typeToIndex) { existingAndPendingComponentStore = new ComponentStore(typeToIndex); @@ -47,15 +38,6 @@ namespace Encompass existingComponentStore.FinishRegistering(); pendingComponentStore.FinishRegistering(); UpToDateComponentStore.FinishRegistering(); - - pendingMask = new BitArray(typeToIndex.Count); - readMask = new BitArray(typeToIndex.Count); - withMask = new BitArray(typeToIndex.Count); - withoutMask = new BitArray(typeToIndex.Count); - notWithMask = new BitArray(typeToIndex.Count); - solverArrayA = new BitArray(typeToIndex.Count); - solverArrayB = new BitArray(typeToIndex.Count); - solverArrayC = new BitArray(typeToIndex.Count); } internal void Clear() @@ -216,32 +198,31 @@ namespace Encompass internal IEnumerable QueryEntities(IEnumerable entities, HashSet readTypes, HashSet readPendingTypes, IEnumerable withTypes, IEnumerable withoutTypes) { - pendingMask.SetAll(false); + var pendingMask = BitSet1024Builder.Zeroes(); foreach (var type in readPendingTypes) { - pendingMask.Set(typeToIndex[type], true); + pendingMask = pendingMask.Set(typeToIndex[type]); } - readMask.SetAll(false); + var readMask = BitSet1024Builder.Zeroes(); foreach (var type in readTypes) { - readMask.Set(typeToIndex[type], true); + readMask = readMask.Set(typeToIndex[type]); } - withMask.SetAll(false); + var withMask = BitSet1024Builder.Zeroes(); foreach (var type in withTypes) { - withMask.Set(typeToIndex[type], true); + withMask = withMask.Set(typeToIndex[type]); } - withoutMask.SetAll(false); + var withoutMask = BitSet1024Builder.Zeroes(); foreach (var type in withoutTypes) { - withoutMask.Set(typeToIndex[type], true); + withoutMask = withoutMask.Set(typeToIndex[type]); } - notWithMask.SetAll(false); - notWithMask.Or(withMask).Not(); + var notWithMask = withMask.Not(); foreach (var entity in entities) { @@ -252,24 +233,17 @@ namespace Encompass } } - internal bool VerifyTypes(BitArray pendingEntity, BitArray existingEntity, BitArray readMask, BitArray pendingMask, BitArray withMask, BitArray withoutMask, BitArray notWithMask) + internal bool VerifyTypes(BitSet1024 pendingEntity, BitSet1024 existingEntity, BitSet1024 readMask, BitSet1024 pendingMask, BitSet1024 withMask, BitSet1024 withoutMask, BitSet1024 notWithMask) { - solverArrayA.SetAll(false); - solverArrayB.SetAll(false); - solverArrayC.SetAll(false); + var pending = pendingMask.And(withMask).And(pendingEntity); + var existing = readMask.And(withMask).And(existingEntity); + var withCheck = pending.Or(existing).Or(notWithMask); - solverArrayA.Or(pendingMask).And(withMask).And(pendingEntity); - solverArrayB.Or(readMask).And(withMask).And(existingEntity); - solverArrayA.Or(solverArrayB).Or(notWithMask); + var pendingForbidden = pendingMask.And(withoutMask).And(pendingEntity).Not(); + var existingForbidden = readMask.And(withoutMask).And(existingEntity).Not(); + var withoutCheck = pendingForbidden.And(existingForbidden); - solverArrayB.SetAll(false); - solverArrayB.Or(pendingMask).And(withoutMask).And(pendingEntity).Not(); - solverArrayC.Or(readMask).And(withoutMask).And(existingEntity).Not(); - solverArrayB.And(solverArrayC); - - solverArrayA.And(solverArrayB); - - return !solverArrayA.Cast().Contains(false); + return withCheck.And(withoutCheck).AllTrue(); } } } diff --git a/test/BitSet1024Test.cs b/test/BitSet1024Test.cs new file mode 100644 index 0000000..81e1a4b --- /dev/null +++ b/test/BitSet1024Test.cs @@ -0,0 +1,62 @@ +using Encompass.Collections; +using FluentAssertions; +using NUnit.Framework; + +namespace Tests +{ + public class BitSet1024Test + { + [Test] + public void Zeroes() + { + var bitSet = BitSet1024Builder.Zeroes(); + bitSet.AllFalse().Should().BeTrue(); + } + + [Test] + public void Ones() + { + var bitSet = BitSet1024Builder.Ones(); + bitSet.AllTrue().Should().BeTrue(); + } + + [Test] + public void Set() + { + var bitSet = BitSet1024Builder.Zeroes().Set(5); + bitSet.AllFalse().Should().BeFalse(); + + bitSet = BitSet1024Builder.Zeroes().Set(278); + bitSet.AllFalse().Should().BeFalse(); + + bitSet = BitSet1024Builder.Zeroes().Set(569); + bitSet.AllFalse().Should().BeFalse(); + + bitSet = BitSet1024Builder.Zeroes().Set(1023); + bitSet.AllFalse().Should().BeFalse(); + } + + [Test] + public void UnSet() + { + var bitSet = BitSet1024Builder.Ones().UnSet(562); + bitSet.Get(562).Should().BeFalse(); + bitSet.Set(562).AllTrue().Should().BeTrue(); + } + + [Test] + public void Get() + { + var bitSet = BitSet1024Builder.Zeroes().Set(359); + bitSet.Get(359).Should().BeTrue(); + bitSet.UnSet(359).AllFalse().Should().BeTrue(); + } + + [Test] + public void Not() + { + var bitSet = BitSet1024Builder.Ones().Not(); + bitSet.AllFalse().Should().BeTrue(); + } + } +} From 4e3aaa7d47c48cdb7cdfa3a7bbb24bf95e77c366 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 22 Dec 2019 17:16:54 -0800 Subject: [PATCH 10/37] restructuring entity set query system for caching benefits --- encompass-cs/ComponentUpdateManager.cs | 53 +++--------------- encompass-cs/Engine.cs | 9 +++- encompass-cs/EntitySetQuery.cs | 74 +++++++++++--------------- encompass-cs/EntitySetQueryBuilder.cs | 70 ++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 92 deletions(-) create mode 100644 encompass-cs/EntitySetQueryBuilder.cs diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 95dae3c..8b371ee 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -12,7 +12,8 @@ namespace Encompass private readonly ComponentStore existingComponentStore; private readonly ComponentStore pendingComponentStore; private readonly Dictionary> typeToEntityToPendingComponentPriority = new Dictionary>(128); - private Dictionary typeToIndex; + public Dictionary TypeToIndex { get; } + public ComponentStore UpToDateComponentStore { get; private set; } public ComponentUpdateManager(Dictionary typeToIndex) @@ -21,7 +22,7 @@ namespace Encompass existingComponentStore = new ComponentStore(typeToIndex); pendingComponentStore = new ComponentStore(typeToIndex); UpToDateComponentStore = new ComponentStore(typeToIndex); - this.typeToIndex = typeToIndex; + TypeToIndex = typeToIndex; } public void RegisterComponentType() where TComponent : struct, IComponent @@ -196,54 +197,14 @@ namespace Encompass UpToDateComponentStore.Remove(entity); } - internal IEnumerable QueryEntities(IEnumerable entities, HashSet readTypes, HashSet readPendingTypes, IEnumerable withTypes, IEnumerable withoutTypes) + internal BitSet1024 PendingBits(Entity entity) { - var pendingMask = BitSet1024Builder.Zeroes(); - foreach (var type in readPendingTypes) - { - pendingMask = pendingMask.Set(typeToIndex[type]); - } - - var readMask = BitSet1024Builder.Zeroes(); - foreach (var type in readTypes) - { - readMask = readMask.Set(typeToIndex[type]); - } - - var withMask = BitSet1024Builder.Zeroes(); - foreach (var type in withTypes) - { - withMask = withMask.Set(typeToIndex[type]); - } - - var withoutMask = BitSet1024Builder.Zeroes(); - foreach (var type in withoutTypes) - { - withoutMask = withoutMask.Set(typeToIndex[type]); - } - - var notWithMask = withMask.Not(); - - foreach (var entity in entities) - { - var pendingEntity = pendingComponentStore.EntityBitArray(entity); - var existingEntity = existingComponentStore.EntityBitArray(entity); - - if (VerifyTypes(pendingEntity, existingEntity, readMask, pendingMask, withMask, withoutMask, notWithMask)) { yield return entity; } - } + return pendingComponentStore.EntityBitArray(entity); } - internal bool VerifyTypes(BitSet1024 pendingEntity, BitSet1024 existingEntity, BitSet1024 readMask, BitSet1024 pendingMask, BitSet1024 withMask, BitSet1024 withoutMask, BitSet1024 notWithMask) + internal BitSet1024 ExistingBits(Entity entity) { - var pending = pendingMask.And(withMask).And(pendingEntity); - var existing = readMask.And(withMask).And(existingEntity); - var withCheck = pending.Or(existing).Or(notWithMask); - - var pendingForbidden = pendingMask.And(withoutMask).And(pendingEntity).Not(); - var existingForbidden = readMask.And(withoutMask).And(existingEntity).Not(); - var withoutCheck = pendingForbidden.And(existingForbidden); - - return withCheck.And(withoutCheck).AllTrue(); + return existingComponentStore.EntityBitArray(entity); } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 457c6f2..dfb3f35 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -615,9 +615,14 @@ namespace Encompass /// /// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria. /// - protected EntitySetQuery QueryEntities() + protected EntitySetQueryBuilder EntityQueryBuilder() { - return new EntitySetQuery(entityManager, componentUpdateManager, readTypes, readPendingTypes); + return new EntitySetQueryBuilder( + entityManager, + componentUpdateManager, + readPendingTypes, + readTypes + ); } } } diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 3fd2e59..60bd110 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -1,61 +1,47 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; +using Encompass.Collections; namespace Encompass { - /// - /// EntitySetQuery is used to efficiently obtain a set of Entities that have all required Components and do not have any forbidden Components. - /// public struct EntitySetQuery : IEnumerable { - private EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet readTypes, HashSet readPendingTypes, ImmutableArray includes, ImmutableArray excludes) - { - EntityManager = entityManager; - ComponentUpdateManager = componentUpdateManager; - ReadTypes = readTypes; - ReadPendingTypes = readPendingTypes; - Includes = includes; - Excludes = excludes; - } - - internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet readTypes, HashSet readPendingTypes) - { - EntityManager = entityManager; - ComponentUpdateManager = componentUpdateManager; - ReadTypes = readTypes; - ReadPendingTypes = readPendingTypes; - Includes = ImmutableArray.Create(); - Excludes = ImmutableArray.Create(); - } - private EntityManager EntityManager { get; } private ComponentUpdateManager ComponentUpdateManager { get; } - private HashSet ReadTypes { get; } - private HashSet ReadPendingTypes { get; } - private ImmutableArray Includes { get; } - private ImmutableArray Excludes { get; } + private BitSet1024 WithPendingMask { get; } + private BitSet1024 WithExistingMask { get; } + private BitSet1024 WithoutPendingMask { get; } + private BitSet1024 WithoutExistingMask { get; } + private BitSet1024 NotWithMask { get; } - /// - /// Designates that the given component type is required. - /// - public EntitySetQuery With() where TComponent : struct, IComponent + internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, BitSet1024 withPendingMask, BitSet1024 withExistingMask, BitSet1024 withoutPendingMask, BitSet1024 withoutExistingMask, BitSet1024 notWithMask) { - return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes.Add(typeof(TComponent)), Excludes); - } - - /// - /// Designates that the given component type is forbidden. - /// - public EntitySetQuery Without() where TComponent : struct, IComponent - { - return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes, Excludes.Add(typeof(TComponent))); + EntityManager = entityManager; + ComponentUpdateManager = componentUpdateManager; + WithPendingMask = withPendingMask; + WithExistingMask = withExistingMask; + WithoutPendingMask = withoutPendingMask; + WithoutExistingMask = withoutExistingMask; + NotWithMask = notWithMask; } public IEnumerator GetEnumerator() { - return ComponentUpdateManager.QueryEntities(EntityManager.Entities, ReadTypes, ReadPendingTypes, Includes, Excludes).GetEnumerator(); + foreach (var entity in EntityManager.Entities) + { + var pendingBits = ComponentUpdateManager.PendingBits(entity); + var existingBits = ComponentUpdateManager.ExistingBits(entity); + + var pending = WithPendingMask.And(pendingBits); + var existing = WithExistingMask.And(existingBits); + var withCheck = pending.Or(existing).Or(NotWithMask); + + var pendingForbidden = WithoutPendingMask.And(pendingBits); + var existingForbidden = WithoutExistingMask.And(existingBits); + var withoutCheck = pendingForbidden.And(existingForbidden); + + if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; } + } } IEnumerator IEnumerable.GetEnumerator() diff --git a/encompass-cs/EntitySetQueryBuilder.cs b/encompass-cs/EntitySetQueryBuilder.cs new file mode 100644 index 0000000..00452c5 --- /dev/null +++ b/encompass-cs/EntitySetQueryBuilder.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using Encompass.Collections; + +namespace Encompass +{ + /// + /// EntitySetQuery is used to efficiently obtain a set of Entities that have all required Components and do not have any forbidden Components. + /// + public class EntitySetQueryBuilder + { + internal EntitySetQueryBuilder(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, IEnumerable pendingTypes, IEnumerable existingTypes) + { + EntityManager = entityManager; + ComponentUpdateManager = componentUpdateManager; + WithMask = BitSet1024Builder.Zeroes(); + WithoutMask = BitSet1024Builder.Zeroes(); + + PendingMask = BitSet1024Builder.Zeroes(); + foreach (var type in pendingTypes) + { + PendingMask = PendingMask.Set(componentUpdateManager.TypeToIndex[type]); + } + + ExistingMask = BitSet1024Builder.Zeroes(); + foreach (var type in existingTypes) + { + ExistingMask = ExistingMask.Set(componentUpdateManager.TypeToIndex[type]); + } + } + + private EntityManager EntityManager { get; } + private ComponentUpdateManager ComponentUpdateManager { get; } + private BitSet1024 WithMask { get; set; } + private BitSet1024 WithoutMask { get; set; } + private BitSet1024 PendingMask { get; set; } + private BitSet1024 ExistingMask { get; set; } + + /// + /// Designates that the given component type is required. + /// + public EntitySetQueryBuilder With() where TComponent : struct, IComponent + { + WithMask = WithMask.Set(ComponentUpdateManager.TypeToIndex[typeof(TComponent)]); + return this; + } + + /// + /// Designates that the given component type is forbidden. + /// + public EntitySetQueryBuilder Without() where TComponent : struct, IComponent + { + WithoutMask = WithoutMask.Set(ComponentUpdateManager.TypeToIndex[typeof(TComponent)]); + return this; + } + + public EntitySetQuery Build() + { + return new EntitySetQuery( + EntityManager, + ComponentUpdateManager, + WithMask.And(PendingMask), + WithMask.And(ExistingMask), + WithoutMask.And(PendingMask), + WithoutMask.And(ExistingMask), + WithMask.Not() + ); + } + } +} From 1e73351b07e9f796df7d579e22037b2ce4000d09 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 22 Dec 2019 18:13:35 -0800 Subject: [PATCH 11/37] restructure entity queries to enable caching --- encompass-cs/Attributes/QueryWith.cs | 28 +++++++++ encompass-cs/Attributes/QueryWithout.cs | 28 +++++++++ encompass-cs/Collections/ComponentStore.cs | 20 +++---- encompass-cs/ComponentUpdateManager.cs | 11 +--- encompass-cs/Engine.cs | 62 +++++++++++++++++-- encompass-cs/EntitySetQuery.cs | 25 +++----- encompass-cs/EntitySetQueryBuilder.cs | 70 ---------------------- encompass-cs/WorldBuilder.cs | 4 +- test/EngineTest.cs | 41 +++++++------ 9 files changed, 156 insertions(+), 133 deletions(-) create mode 100644 encompass-cs/Attributes/QueryWith.cs create mode 100644 encompass-cs/Attributes/QueryWithout.cs delete mode 100644 encompass-cs/EntitySetQueryBuilder.cs diff --git a/encompass-cs/Attributes/QueryWith.cs b/encompass-cs/Attributes/QueryWith.cs new file mode 100644 index 0000000..3ae11a6 --- /dev/null +++ b/encompass-cs/Attributes/QueryWith.cs @@ -0,0 +1,28 @@ +using Encompass.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Encompass +{ + [AttributeUsage(AttributeTargets.Class)] + public class QueryWith : Attribute + { + public readonly HashSet queryWithTypes = new HashSet(); + + public QueryWith(params Type[] queryWithTypes) + { + foreach (var queryWithType in queryWithTypes) + { + var isComponent = queryWithType.GetInterfaces().Contains(typeof(IComponent)); + + if (!isComponent) + { + throw new IllegalReadTypeException("{0} must be a Component", queryWithType.Name); + } + + this.queryWithTypes.Add(queryWithType); + } + } + } +} diff --git a/encompass-cs/Attributes/QueryWithout.cs b/encompass-cs/Attributes/QueryWithout.cs new file mode 100644 index 0000000..77f1e9f --- /dev/null +++ b/encompass-cs/Attributes/QueryWithout.cs @@ -0,0 +1,28 @@ +using Encompass.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Encompass +{ + [AttributeUsage(AttributeTargets.Class)] + public class QueryWithout : Attribute + { + public readonly HashSet queryWithoutTypes = new HashSet(); + + public QueryWithout(params Type[] queryWithoutTypes) + { + foreach (var type in queryWithoutTypes) + { + var isComponent = type.GetInterfaces().Contains(typeof(IComponent)); + + if (!isComponent) + { + throw new IllegalReadTypeException("{0} must be a Component", type.Name); + } + + this.queryWithoutTypes.Add(type); + } + } + } +} diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 6e44131..5d3f09a 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -7,11 +7,11 @@ namespace Encompass internal class ComponentStore { private Dictionary Stores = new Dictionary(512); - private ComponentBitSet componentBitSet; + public ComponentBitSet ComponentBitSet { get; private set; } public ComponentStore(Dictionary typeToIndex) { - componentBitSet = new ComponentBitSet(typeToIndex); + ComponentBitSet = new ComponentBitSet(typeToIndex); } public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable() @@ -33,7 +33,7 @@ namespace Encompass public void FinishRegistering() { - componentBitSet.FinishRegistering(); + ComponentBitSet.FinishRegistering(); } private TypedComponentStore Lookup() where TComponent : struct, IComponent @@ -54,7 +54,7 @@ namespace Encompass public BitSet1024 EntityBitArray(Entity entity) { - return componentBitSet.EntityBitArray(entity); + return ComponentBitSet.EntityBitArray(entity); } public TComponent Get(Entity entity) where TComponent : struct, IComponent @@ -65,18 +65,18 @@ namespace Encompass public void Set(Entity entity, TComponent component) where TComponent : struct, IComponent { Lookup().Set(entity, component); - componentBitSet.Set(entity); + ComponentBitSet.Set(entity); } public bool Set(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { - componentBitSet.Set(entity); + ComponentBitSet.Set(entity); return Lookup().Set(entity, component, priority); } public void Remove(Entity entity) where TComponent : struct, IComponent { - componentBitSet.RemoveComponent(entity); + ComponentBitSet.RemoveComponent(entity); Lookup().Remove(entity); } @@ -86,7 +86,7 @@ namespace Encompass { entry.Remove(entity); } - componentBitSet.RemoveEntity(entity); + ComponentBitSet.RemoveEntity(entity); } public bool Any() where TComponent : struct, IComponent @@ -117,7 +117,7 @@ namespace Encompass public void ClearAll() { - componentBitSet.Clear(); + ComponentBitSet.Clear(); foreach (var store in Stores.Values) { store.Clear(); @@ -127,7 +127,7 @@ namespace Encompass public void SwapWith(ComponentStore other) { (Stores, other.Stores) = (other.Stores, Stores); - (componentBitSet, other.componentBitSet) = (other.componentBitSet, componentBitSet); + (ComponentBitSet, other.ComponentBitSet) = (other.ComponentBitSet, ComponentBitSet); } } } diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 8b371ee..d0ee527 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -197,14 +197,7 @@ namespace Encompass UpToDateComponentStore.Remove(entity); } - internal BitSet1024 PendingBits(Entity entity) - { - return pendingComponentStore.EntityBitArray(entity); - } - - internal BitSet1024 ExistingBits(Entity entity) - { - return existingComponentStore.EntityBitArray(entity); - } + internal ComponentBitSet PendingBits { get { return pendingComponentStore.ComponentBitSet; } } + internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index dfb3f35..8e0a8ae 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Collections.Generic; using System.Linq; using Encompass.Exceptions; +using Encompass.Collections; namespace Encompass { @@ -21,6 +22,8 @@ namespace Encompass internal readonly HashSet receiveTypes = new HashSet(); internal readonly HashSet writeTypes = new HashSet(); internal readonly HashSet writePendingTypes = new HashSet(); + internal readonly HashSet queryWithTypes = new HashSet(); + internal readonly HashSet queryWithoutTypes = new HashSet(); internal readonly Dictionary writePriorities = new Dictionary(); internal readonly int defaultWritePriority = 0; @@ -36,6 +39,8 @@ namespace Encompass private ComponentUpdateManager componentUpdateManager; private TimeManager timeManager; + private EntitySetQuery entityQuery; + protected Engine() { ID = Guid.NewGuid(); @@ -82,6 +87,18 @@ namespace Encompass { readPendingTypes = readsPendingAttribute.readPendingTypes; } + + var queryWithAttribute = GetType().GetCustomAttribute(false); + if (queryWithAttribute != null) + { + queryWithTypes = queryWithAttribute.queryWithTypes; + } + + var queryWithoutAttribute = GetType().GetCustomAttribute(false); + if (queryWithoutAttribute != null) + { + queryWithoutTypes = queryWithoutAttribute.queryWithoutTypes; + } } public override bool Equals(object obj) @@ -612,16 +629,49 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } + protected IEnumerable QueryEntities() + { + foreach (var entity in entityQuery.FilterEntities(entityManager.Entities, componentUpdateManager.PendingBits, componentUpdateManager.ExistingBits)) + { + yield return entity; + } + } + /// /// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria. /// - protected EntitySetQueryBuilder EntityQueryBuilder() + internal void BuildEntityQuery() { - return new EntitySetQueryBuilder( - entityManager, - componentUpdateManager, - readPendingTypes, - readTypes + var withMask = BitSet1024Builder.Zeroes(); + foreach (var type in queryWithTypes) + { + withMask = withMask.Set(componentUpdateManager.TypeToIndex[type]); + } + + var withoutMask = BitSet1024Builder.Zeroes(); + foreach (var type in queryWithoutTypes) + { + withoutMask = withoutMask.Set(componentUpdateManager.TypeToIndex[type]); + } + + var pendingMask = BitSet1024Builder.Zeroes(); + foreach (var type in readPendingTypes) + { + pendingMask = pendingMask.Set(componentUpdateManager.TypeToIndex[type]); + } + + var existingMask = BitSet1024Builder.Zeroes(); + foreach (var type in readTypes) + { + existingMask = existingMask.Set(componentUpdateManager.TypeToIndex[type]); + } + + entityQuery = new EntitySetQuery( + withMask.And(pendingMask), + withMask.And(existingMask), + withoutMask.And(pendingMask), + withoutMask.And(existingMask), + withMask.Not() ); } } diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 60bd110..899ddcc 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -4,20 +4,16 @@ using Encompass.Collections; namespace Encompass { - public struct EntitySetQuery : IEnumerable + internal struct EntitySetQuery { - private EntityManager EntityManager { get; } - private ComponentUpdateManager ComponentUpdateManager { get; } private BitSet1024 WithPendingMask { get; } private BitSet1024 WithExistingMask { get; } private BitSet1024 WithoutPendingMask { get; } private BitSet1024 WithoutExistingMask { get; } private BitSet1024 NotWithMask { get; } - internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, BitSet1024 withPendingMask, BitSet1024 withExistingMask, BitSet1024 withoutPendingMask, BitSet1024 withoutExistingMask, BitSet1024 notWithMask) + internal EntitySetQuery(BitSet1024 withPendingMask, BitSet1024 withExistingMask, BitSet1024 withoutPendingMask, BitSet1024 withoutExistingMask, BitSet1024 notWithMask) { - EntityManager = entityManager; - ComponentUpdateManager = componentUpdateManager; WithPendingMask = withPendingMask; WithExistingMask = withExistingMask; WithoutPendingMask = withoutPendingMask; @@ -25,28 +21,23 @@ namespace Encompass NotWithMask = notWithMask; } - public IEnumerator GetEnumerator() + public IEnumerable FilterEntities(IEnumerable entities, ComponentBitSet pendingBitLookup, ComponentBitSet existingBitLookup) { - foreach (var entity in EntityManager.Entities) + foreach (var entity in entities) { - var pendingBits = ComponentUpdateManager.PendingBits(entity); - var existingBits = ComponentUpdateManager.ExistingBits(entity); + var pendingBits = pendingBitLookup.EntityBitArray(entity); + var existingBits = existingBitLookup.EntityBitArray(entity); var pending = WithPendingMask.And(pendingBits); var existing = WithExistingMask.And(existingBits); var withCheck = pending.Or(existing).Or(NotWithMask); - var pendingForbidden = WithoutPendingMask.And(pendingBits); - var existingForbidden = WithoutExistingMask.And(existingBits); + var pendingForbidden = WithoutPendingMask.And(pendingBits).Not(); + var existingForbidden = WithoutExistingMask.And(existingBits).Not(); var withoutCheck = pendingForbidden.And(existingForbidden); if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; } } } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } } } diff --git a/encompass-cs/EntitySetQueryBuilder.cs b/encompass-cs/EntitySetQueryBuilder.cs deleted file mode 100644 index 00452c5..0000000 --- a/encompass-cs/EntitySetQueryBuilder.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using Encompass.Collections; - -namespace Encompass -{ - /// - /// EntitySetQuery is used to efficiently obtain a set of Entities that have all required Components and do not have any forbidden Components. - /// - public class EntitySetQueryBuilder - { - internal EntitySetQueryBuilder(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, IEnumerable pendingTypes, IEnumerable existingTypes) - { - EntityManager = entityManager; - ComponentUpdateManager = componentUpdateManager; - WithMask = BitSet1024Builder.Zeroes(); - WithoutMask = BitSet1024Builder.Zeroes(); - - PendingMask = BitSet1024Builder.Zeroes(); - foreach (var type in pendingTypes) - { - PendingMask = PendingMask.Set(componentUpdateManager.TypeToIndex[type]); - } - - ExistingMask = BitSet1024Builder.Zeroes(); - foreach (var type in existingTypes) - { - ExistingMask = ExistingMask.Set(componentUpdateManager.TypeToIndex[type]); - } - } - - private EntityManager EntityManager { get; } - private ComponentUpdateManager ComponentUpdateManager { get; } - private BitSet1024 WithMask { get; set; } - private BitSet1024 WithoutMask { get; set; } - private BitSet1024 PendingMask { get; set; } - private BitSet1024 ExistingMask { get; set; } - - /// - /// Designates that the given component type is required. - /// - public EntitySetQueryBuilder With() where TComponent : struct, IComponent - { - WithMask = WithMask.Set(ComponentUpdateManager.TypeToIndex[typeof(TComponent)]); - return this; - } - - /// - /// Designates that the given component type is forbidden. - /// - public EntitySetQueryBuilder Without() where TComponent : struct, IComponent - { - WithoutMask = WithoutMask.Set(ComponentUpdateManager.TypeToIndex[typeof(TComponent)]); - return this; - } - - public EntitySetQuery Build() - { - return new EntitySetQuery( - EntityManager, - ComponentUpdateManager, - WithMask.And(PendingMask), - WithMask.And(ExistingMask), - WithoutMask.And(PendingMask), - WithoutMask.And(ExistingMask), - WithMask.Not() - ); - } - } -} diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 1e88a88..3c82f8c 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -173,9 +173,6 @@ namespace Encompass typeToReaders[receiveType].Add(engine); } - // System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(typeof(TEngine).GetRuntimeMethod("Update", new Type[] { typeof(double) }).MethodHandle); - // typeof(TEngine).GetMethod("Update", new Type[] { typeof(double) }).MethodHandle.GetFunctionPointer(); - return engine; } @@ -385,6 +382,7 @@ namespace Encompass foreach (var engine in engineGraph.TopologicalSort()) { engineOrder.Add(engine); + engine.BuildEntityQuery(); } var world = new World( diff --git a/test/EngineTest.cs b/test/EngineTest.cs index b36541d..1b36ce6 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1095,6 +1095,7 @@ namespace Tests struct MockComponentD : IComponent { } [Reads(typeof(MockComponent), typeof(MockComponentB))] + [QueryWith(typeof(MockComponent), typeof(MockComponentB))] class EntityQueryWithComponentsEngine : Engine { private List entities; @@ -1107,8 +1108,10 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - - entities.AddRange(QueryEntities().With().With()); + foreach (var entity in QueryEntities()) + { + entities.Add(entity); + } } } @@ -1140,6 +1143,7 @@ namespace Tests } [Reads(typeof(MockComponent))] + [QueryWithout(typeof(MockComponent))] class EntityQueryWithoutComponentsEngine : Engine { private List entities; @@ -1152,8 +1156,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - - entities.AddRange(QueryEntities().Without()); + entities.AddRange(QueryEntities()); } } @@ -1185,6 +1188,8 @@ namespace Tests } [Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))] + [QueryWith(typeof(MockComponent), typeof(MockComponentB))] + [QueryWithout(typeof(MockComponentD))] class EntityQueryWithandWithoutComponentsEngine : Engine { private List entities; @@ -1198,9 +1203,7 @@ namespace Tests { entities.Clear(); - entities.AddRange(QueryEntities().With() - .With() - .Without()); + entities.AddRange(QueryEntities()); } } @@ -1246,7 +1249,7 @@ namespace Tests { public override void Update(double dt) { - foreach (var entity in QueryEntities().With()) + foreach (var entity in ReadEntities()) { SetComponent(entity, new MockComponentB()); } @@ -1254,6 +1257,7 @@ namespace Tests } [ReadsPending(typeof(MockComponentB))] + [QueryWith(typeof(MockComponentB))] class EntityQueryWithPendingComponentsEngine : Engine { private List entities; @@ -1266,8 +1270,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - - entities.AddRange(QueryEntities().With()); + entities.AddRange(QueryEntities()); } } @@ -1293,6 +1296,7 @@ namespace Tests } [ReadsPending(typeof(MockComponentB))] + [QueryWithout(typeof(MockComponentB))] class EntityQueryWithoutPendingComponentsEngine : Engine { private List entities; @@ -1305,8 +1309,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - - entities.AddRange(QueryEntities().Without()); + entities.AddRange(QueryEntities()); } } @@ -1339,12 +1342,12 @@ namespace Tests { public override void Update(double dt) { - foreach (var entity in QueryEntities().With()) + foreach (var entity in ReadEntities()) { SetComponent(entity, new MockComponent()); } - foreach (var entity in QueryEntities().With()) + foreach (var entity in ReadEntities()) { SetComponent(entity, new MockComponent()); SetComponent(entity, new MockComponentB()); @@ -1353,6 +1356,8 @@ namespace Tests } [ReadsPending(typeof(MockComponent), typeof(MockComponentB))] + [QueryWith(typeof(MockComponent))] + [QueryWithout(typeof(MockComponentB))] class EntityQueryWithAndWithoutPendingComponentsEngine : Engine { private List entities; @@ -1366,7 +1371,7 @@ namespace Tests { entities.Clear(); - entities.AddRange(QueryEntities().With().Without()); + entities.AddRange(QueryEntities()); } } @@ -1400,7 +1405,7 @@ namespace Tests { public override void Update(double dt) { - foreach (var entity in QueryEntities().With()) + foreach (var entity in ReadEntities()) { SetComponent(entity, new MockComponentB()); } @@ -1409,6 +1414,7 @@ namespace Tests [ReadsPending(typeof(MockComponentB))] [Reads(typeof(MockComponent))] + [QueryWith(typeof(MockComponent), typeof(MockComponentB))] class EntityQueryWithPendingAndNonPendingComponents : Engine { private List entities; @@ -1421,8 +1427,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - - entities.AddRange(QueryEntities().With().With()); + entities.AddRange(QueryEntities()); } } From d6b96cd470cbbdcbe0b1c7ca038d008736c75897 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 22 Dec 2019 21:13:54 -0800 Subject: [PATCH 12/37] halve size of bitset for cpu compatibility --- .../{BitSet1024.cs => BitSet512.cs} | 100 +++++++++--------- encompass-cs/Collections/ComponentBitSet.cs | 8 +- encompass-cs/Collections/ComponentStore.cs | 2 +- encompass-cs/Engine.cs | 8 +- encompass-cs/EntitySetQuery.cs | 12 +-- test/BitSet1024Test.cs | 24 ++--- 6 files changed, 77 insertions(+), 77 deletions(-) rename encompass-cs/Collections/{BitSet1024.cs => BitSet512.cs} (55%) diff --git a/encompass-cs/Collections/BitSet1024.cs b/encompass-cs/Collections/BitSet512.cs similarity index 55% rename from encompass-cs/Collections/BitSet1024.cs rename to encompass-cs/Collections/BitSet512.cs index 239cd79..ea0593a 100644 --- a/encompass-cs/Collections/BitSet1024.cs +++ b/encompass-cs/Collections/BitSet512.cs @@ -2,28 +2,28 @@ namespace Encompass.Collections { - public static class BitSet1024Builder + public static class BitSet512Builder { - public static BitSet1024 Zeroes() + public static BitSet512 Zeroes() { - return new BitSet1024(Vector256Builder.Zeroes(), Vector256Builder.Zeroes(), Vector256Builder.Zeroes(), Vector256Builder.Zeroes()); + return new BitSet512(Vector128Builder.Zeroes(), Vector128Builder.Zeroes(), Vector128Builder.Zeroes(), Vector128Builder.Zeroes()); } - public static BitSet1024 Ones() + public static BitSet512 Ones() { - return new BitSet1024(Vector256Builder.Ones(), Vector256Builder.Ones(), Vector256Builder.Ones(), Vector256Builder.Ones()); + return new BitSet512(Vector128Builder.Ones(), Vector128Builder.Ones(), Vector128Builder.Ones(), Vector128Builder.Ones()); } } - public static class Vector256Builder + public static class Vector128Builder { - static readonly uint[] zeroes = new uint[8]; // max size of a Vector is 8 uints - static readonly uint[] ones = new uint[8] { uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue }; - static uint[] builderInts = new uint[8]; + static readonly uint[] zeroes = new uint[4]; // max size of a Vector is 8 uints + static readonly uint[] ones = new uint[4] { uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue }; + static uint[] builderInts = new uint[4]; private static void ResetBuilder() { - for (var i = 0; i < 8; i++) + for (var i = 0; i < 4; i++) { builderInts[i] = 0; } @@ -41,21 +41,21 @@ namespace Encompass.Collections public static Vector Build(int index) { - if (index > 255) { throw new System.ArgumentOutOfRangeException(nameof(index)); } + if (index > 127) { throw new System.ArgumentOutOfRangeException(nameof(index)); } ResetBuilder(); builderInts[index / 32] |= (uint)(1 << (index % 32)); return new Vector(builderInts); } } - public struct BitSet1024 + public struct BitSet512 { public Vector A { get; } public Vector B { get; } public Vector C { get; } public Vector D { get; } - internal BitSet1024(Vector a, Vector b, Vector c, Vector d) + internal BitSet512(Vector a, Vector b, Vector c, Vector d) { A = a; B = b; @@ -63,38 +63,38 @@ namespace Encompass.Collections D = d; } - public BitSet1024 And(BitSet1024 other) + public BitSet512 And(BitSet512 other) { - return new BitSet1024(A & other.A, B & other.B, C & other.C, D & other.D); + return new BitSet512(A & other.A, B & other.B, C & other.C, D & other.D); } - public BitSet1024 Or(BitSet1024 other) + public BitSet512 Or(BitSet512 other) { - return new BitSet1024(A | other.A, B | other.B, C | other.C, D | other.D); + return new BitSet512(A | other.A, B | other.B, C | other.C, D | other.D); } - public BitSet1024 Not() + public BitSet512 Not() { - return new BitSet1024(~A, ~B, ~C, ~D); + return new BitSet512(~A, ~B, ~C, ~D); } - public BitSet1024 Set(int index) + public BitSet512 Set(int index) { - if (index < 256) + if (index < 128) { - return new BitSet1024(A | Vector256Builder.Build(index % 256), B, C, D); + return new BitSet512(A | Vector128Builder.Build(index % 128), B, C, D); + } + else if (index < 256) + { + return new BitSet512(A, B | Vector128Builder.Build(index % 128), C, D); + } + else if (index < 384) + { + return new BitSet512(A, B, C | Vector128Builder.Build(index % 128), D); } else if (index < 512) { - return new BitSet1024(A, B | Vector256Builder.Build(index % 256), C, D); - } - else if (index < 768) - { - return new BitSet1024(A, B, C | Vector256Builder.Build(index % 256), D); - } - else if (index < 1024) - { - return new BitSet1024(A, B, C, D | Vector256Builder.Build(index % 256)); + return new BitSet512(A, B, C, D | Vector128Builder.Build(index % 128)); } else { @@ -102,23 +102,23 @@ namespace Encompass.Collections } } - public BitSet1024 UnSet(int index) + public BitSet512 UnSet(int index) { - if (index < 256) + if (index < 128) { - return new BitSet1024(A & ~Vector256Builder.Build(index % 256), B, C, D); + return new BitSet512(A & ~Vector128Builder.Build(index % 256), B, C, D); + } + else if (index < 256) + { + return new BitSet512(A, B & ~Vector128Builder.Build(index % 256), C, D); + } + else if (index < 384) + { + return new BitSet512(A, B, C & ~Vector128Builder.Build(index % 256), D); } else if (index < 512) { - return new BitSet1024(A, B & ~Vector256Builder.Build(index % 256), C, D); - } - else if (index < 768) - { - return new BitSet1024(A, B, C & ~Vector256Builder.Build(index % 256), D); - } - else if (index < 1024) - { - return new BitSet1024(A, B, C, D & ~Vector256Builder.Build(index % 256)); + return new BitSet512(A, B, C, D & ~Vector128Builder.Build(index % 256)); } else { @@ -128,20 +128,20 @@ namespace Encompass.Collections public bool Get(int index) { - var vectorIndex = index % 256; - if (index < 256) + var vectorIndex = index % 128; + if (index < 128) { return (A[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; } - else if (index < 512) + else if (index < 256) { return (B[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; } - else if (index < 768) + else if (index < 384) { return (C[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; } - else if (index < 1024) + else if (index < 512) { return (D[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; } @@ -153,7 +153,7 @@ namespace Encompass.Collections public bool AllTrue() { - for (var i = 0; i < 8; i++) + for (var i = 0; i < 4; i++) { if (A[i] != uint.MaxValue) { return false; } if (B[i] != uint.MaxValue) { return false; } @@ -165,7 +165,7 @@ namespace Encompass.Collections public bool AllFalse() { - for (var i = 0; i < 8; i++) + for (var i = 0; i < 4; i++) { if (A[i] != 0) { return false; } if (B[i] != 0) { return false; } diff --git a/encompass-cs/Collections/ComponentBitSet.cs b/encompass-cs/Collections/ComponentBitSet.cs index 63433dd..5dad445 100644 --- a/encompass-cs/Collections/ComponentBitSet.cs +++ b/encompass-cs/Collections/ComponentBitSet.cs @@ -7,7 +7,7 @@ namespace Encompass { internal class ComponentBitSet { - Dictionary entities = new Dictionary(); + Dictionary entities = new Dictionary(); Dictionary TypeToIndex { get; } public ComponentBitSet(Dictionary typeToIndex) @@ -27,7 +27,7 @@ namespace Encompass public void AddEntity(Entity entity) { - entities.Add(entity, BitSet1024Builder.Zeroes()); + entities.Add(entity, BitSet512Builder.Zeroes()); } public void Set(Entity entity) where TComponent : struct, IComponent @@ -52,9 +52,9 @@ namespace Encompass } } - public BitSet1024 EntityBitArray(Entity entity) + public BitSet512 EntityBitArray(Entity entity) { - return entities.ContainsKey(entity) ? entities[entity] : BitSet1024Builder.Zeroes(); + return entities.ContainsKey(entity) ? entities[entity] : BitSet512Builder.Zeroes(); } } } diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 5d3f09a..90280c5 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -52,7 +52,7 @@ namespace Encompass return Stores.ContainsKey(type) && Stores[type].Has(entity); } - public BitSet1024 EntityBitArray(Entity entity) + public BitSet512 EntityBitArray(Entity entity) { return ComponentBitSet.EntityBitArray(entity); } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 8e0a8ae..b61752d 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -642,25 +642,25 @@ namespace Encompass /// internal void BuildEntityQuery() { - var withMask = BitSet1024Builder.Zeroes(); + var withMask = BitSet512Builder.Zeroes(); foreach (var type in queryWithTypes) { withMask = withMask.Set(componentUpdateManager.TypeToIndex[type]); } - var withoutMask = BitSet1024Builder.Zeroes(); + var withoutMask = BitSet512Builder.Zeroes(); foreach (var type in queryWithoutTypes) { withoutMask = withoutMask.Set(componentUpdateManager.TypeToIndex[type]); } - var pendingMask = BitSet1024Builder.Zeroes(); + var pendingMask = BitSet512Builder.Zeroes(); foreach (var type in readPendingTypes) { pendingMask = pendingMask.Set(componentUpdateManager.TypeToIndex[type]); } - var existingMask = BitSet1024Builder.Zeroes(); + var existingMask = BitSet512Builder.Zeroes(); foreach (var type in readTypes) { existingMask = existingMask.Set(componentUpdateManager.TypeToIndex[type]); diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 899ddcc..219c712 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -6,13 +6,13 @@ namespace Encompass { internal struct EntitySetQuery { - private BitSet1024 WithPendingMask { get; } - private BitSet1024 WithExistingMask { get; } - private BitSet1024 WithoutPendingMask { get; } - private BitSet1024 WithoutExistingMask { get; } - private BitSet1024 NotWithMask { get; } + private BitSet512 WithPendingMask { get; } + private BitSet512 WithExistingMask { get; } + private BitSet512 WithoutPendingMask { get; } + private BitSet512 WithoutExistingMask { get; } + private BitSet512 NotWithMask { get; } - internal EntitySetQuery(BitSet1024 withPendingMask, BitSet1024 withExistingMask, BitSet1024 withoutPendingMask, BitSet1024 withoutExistingMask, BitSet1024 notWithMask) + internal EntitySetQuery(BitSet512 withPendingMask, BitSet512 withExistingMask, BitSet512 withoutPendingMask, BitSet512 withoutExistingMask, BitSet512 notWithMask) { WithPendingMask = withPendingMask; WithExistingMask = withExistingMask; diff --git a/test/BitSet1024Test.cs b/test/BitSet1024Test.cs index 81e1a4b..9eeac8a 100644 --- a/test/BitSet1024Test.cs +++ b/test/BitSet1024Test.cs @@ -4,50 +4,50 @@ using NUnit.Framework; namespace Tests { - public class BitSet1024Test + public class BitSet512Test { [Test] public void Zeroes() { - var bitSet = BitSet1024Builder.Zeroes(); + var bitSet = BitSet512Builder.Zeroes(); bitSet.AllFalse().Should().BeTrue(); } [Test] public void Ones() { - var bitSet = BitSet1024Builder.Ones(); + var bitSet = BitSet512Builder.Ones(); bitSet.AllTrue().Should().BeTrue(); } [Test] public void Set() { - var bitSet = BitSet1024Builder.Zeroes().Set(5); + var bitSet = BitSet512Builder.Zeroes().Set(5); bitSet.AllFalse().Should().BeFalse(); - bitSet = BitSet1024Builder.Zeroes().Set(278); + bitSet = BitSet512Builder.Zeroes().Set(132); bitSet.AllFalse().Should().BeFalse(); - bitSet = BitSet1024Builder.Zeroes().Set(569); + bitSet = BitSet512Builder.Zeroes().Set(268); bitSet.AllFalse().Should().BeFalse(); - bitSet = BitSet1024Builder.Zeroes().Set(1023); + bitSet = BitSet512Builder.Zeroes().Set(450); bitSet.AllFalse().Should().BeFalse(); } [Test] public void UnSet() { - var bitSet = BitSet1024Builder.Ones().UnSet(562); - bitSet.Get(562).Should().BeFalse(); - bitSet.Set(562).AllTrue().Should().BeTrue(); + var bitSet = BitSet512Builder.Ones().UnSet(285); + bitSet.Get(285).Should().BeFalse(); + bitSet.Set(285).AllTrue().Should().BeTrue(); } [Test] public void Get() { - var bitSet = BitSet1024Builder.Zeroes().Set(359); + var bitSet = BitSet512Builder.Zeroes().Set(359); bitSet.Get(359).Should().BeTrue(); bitSet.UnSet(359).AllFalse().Should().BeTrue(); } @@ -55,7 +55,7 @@ namespace Tests [Test] public void Not() { - var bitSet = BitSet1024Builder.Ones().Not(); + var bitSet = BitSet512Builder.Ones().Not(); bitSet.AllFalse().Should().BeTrue(); } } From 51a248156e83a5ed5959ea652438ce98d1fddf41 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 22 Dec 2019 22:02:17 -0800 Subject: [PATCH 13/37] fix bug on BitSet UnSet --- encompass-cs/Collections/BitSet512.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/encompass-cs/Collections/BitSet512.cs b/encompass-cs/Collections/BitSet512.cs index ea0593a..beef1e8 100644 --- a/encompass-cs/Collections/BitSet512.cs +++ b/encompass-cs/Collections/BitSet512.cs @@ -106,19 +106,19 @@ namespace Encompass.Collections { if (index < 128) { - return new BitSet512(A & ~Vector128Builder.Build(index % 256), B, C, D); + return new BitSet512(A & ~Vector128Builder.Build(index % 128), B, C, D); } else if (index < 256) { - return new BitSet512(A, B & ~Vector128Builder.Build(index % 256), C, D); + return new BitSet512(A, B & ~Vector128Builder.Build(index % 128), C, D); } else if (index < 384) { - return new BitSet512(A, B, C & ~Vector128Builder.Build(index % 256), D); + return new BitSet512(A, B, C & ~Vector128Builder.Build(index % 128), D); } else if (index < 512) { - return new BitSet512(A, B, C, D & ~Vector128Builder.Build(index % 256)); + return new BitSet512(A, B, C, D & ~Vector128Builder.Build(index % 128)); } else { From b1c09a20a07522f1e27bb12c97601ab62f007087 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 22 Dec 2019 22:34:58 -0800 Subject: [PATCH 14/37] change bitset to be cross platform --- encompass-cs/Collections/BitSet.cs | 182 ++++++++++++++++++++ encompass-cs/Collections/BitSet512.cs | 178 ------------------- encompass-cs/Collections/ComponentBitSet.cs | 8 +- encompass-cs/Collections/ComponentStore.cs | 2 +- encompass-cs/Engine.cs | 8 +- encompass-cs/EntitySetQuery.cs | 12 +- test/{BitSet1024Test.cs => BitSetTest.cs} | 20 +-- 7 files changed, 207 insertions(+), 203 deletions(-) create mode 100644 encompass-cs/Collections/BitSet.cs delete mode 100644 encompass-cs/Collections/BitSet512.cs rename test/{BitSet1024Test.cs => BitSetTest.cs} (65%) diff --git a/encompass-cs/Collections/BitSet.cs b/encompass-cs/Collections/BitSet.cs new file mode 100644 index 0000000..abe9960 --- /dev/null +++ b/encompass-cs/Collections/BitSet.cs @@ -0,0 +1,182 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace Encompass.Collections +{ + public static class BitSetBuilder + { + public static BitSet Zeroes() + { + return new BitSet(VectorBuilder.Zeroes(), VectorBuilder.Zeroes(), VectorBuilder.Zeroes(), VectorBuilder.Zeroes()); + } + + public static BitSet Ones() + { + return new BitSet(VectorBuilder.Ones(), VectorBuilder.Ones(), VectorBuilder.Ones(), VectorBuilder.Ones()); + } + } + + public static class VectorBuilder + { + static readonly uint[] zeroes = new uint[Vector.Count]; // max size of a Vector is 8 uints + static readonly uint[] ones = Enumerable.Repeat(uint.MaxValue, Vector.Count).ToArray(); + static uint[] builderInts = new uint[Vector.Count]; + + private static void ResetBuilder() + { + for (var i = 0; i < Vector.Count; i++) + { + builderInts[i] = 0; + } + } + + public static Vector Zeroes() + { + return new Vector(zeroes); + } + + public static Vector Ones() + { + return new Vector(ones); + } + + public static Vector Build(int index) + { + if (index > Vector.Count * 32) { throw new System.ArgumentOutOfRangeException(nameof(index)); } + ResetBuilder(); + builderInts[index / 32] |= (uint)(1 << (index % 32)); + return new Vector(builderInts); + } + } + + public struct BitSet + { + public static int VectorLength { get { return Vector.Count * 32; } } + + public Vector A { get; } + public Vector B { get; } + public Vector C { get; } + public Vector D { get; } + + internal BitSet(Vector a, Vector b, Vector c, Vector d) + { + A = a; + B = b; + C = c; + D = d; + } + + public BitSet And(BitSet other) + { + return new BitSet(A & other.A, B & other.B, C & other.C, D & other.D); + } + + public BitSet Or(BitSet other) + { + return new BitSet(A | other.A, B | other.B, C | other.C, D | other.D); + } + + public BitSet Not() + { + return new BitSet(~A, ~B, ~C, ~D); + } + + public BitSet Set(int index) + { + if (index < VectorLength) + { + return new BitSet(A | VectorBuilder.Build(index % VectorLength), B, C, D); + } + else if (index < VectorLength * 2) + { + return new BitSet(A, B | VectorBuilder.Build(index % VectorLength), C, D); + } + else if (index < VectorLength * 3) + { + return new BitSet(A, B, C | VectorBuilder.Build(index % VectorLength), D); + } + else if (index < VectorLength * 4) + { + return new BitSet(A, B, C, D | VectorBuilder.Build(index % VectorLength)); + } + else + { + throw new System.ArgumentOutOfRangeException(nameof(index)); + } + } + + public BitSet UnSet(int index) + { + if (index < VectorLength) + { + return new BitSet(A & ~VectorBuilder.Build(index % VectorLength), B, C, D); + } + else if (index < VectorLength * 2) + { + return new BitSet(A, B & ~VectorBuilder.Build(index % VectorLength), C, D); + } + else if (index < VectorLength * 3) + { + return new BitSet(A, B, C & ~VectorBuilder.Build(index % VectorLength), D); + } + else if (index < VectorLength * 4) + { + return new BitSet(A, B, C, D & ~VectorBuilder.Build(index % VectorLength)); + } + else + { + throw new System.ArgumentOutOfRangeException(nameof(index)); + } + } + + public bool Get(int index) + { + var vectorIndex = index % VectorLength; + if (index < VectorLength) + { + return (A[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else if (index < VectorLength * 2) + { + return (B[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else if (index < VectorLength * 3) + { + return (C[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else if (index < VectorLength * 4) + { + return (D[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; + } + else + { + throw new System.ArgumentOutOfRangeException(nameof(index)); + } + } + + public bool AllTrue() + { + for (var i = 0; i < Vector.Count; i++) + { + if (A[i] != uint.MaxValue) { return false; } + if (B[i] != uint.MaxValue) { return false; } + if (C[i] != uint.MaxValue) { return false; } + if (D[i] != uint.MaxValue) { return false; } + } + return true; + } + + public bool AllFalse() + { + for (var i = 0; i < Vector.Count; i++) + { + if (A[i] != 0) { return false; } + if (B[i] != 0) { return false; } + if (C[i] != 0) { return false; } + if (D[i] != 0) { return false; } + } + return true; + } + } +} diff --git a/encompass-cs/Collections/BitSet512.cs b/encompass-cs/Collections/BitSet512.cs deleted file mode 100644 index beef1e8..0000000 --- a/encompass-cs/Collections/BitSet512.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System.Numerics; - -namespace Encompass.Collections -{ - public static class BitSet512Builder - { - public static BitSet512 Zeroes() - { - return new BitSet512(Vector128Builder.Zeroes(), Vector128Builder.Zeroes(), Vector128Builder.Zeroes(), Vector128Builder.Zeroes()); - } - - public static BitSet512 Ones() - { - return new BitSet512(Vector128Builder.Ones(), Vector128Builder.Ones(), Vector128Builder.Ones(), Vector128Builder.Ones()); - } - } - - public static class Vector128Builder - { - static readonly uint[] zeroes = new uint[4]; // max size of a Vector is 8 uints - static readonly uint[] ones = new uint[4] { uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue }; - static uint[] builderInts = new uint[4]; - - private static void ResetBuilder() - { - for (var i = 0; i < 4; i++) - { - builderInts[i] = 0; - } - } - - public static Vector Zeroes() - { - return new Vector(zeroes); - } - - public static Vector Ones() - { - return new Vector(ones); - } - - public static Vector Build(int index) - { - if (index > 127) { throw new System.ArgumentOutOfRangeException(nameof(index)); } - ResetBuilder(); - builderInts[index / 32] |= (uint)(1 << (index % 32)); - return new Vector(builderInts); - } - } - - public struct BitSet512 - { - public Vector A { get; } - public Vector B { get; } - public Vector C { get; } - public Vector D { get; } - - internal BitSet512(Vector a, Vector b, Vector c, Vector d) - { - A = a; - B = b; - C = c; - D = d; - } - - public BitSet512 And(BitSet512 other) - { - return new BitSet512(A & other.A, B & other.B, C & other.C, D & other.D); - } - - public BitSet512 Or(BitSet512 other) - { - return new BitSet512(A | other.A, B | other.B, C | other.C, D | other.D); - } - - public BitSet512 Not() - { - return new BitSet512(~A, ~B, ~C, ~D); - } - - public BitSet512 Set(int index) - { - if (index < 128) - { - return new BitSet512(A | Vector128Builder.Build(index % 128), B, C, D); - } - else if (index < 256) - { - return new BitSet512(A, B | Vector128Builder.Build(index % 128), C, D); - } - else if (index < 384) - { - return new BitSet512(A, B, C | Vector128Builder.Build(index % 128), D); - } - else if (index < 512) - { - return new BitSet512(A, B, C, D | Vector128Builder.Build(index % 128)); - } - else - { - throw new System.ArgumentOutOfRangeException(nameof(index)); - } - } - - public BitSet512 UnSet(int index) - { - if (index < 128) - { - return new BitSet512(A & ~Vector128Builder.Build(index % 128), B, C, D); - } - else if (index < 256) - { - return new BitSet512(A, B & ~Vector128Builder.Build(index % 128), C, D); - } - else if (index < 384) - { - return new BitSet512(A, B, C & ~Vector128Builder.Build(index % 128), D); - } - else if (index < 512) - { - return new BitSet512(A, B, C, D & ~Vector128Builder.Build(index % 128)); - } - else - { - throw new System.ArgumentOutOfRangeException(nameof(index)); - } - } - - public bool Get(int index) - { - var vectorIndex = index % 128; - if (index < 128) - { - return (A[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else if (index < 256) - { - return (B[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else if (index < 384) - { - return (C[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else if (index < 512) - { - return (D[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else - { - throw new System.ArgumentOutOfRangeException(nameof(index)); - } - } - - public bool AllTrue() - { - for (var i = 0; i < 4; i++) - { - if (A[i] != uint.MaxValue) { return false; } - if (B[i] != uint.MaxValue) { return false; } - if (C[i] != uint.MaxValue) { return false; } - if (D[i] != uint.MaxValue) { return false; } - } - return true; - } - - public bool AllFalse() - { - for (var i = 0; i < 4; i++) - { - if (A[i] != 0) { return false; } - if (B[i] != 0) { return false; } - if (C[i] != 0) { return false; } - if (D[i] != 0) { return false; } - } - return true; - } - } -} diff --git a/encompass-cs/Collections/ComponentBitSet.cs b/encompass-cs/Collections/ComponentBitSet.cs index 5dad445..d1ab784 100644 --- a/encompass-cs/Collections/ComponentBitSet.cs +++ b/encompass-cs/Collections/ComponentBitSet.cs @@ -7,7 +7,7 @@ namespace Encompass { internal class ComponentBitSet { - Dictionary entities = new Dictionary(); + Dictionary entities = new Dictionary(); Dictionary TypeToIndex { get; } public ComponentBitSet(Dictionary typeToIndex) @@ -27,7 +27,7 @@ namespace Encompass public void AddEntity(Entity entity) { - entities.Add(entity, BitSet512Builder.Zeroes()); + entities.Add(entity, BitSetBuilder.Zeroes()); } public void Set(Entity entity) where TComponent : struct, IComponent @@ -52,9 +52,9 @@ namespace Encompass } } - public BitSet512 EntityBitArray(Entity entity) + public BitSet EntityBitArray(Entity entity) { - return entities.ContainsKey(entity) ? entities[entity] : BitSet512Builder.Zeroes(); + return entities.ContainsKey(entity) ? entities[entity] : BitSetBuilder.Zeroes(); } } } diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 90280c5..6fe1af3 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -52,7 +52,7 @@ namespace Encompass return Stores.ContainsKey(type) && Stores[type].Has(entity); } - public BitSet512 EntityBitArray(Entity entity) + public BitSet EntityBitArray(Entity entity) { return ComponentBitSet.EntityBitArray(entity); } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index b61752d..acd0e66 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -642,25 +642,25 @@ namespace Encompass /// internal void BuildEntityQuery() { - var withMask = BitSet512Builder.Zeroes(); + var withMask = BitSetBuilder.Zeroes(); foreach (var type in queryWithTypes) { withMask = withMask.Set(componentUpdateManager.TypeToIndex[type]); } - var withoutMask = BitSet512Builder.Zeroes(); + var withoutMask = BitSetBuilder.Zeroes(); foreach (var type in queryWithoutTypes) { withoutMask = withoutMask.Set(componentUpdateManager.TypeToIndex[type]); } - var pendingMask = BitSet512Builder.Zeroes(); + var pendingMask = BitSetBuilder.Zeroes(); foreach (var type in readPendingTypes) { pendingMask = pendingMask.Set(componentUpdateManager.TypeToIndex[type]); } - var existingMask = BitSet512Builder.Zeroes(); + var existingMask = BitSetBuilder.Zeroes(); foreach (var type in readTypes) { existingMask = existingMask.Set(componentUpdateManager.TypeToIndex[type]); diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 219c712..b564212 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -6,13 +6,13 @@ namespace Encompass { internal struct EntitySetQuery { - private BitSet512 WithPendingMask { get; } - private BitSet512 WithExistingMask { get; } - private BitSet512 WithoutPendingMask { get; } - private BitSet512 WithoutExistingMask { get; } - private BitSet512 NotWithMask { get; } + private BitSet WithPendingMask { get; } + private BitSet WithExistingMask { get; } + private BitSet WithoutPendingMask { get; } + private BitSet WithoutExistingMask { get; } + private BitSet NotWithMask { get; } - internal EntitySetQuery(BitSet512 withPendingMask, BitSet512 withExistingMask, BitSet512 withoutPendingMask, BitSet512 withoutExistingMask, BitSet512 notWithMask) + internal EntitySetQuery(BitSet withPendingMask, BitSet withExistingMask, BitSet withoutPendingMask, BitSet withoutExistingMask, BitSet notWithMask) { WithPendingMask = withPendingMask; WithExistingMask = withExistingMask; diff --git a/test/BitSet1024Test.cs b/test/BitSetTest.cs similarity index 65% rename from test/BitSet1024Test.cs rename to test/BitSetTest.cs index 9eeac8a..dfcfb6d 100644 --- a/test/BitSet1024Test.cs +++ b/test/BitSetTest.cs @@ -4,42 +4,42 @@ using NUnit.Framework; namespace Tests { - public class BitSet512Test + public class BitSetTest { [Test] public void Zeroes() { - var bitSet = BitSet512Builder.Zeroes(); + var bitSet = BitSetBuilder.Zeroes(); bitSet.AllFalse().Should().BeTrue(); } [Test] public void Ones() { - var bitSet = BitSet512Builder.Ones(); + var bitSet = BitSetBuilder.Ones(); bitSet.AllTrue().Should().BeTrue(); } [Test] public void Set() { - var bitSet = BitSet512Builder.Zeroes().Set(5); + var bitSet = BitSetBuilder.Zeroes().Set(5); bitSet.AllFalse().Should().BeFalse(); - bitSet = BitSet512Builder.Zeroes().Set(132); + bitSet = BitSetBuilder.Zeroes().Set(132); bitSet.AllFalse().Should().BeFalse(); - bitSet = BitSet512Builder.Zeroes().Set(268); + bitSet = BitSetBuilder.Zeroes().Set(268); bitSet.AllFalse().Should().BeFalse(); - bitSet = BitSet512Builder.Zeroes().Set(450); + bitSet = BitSetBuilder.Zeroes().Set(450); bitSet.AllFalse().Should().BeFalse(); } [Test] public void UnSet() { - var bitSet = BitSet512Builder.Ones().UnSet(285); + var bitSet = BitSetBuilder.Ones().UnSet(285); bitSet.Get(285).Should().BeFalse(); bitSet.Set(285).AllTrue().Should().BeTrue(); } @@ -47,7 +47,7 @@ namespace Tests [Test] public void Get() { - var bitSet = BitSet512Builder.Zeroes().Set(359); + var bitSet = BitSetBuilder.Zeroes().Set(359); bitSet.Get(359).Should().BeTrue(); bitSet.UnSet(359).AllFalse().Should().BeTrue(); } @@ -55,7 +55,7 @@ namespace Tests [Test] public void Not() { - var bitSet = BitSet512Builder.Ones().Not(); + var bitSet = BitSetBuilder.Ones().Not(); bitSet.AllFalse().Should().BeTrue(); } } From 7885c2e0f49855b2c799e036b44640d040977ae3 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Mon, 23 Dec 2019 18:01:49 -0800 Subject: [PATCH 15/37] component removes are treated as priority writes --- encompass-cs/Collections/ComponentStore.cs | 25 ++++++++++---- .../Collections/TypedComponentStore.cs | 28 +++++++++++---- encompass-cs/ComponentManager.cs | 20 +++++++++-- encompass-cs/ComponentUpdateManager.cs | 34 +++++++++---------- encompass-cs/DrawLayerManager.cs | 2 +- encompass-cs/Engine.cs | 19 ++++++++--- test/ComponentTest.cs | 1 + test/EngineTest.cs | 14 ++++++-- 8 files changed, 101 insertions(+), 42 deletions(-) diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 90280c5..1d5287d 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -70,21 +70,34 @@ namespace Encompass public bool Set(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { - ComponentBitSet.Set(entity); - return Lookup().Set(entity, component, priority); + if (Lookup().Set(entity, component, priority)) + { + ComponentBitSet.Set(entity); + return true; + } + return false; } - public void Remove(Entity entity) where TComponent : struct, IComponent + public bool Remove(Entity entity, int priority) where TComponent : struct, IComponent { - ComponentBitSet.RemoveComponent(entity); - Lookup().Remove(entity); + if (Lookup().Remove(entity, priority)) + { + ComponentBitSet.RemoveComponent(entity); + return true; + } + return false; + } + + public void ForceRemove(Entity entity) where TComponent : struct, IComponent + { + Lookup().ForceRemove(entity); } public void Remove(Entity entity) { foreach (var entry in Stores.Values) { - entry.Remove(entity); + entry.ForceRemove(entity); } ComponentBitSet.RemoveEntity(entity); } diff --git a/encompass-cs/Collections/TypedComponentStore.cs b/encompass-cs/Collections/TypedComponentStore.cs index 63409a2..54f1298 100644 --- a/encompass-cs/Collections/TypedComponentStore.cs +++ b/encompass-cs/Collections/TypedComponentStore.cs @@ -8,7 +8,8 @@ namespace Encompass public abstract int Count { get; } public abstract IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped(); public abstract bool Has(Entity entity); - public abstract void Remove(Entity entity); + public abstract bool Remove(Entity entity, int priority); + public abstract void ForceRemove(Entity entity); public abstract void Clear(); } @@ -40,6 +41,25 @@ namespace Encompass return false; } + public override bool Remove(Entity entity, int priority) + { + if (!priorities.ContainsKey(entity) || priority < priorities[entity]) + { + priorities[entity] = priority; + store.Remove(entity); + priorities.Remove(entity); + return true; + } + + return false; + } + + public override void ForceRemove(Entity entity) + { + store.Remove(entity); + priorities.Remove(entity); + } + public override bool Has(Entity entity) { return store.ContainsKey(entity); @@ -66,11 +86,5 @@ namespace Encompass yield return (kvp.Key, typeof(TComponent), (IComponent)kvp.Value); } } - - public override void Remove(Entity entity) - { - store.Remove(entity); - priorities.Remove(entity); - } } } diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 30d024b..36f6cd8 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -92,10 +92,24 @@ namespace Encompass entitiesMarkedForRemoval.Clear(); } - public void Remove(Entity entity) where TComponent : struct, IComponent + public bool RemovePending(Entity entity, int priority) where TComponent : struct, IComponent { - componentUpdateManager.Remove(entity); - drawLayerManager.UnRegisterComponentWithLayer(entity); + if (componentUpdateManager.RemovePending(entity, priority)) + { + drawLayerManager.UnRegisterComponentWithLayer(entity); + return true; + } + return false; + } + + public bool Remove(Entity entity, int priority) where TComponent : struct, IComponent + { + if (componentUpdateManager.Remove(entity, priority)) + { + drawLayerManager.UnRegisterComponentWithLayer(entity); + return true; + } + return false; } } } diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index d0ee527..aa195e1 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -1,6 +1,4 @@ -using Encompass.Collections; -using System; -using System.Collections; +using System; using System.Collections.Generic; using System.Linq; @@ -8,7 +6,6 @@ namespace Encompass { internal class ComponentUpdateManager { - private readonly ComponentStore existingAndPendingComponentStore; private readonly ComponentStore existingComponentStore; private readonly ComponentStore pendingComponentStore; private readonly Dictionary> typeToEntityToPendingComponentPriority = new Dictionary>(128); @@ -18,7 +15,6 @@ namespace Encompass public ComponentUpdateManager(Dictionary typeToIndex) { - existingAndPendingComponentStore = new ComponentStore(typeToIndex); existingComponentStore = new ComponentStore(typeToIndex); pendingComponentStore = new ComponentStore(typeToIndex); UpToDateComponentStore = new ComponentStore(typeToIndex); @@ -27,7 +23,6 @@ namespace Encompass public void RegisterComponentType() where TComponent : struct, IComponent { - existingAndPendingComponentStore.RegisterComponentType(); existingComponentStore.RegisterComponentType(); pendingComponentStore.RegisterComponentType(); UpToDateComponentStore.RegisterComponentType(); @@ -35,7 +30,6 @@ namespace Encompass public void FinishRegistering() { - existingAndPendingComponentStore.FinishRegistering(); existingComponentStore.FinishRegistering(); pendingComponentStore.FinishRegistering(); UpToDateComponentStore.FinishRegistering(); @@ -43,7 +37,6 @@ namespace Encompass internal void Clear() { - existingAndPendingComponentStore.ClearAll(); existingComponentStore.ClearAll(); pendingComponentStore.ClearAll(); UpToDateComponentStore.ClearAll(); @@ -77,9 +70,19 @@ namespace Encompass return false; } + internal bool RemovePending(Entity entity, int priority) where TComponent : struct, IComponent + { + UpToDateComponentStore.Remove(entity, priority); + return pendingComponentStore.Remove(entity, priority); + } + + internal bool Remove(Entity entity, int priority) where TComponent : struct, IComponent + { + return UpToDateComponentStore.Remove(entity, priority); + } + private void RegisterExistingOrPendingComponentMessage(Entity entity, TComponent component) where TComponent : struct, IComponent { - existingAndPendingComponentStore.Set(entity, component); UpToDateComponentStore.Set(entity, component); } @@ -92,7 +95,7 @@ namespace Encompass internal IEnumerable<(TComponent, Entity)> ReadExistingAndPendingComponentsByType() where TComponent : struct, IComponent { - return existingAndPendingComponentStore.All(); + return UpToDateComponentStore.All(); } internal IEnumerable<(TComponent, Entity)> ReadExistingComponentsByType() where TComponent : struct, IComponent @@ -135,7 +138,7 @@ namespace Encompass internal bool SomeExistingOrPendingComponent() where TComponent : struct, IComponent { - return existingAndPendingComponentStore.Any(); + return UpToDateComponentStore.Any(); } internal bool SomeExistingComponent() where TComponent : struct, IComponent @@ -164,12 +167,12 @@ namespace Encompass internal bool HasExistingOrPendingComponent(Entity entity) where TComponent : struct, IComponent { - return existingAndPendingComponentStore.Has(entity); + return UpToDateComponentStore.Has(entity); } internal bool HasExistingOrPendingComponent(Entity entity, Type type) { - return existingAndPendingComponentStore.Has(type, entity); + return UpToDateComponentStore.Has(type, entity); } internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent @@ -192,11 +195,6 @@ namespace Encompass return pendingComponentStore.Has(type, entity); } - internal void Remove(Entity entity) where TComponent : struct, IComponent - { - UpToDateComponentStore.Remove(entity); - } - internal ComponentBitSet PendingBits { get { return pendingComponentStore.ComponentBitSet; } } internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } } diff --git a/encompass-cs/DrawLayerManager.cs b/encompass-cs/DrawLayerManager.cs index 65cde48..b2bc047 100644 --- a/encompass-cs/DrawLayerManager.cs +++ b/encompass-cs/DrawLayerManager.cs @@ -96,7 +96,7 @@ namespace Encompass if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) { var layer = typeToEntityToLayer[typeof(TComponent)][entity]; - layerIndexToComponentStore[layer].Remove(entity); + layerIndexToComponentStore[layer].ForceRemove(entity); } typeToEntityToLayer[typeof(TComponent)].Remove(entity); } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index b61752d..5157fa5 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -561,12 +561,23 @@ namespace Encompass /// Note that the Engine must Read the Component type that is being removed. /// If a Component with the specified type does not exist on the Entity, returns false and does not mutate the Entity. /// - protected bool RemoveComponent(Entity entity) where TComponent : struct, IComponent + protected void RemoveComponent(Entity entity) where TComponent : struct, IComponent { - if (!HasComponent(entity)) { return false; } + var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : defaultWritePriority; - componentManager.Remove(entity); - return true; + if (!writeTypes.Contains(typeof(TComponent))) + { + throw new IllegalWriteException("Engine {0} tried to remove undeclared Component {1}. Declare with Writes attribute.", GetType().Name, typeof(TComponent).Name); + } + + if (writePendingTypes.Contains(typeof(TComponent))) + { + componentManager.RemovePending(entity, priority); + } + else + { + componentManager.Remove(entity, priority); + } } /// diff --git a/test/ComponentTest.cs b/test/ComponentTest.cs index febbd0b..c7c1c78 100644 --- a/test/ComponentTest.cs +++ b/test/ComponentTest.cs @@ -369,6 +369,7 @@ namespace Tests [Reads(typeof(MockComponent))] [Receives(typeof(RemoveComponentTestMessage))] + [Writes(typeof(MockComponent))] class RemoveComponentTestEngine : Engine { public override void Update(double dt) diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 1b36ce6..3fc6a9a 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -593,7 +593,8 @@ namespace Tests Assert.That(results, Does.Not.Contain((mockComponent, entity))); } - [Reads(typeof(DestroyerComponent), typeof(MockComponent))] + [Reads(typeof(DestroyerComponent))] + [Writes(typeof(MockComponent))] class DestroyAndAddComponentEngine : Engine { public override void Update(double dt) @@ -665,6 +666,7 @@ namespace Tests } [Reads(typeof(MockComponent))] + [Writes(typeof(MockComponent))] class DelayedMessageEngine : Engine { public override void Update(double dt) @@ -711,6 +713,7 @@ namespace Tests } [Reads(typeof(MockComponent))] + [Writes(typeof(MockComponent))] class DelayedMessageIgnoringTimeDilationEngine : Engine { public override void Update(double dt) @@ -754,7 +757,7 @@ namespace Tests [Receives(typeof(MockMessage))] [WritesPending(typeof(MockComponent))] - [Writes(typeof(MockComponent))] + [Writes(typeof(MockComponent), 1)] class ActivateComponentEngine : Engine { public override void Update(double dt) @@ -768,6 +771,7 @@ namespace Tests } [ReadsPending(typeof(MockComponent))] + [Writes(typeof(MockComponent), 0)] class RemoveComponentEngine : Engine { public override void Update(double dt) @@ -780,17 +784,20 @@ namespace Tests } [Test] - public void EngineAddAndRemoveComponentSameFrame() + public void EngineAddAndRemoveComponentSameFrameWithRemovePriority() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new ActivateComponentEngine()); worldBuilder.AddEngine(new RemoveComponentEngine()); + worldBuilder.AddEngine(new ReadComponentsTestEngine()); worldBuilder.SendMessage(new MockMessage { }); var world = worldBuilder.Build(); Assert.DoesNotThrow(() => world.Update(0.01)); + world.Update(0.01); // update again for the read + resultComponents.Should().BeEmpty(); } struct DestroyComponentMessage : IMessage { public Entity entity; } @@ -926,6 +933,7 @@ namespace Tests } [Reads(typeof(MockComponent))] + [Writes(typeof(MockComponent))] class RemoveComponentByTypeEngine : Engine { public override void Update(double dt) From 47e1072c8dff3a47d922090ca0326eb6daa6f970 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Mon, 23 Dec 2019 18:45:49 -0800 Subject: [PATCH 16/37] internal storage using int ID instead of entity to avoid hash lookups --- encompass-cs/Collections/ComponentBitSet.cs | 33 ++++----- encompass-cs/Collections/ComponentStore.cs | 53 +++++++-------- .../Collections/TypedComponentStore.cs | 67 ++++++++++--------- encompass-cs/ComponentManager.cs | 29 ++++---- encompass-cs/ComponentUpdateManager.cs | 51 ++++++-------- encompass-cs/DrawLayerManager.cs | 38 +++++------ encompass-cs/Engine.cs | 21 ++++-- encompass-cs/EntityManager.cs | 10 +++ encompass-cs/EntitySetQuery.cs | 4 +- encompass-cs/RenderManager.cs | 8 ++- encompass-cs/Renderer.cs | 5 +- encompass-cs/UberEngine.cs | 15 +++-- encompass-cs/UberRenderer.cs | 10 ++- encompass-cs/WorldBuilder.cs | 22 ++---- 14 files changed, 178 insertions(+), 188 deletions(-) diff --git a/encompass-cs/Collections/ComponentBitSet.cs b/encompass-cs/Collections/ComponentBitSet.cs index 5dad445..6b0de92 100644 --- a/encompass-cs/Collections/ComponentBitSet.cs +++ b/encompass-cs/Collections/ComponentBitSet.cs @@ -7,7 +7,7 @@ namespace Encompass { internal class ComponentBitSet { - Dictionary entities = new Dictionary(); + Dictionary entities = new Dictionary(); Dictionary TypeToIndex { get; } public ComponentBitSet(Dictionary typeToIndex) @@ -15,46 +15,41 @@ namespace Encompass TypeToIndex = typeToIndex; } - public void FinishRegistering() - { - - } - public void Clear() { entities.Clear(); } - public void AddEntity(Entity entity) + public void AddEntity(int entityID) { - entities.Add(entity, BitSet512Builder.Zeroes()); + entities.Add(entityID, BitSet512Builder.Zeroes()); } - public void Set(Entity entity) where TComponent : struct, IComponent + public void Set(int entityID) where TComponent : struct, IComponent { - if (!entities.ContainsKey(entity)) { AddEntity(entity); } - entities[entity] = entities[entity].Set(TypeToIndex[typeof(TComponent)]); + if (!entities.ContainsKey(entityID)) { AddEntity(entityID); } + entities[entityID] = entities[entityID].Set(TypeToIndex[typeof(TComponent)]); } - public void RemoveComponent(Entity entity) where TComponent : struct, IComponent + public void RemoveComponent(int entityID) where TComponent : struct, IComponent { - if (entities.ContainsKey(entity)) + if (entities.ContainsKey(entityID)) { - entities[entity] = entities[entity].UnSet(TypeToIndex[typeof(TComponent)]); + entities[entityID] = entities[entityID].UnSet(TypeToIndex[typeof(TComponent)]); } } - public void RemoveEntity(Entity entity) + public void RemoveEntity(int entityID) { - if (entities.ContainsKey(entity)) + if (entities.ContainsKey(entityID)) { - entities.Remove(entity); + entities.Remove(entityID); } } - public BitSet512 EntityBitArray(Entity entity) + public BitSet512 EntityBitArray(int entityID) { - return entities.ContainsKey(entity) ? entities[entity] : BitSet512Builder.Zeroes(); + return entities.ContainsKey(entityID) ? entities[entityID] : BitSet512Builder.Zeroes(); } } } diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 1d5287d..d47b84e 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -31,75 +31,70 @@ namespace Encompass } } - public void FinishRegistering() - { - ComponentBitSet.FinishRegistering(); - } - private TypedComponentStore Lookup() where TComponent : struct, IComponent { //RegisterComponentType(); return Stores[typeof(TComponent)] as TypedComponentStore; } - public bool Has(Entity entity) where TComponent : struct, IComponent + public bool Has(int entityID) where TComponent : struct, IComponent { - return Lookup().Has(entity); + return Lookup().Has(entityID); } - public bool Has(Type type, Entity entity) + public bool Has(Type type, int entityID) { - return Stores.ContainsKey(type) && Stores[type].Has(entity); + return Stores.ContainsKey(type) && Stores[type].Has(entityID); } - public BitSet512 EntityBitArray(Entity entity) + public BitSet512 EntityBitArray(int entityID) { - return ComponentBitSet.EntityBitArray(entity); + return ComponentBitSet.EntityBitArray(entityID); } - public TComponent Get(Entity entity) where TComponent : struct, IComponent + public TComponent Get(int entityID) where TComponent : struct, IComponent { - return Lookup().Get(entity); + return Lookup().Get(entityID); } - public void Set(Entity entity, TComponent component) where TComponent : struct, IComponent + public void Set(int entityID, TComponent component) where TComponent : struct, IComponent { - Lookup().Set(entity, component); - ComponentBitSet.Set(entity); + Lookup().Set(entityID, component); + ComponentBitSet.Set(entityID); } - public bool Set(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + public bool Set(int entityID, TComponent component, int priority) where TComponent : struct, IComponent { - if (Lookup().Set(entity, component, priority)) + if (Lookup().Set(entityID, component, priority)) { - ComponentBitSet.Set(entity); + ComponentBitSet.Set(entityID); return true; } return false; } - public bool Remove(Entity entity, int priority) where TComponent : struct, IComponent + public bool Remove(int entityID, int priority) where TComponent : struct, IComponent { - if (Lookup().Remove(entity, priority)) + if (Lookup().Remove(entityID, priority)) { - ComponentBitSet.RemoveComponent(entity); + ComponentBitSet.RemoveComponent(entityID); return true; } return false; } - public void ForceRemove(Entity entity) where TComponent : struct, IComponent + public void ForceRemove(int entityID) where TComponent : struct, IComponent { - Lookup().ForceRemove(entity); + Lookup().ForceRemove(entityID); } - public void Remove(Entity entity) + public void Remove(int entityID) { foreach (var entry in Stores.Values) { - entry.ForceRemove(entity); + entry.ForceRemove(entityID); } - ComponentBitSet.RemoveEntity(entity); + ComponentBitSet.RemoveEntity(entityID); } public bool Any() where TComponent : struct, IComponent @@ -107,7 +102,7 @@ namespace Encompass return Lookup().Count > 0; } - public IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped() + public IEnumerable<(int, Type, IComponent)> AllInterfaceTyped() { foreach (var store in Stores.Values) { @@ -118,7 +113,7 @@ namespace Encompass } } - public IEnumerable<(TComponent, Entity)> All() where TComponent : struct, IComponent + public IEnumerable<(TComponent, int)> All() where TComponent : struct, IComponent { return Lookup().All(); } diff --git a/encompass-cs/Collections/TypedComponentStore.cs b/encompass-cs/Collections/TypedComponentStore.cs index 54f1298..07db0f1 100644 --- a/encompass-cs/Collections/TypedComponentStore.cs +++ b/encompass-cs/Collections/TypedComponentStore.cs @@ -6,63 +6,64 @@ namespace Encompass internal abstract class TypedComponentStore { public abstract int Count { get; } - public abstract IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped(); - public abstract bool Has(Entity entity); - public abstract bool Remove(Entity entity, int priority); - public abstract void ForceRemove(Entity entity); + public abstract IEnumerable<(int, Type, IComponent)> AllInterfaceTyped(); + public abstract bool Has(int entity); + public abstract bool Remove(int entity, int priority); + public abstract void ForceRemove(int entity); public abstract void Clear(); } internal class TypedComponentStore : TypedComponentStore where TComponent : struct, IComponent { - private readonly Dictionary store = new Dictionary(128); - private readonly Dictionary priorities = new Dictionary(128); + private readonly Dictionary store = new Dictionary(128); + private readonly Dictionary priorities = new Dictionary(128); public override int Count { get => store.Count; } - public TComponent Get(Entity entity) + public TComponent Get(int entityID) { - return store[entity]; + return store[entityID]; } - public void Set(Entity entity, TComponent component) + public void Set(int entityID, TComponent component) { - store[entity] = component; + store[entityID] = component; } - public bool Set(Entity entity, TComponent component, int priority) + public bool Set(int entityID, TComponent component, int priority) { - if (!priorities.ContainsKey(entity) || priority < priorities[entity]) { - store[entity] = component; - priorities[entity] = priority; - return true; - } - - return false; - } - - public override bool Remove(Entity entity, int priority) - { - if (!priorities.ContainsKey(entity) || priority < priorities[entity]) + if (!priorities.ContainsKey(entityID) || priority < priorities[entityID]) { - priorities[entity] = priority; - store.Remove(entity); - priorities.Remove(entity); + store[entityID] = component; + priorities[entityID] = priority; return true; } return false; } - public override void ForceRemove(Entity entity) + public override bool Remove(int entityID, int priority) { - store.Remove(entity); - priorities.Remove(entity); + if (!priorities.ContainsKey(entityID) || priority < priorities[entityID]) + { + priorities[entityID] = priority; + store.Remove(entityID); + priorities.Remove(entityID); + return true; + } + + return false; } - public override bool Has(Entity entity) + public override void ForceRemove(int entityID) { - return store.ContainsKey(entity); + store.Remove(entityID); + priorities.Remove(entityID); + } + + public override bool Has(int entityID) + { + return store.ContainsKey(entityID); } public override void Clear() @@ -71,7 +72,7 @@ namespace Encompass priorities.Clear(); } - public IEnumerable<(TComponent, Entity)> All() + public IEnumerable<(TComponent, int)> All() { foreach (var kvp in store) { @@ -79,7 +80,7 @@ namespace Encompass } } - public override IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped() + public override IEnumerable<(int, Type, IComponent)> AllInterfaceTyped() { foreach (var kvp in store) { diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 36f6cd8..959157b 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -9,7 +9,7 @@ namespace Encompass private readonly ComponentUpdateManager componentUpdateManager; private readonly ComponentStore componentStore; - private readonly HashSet entitiesMarkedForRemoval = new HashSet(); + private readonly HashSet entitiesMarkedForRemoval = new HashSet(); public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager, Dictionary typeToIndex) { @@ -23,11 +23,6 @@ namespace Encompass componentStore.RegisterComponentType(); } - public void FinishRegistering() - { - componentStore.FinishRegistering(); - } - internal void SetComponentStore(ComponentStore componentStore) { this.componentStore.SwapWith(componentStore); @@ -35,12 +30,12 @@ namespace Encompass internal void RegisterDrawableComponent(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent { - drawLayerManager.RegisterComponentWithLayer(entity, component, layer); + drawLayerManager.RegisterComponentWithLayer(entity.ID, component, layer); } internal void AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - componentStore.Set(entity, component); + componentStore.Set(entity.ID, component); } internal void WriteComponents() @@ -48,7 +43,7 @@ namespace Encompass componentStore.SwapWith(componentUpdateManager.UpToDateComponentStore); } - internal IEnumerable<(TComponent, Entity)> GetComponentsIncludingEntity() where TComponent : struct, IComponent + internal IEnumerable<(TComponent, int)> GetComponentsIncludingEntity() where TComponent : struct, IComponent { return componentStore.All(); } @@ -63,12 +58,12 @@ namespace Encompass internal TComponent GetComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - return componentStore.Get(entity); + return componentStore.Get(entity.ID); } internal bool EntityHasComponentOfType(Entity entity) where TComponent : struct, IComponent { - return componentStore.Has(entity); + return componentStore.Has(entity.ID); } internal bool ComponentOfTypeExists() where TComponent : struct, IComponent @@ -78,15 +73,15 @@ namespace Encompass internal void MarkAllComponentsOnEntityForRemoval(Entity entity) { - entitiesMarkedForRemoval.Add(entity); + entitiesMarkedForRemoval.Add(entity.ID); } internal void RemoveMarkedComponents() { - foreach (var entity in entitiesMarkedForRemoval) + foreach (var entityID in entitiesMarkedForRemoval) { - componentStore.Remove(entity); - drawLayerManager.UnRegisterEntityWithLayer(entity); + componentStore.Remove(entityID); + drawLayerManager.UnRegisterEntityWithLayer(entityID); } entitiesMarkedForRemoval.Clear(); @@ -96,7 +91,7 @@ namespace Encompass { if (componentUpdateManager.RemovePending(entity, priority)) { - drawLayerManager.UnRegisterComponentWithLayer(entity); + drawLayerManager.UnRegisterComponentWithLayer(entity.ID); return true; } return false; @@ -106,7 +101,7 @@ namespace Encompass { if (componentUpdateManager.Remove(entity, priority)) { - drawLayerManager.UnRegisterComponentWithLayer(entity); + drawLayerManager.UnRegisterComponentWithLayer(entity.ID); return true; } return false; diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index aa195e1..3f0173b 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -28,13 +28,6 @@ namespace Encompass UpToDateComponentStore.RegisterComponentType(); } - public void FinishRegistering() - { - existingComponentStore.FinishRegistering(); - pendingComponentStore.FinishRegistering(); - UpToDateComponentStore.FinishRegistering(); - } - internal void Clear() { existingComponentStore.ClearAll(); @@ -56,12 +49,12 @@ namespace Encompass { RegisterExistingOrPendingComponentMessage(entity, component); - existingComponentStore.Set(entity, component); + existingComponentStore.Set(entity.ID, component); } internal bool AddPendingComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { - if (pendingComponentStore.Set(entity, component, priority)) + if (pendingComponentStore.Set(entity.ID, component, priority)) { RegisterExistingOrPendingComponentMessage(entity, component); return true; @@ -72,45 +65,45 @@ namespace Encompass internal bool RemovePending(Entity entity, int priority) where TComponent : struct, IComponent { - UpToDateComponentStore.Remove(entity, priority); - return pendingComponentStore.Remove(entity, priority); + UpToDateComponentStore.Remove(entity.ID, priority); + return pendingComponentStore.Remove(entity.ID, priority); } internal bool Remove(Entity entity, int priority) where TComponent : struct, IComponent { - return UpToDateComponentStore.Remove(entity, priority); + return UpToDateComponentStore.Remove(entity.ID, priority); } private void RegisterExistingOrPendingComponentMessage(Entity entity, TComponent component) where TComponent : struct, IComponent { - UpToDateComponentStore.Set(entity, component); + UpToDateComponentStore.Set(entity.ID, component); } public bool UpdateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { - return UpToDateComponentStore.Set(entity, component, priority); + return UpToDateComponentStore.Set(entity.ID, component, priority); } // general component reads by type - internal IEnumerable<(TComponent, Entity)> ReadExistingAndPendingComponentsByType() where TComponent : struct, IComponent + internal IEnumerable<(TComponent, int)> ReadExistingAndPendingComponentsByType() where TComponent : struct, IComponent { return UpToDateComponentStore.All(); } - internal IEnumerable<(TComponent, Entity)> ReadExistingComponentsByType() where TComponent : struct, IComponent + internal IEnumerable<(TComponent, int)> ReadExistingComponentsByType() where TComponent : struct, IComponent { return existingComponentStore.All(); } - internal IEnumerable<(TComponent, Entity)> ReadPendingComponentsByType() where TComponent : struct, IComponent + internal IEnumerable<(TComponent, int)> ReadPendingComponentsByType() where TComponent : struct, IComponent { return pendingComponentStore.All(); } // singular component reads by type - internal (TComponent, Entity) ReadFirstExistingOrPendingComponentByType() where TComponent : struct, IComponent + internal (TComponent, int) ReadFirstExistingOrPendingComponentByType() where TComponent : struct, IComponent { if (!SomeExistingOrPendingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } var enumerator = ReadExistingAndPendingComponentsByType().GetEnumerator(); @@ -118,7 +111,7 @@ namespace Encompass return enumerator.Current; } - internal (TComponent, Entity) ReadFirstExistingComponentByType() where TComponent : struct, IComponent + internal (TComponent, int) ReadFirstExistingComponentByType() where TComponent : struct, IComponent { if (!SomeExistingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } var enumerator = ReadExistingComponentsByType().GetEnumerator(); @@ -126,7 +119,7 @@ namespace Encompass return enumerator.Current; } - internal (TComponent, Entity) ReadFirstPendingComponentByType() where TComponent : struct, IComponent + internal (TComponent, int) ReadFirstPendingComponentByType() where TComponent : struct, IComponent { if (!SomePendingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } var enumerator = ReadPendingComponentsByType().GetEnumerator(); @@ -155,47 +148,47 @@ namespace Encompass internal TComponent ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - return existingComponentStore.Get(entity); + return existingComponentStore.Get(entity.ID); } internal TComponent ReadPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - return pendingComponentStore.Get(entity); + return pendingComponentStore.Get(entity.ID); } // check if entity has component of type internal bool HasExistingOrPendingComponent(Entity entity) where TComponent : struct, IComponent { - return UpToDateComponentStore.Has(entity); + return UpToDateComponentStore.Has(entity.ID); } internal bool HasExistingOrPendingComponent(Entity entity, Type type) { - return UpToDateComponentStore.Has(type, entity); + return UpToDateComponentStore.Has(type, entity.ID); } internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent { - return existingComponentStore.Has(entity); + return existingComponentStore.Has(entity.ID); } internal bool HasExistingComponent(Entity entity, Type type) { - return existingComponentStore.Has(type, entity); + return existingComponentStore.Has(type, entity.ID); } internal bool HasPendingComponent(Entity entity) where TComponent : struct, IComponent { - return pendingComponentStore.Has(entity); + return pendingComponentStore.Has(entity.ID); } internal bool HasPendingComponent(Entity entity, Type type) { - return pendingComponentStore.Has(type, entity); + return pendingComponentStore.Has(type, entity.ID); } internal ComponentBitSet PendingBits { get { return pendingComponentStore.ComponentBitSet; } } - internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } + internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } } } diff --git a/encompass-cs/DrawLayerManager.cs b/encompass-cs/DrawLayerManager.cs index b2bc047..b738fce 100644 --- a/encompass-cs/DrawLayerManager.cs +++ b/encompass-cs/DrawLayerManager.cs @@ -12,7 +12,7 @@ namespace Encompass private readonly Dictionary layerIndexToComponentStore = new Dictionary(512); private readonly Dictionary> layerIndexToGeneralRenderers = new Dictionary>(512); - private readonly Dictionary> typeToEntityToLayer = new Dictionary>(512); + private readonly Dictionary> typeToEntityToLayer = new Dictionary>(512); private Dictionary typeToIndex; public IEnumerable LayerOrder { get { return layerOrder.Values; } } @@ -32,19 +32,11 @@ namespace Encompass } } - public void FinishRegistering() - { - foreach (var store in layerIndexToComponentStore.Values) - { - store.FinishRegistering(); - } - } - public void RegisterOrderedDrawable() where TComponent : struct, IComponent { if (!typeToEntityToLayer.ContainsKey(typeof(TComponent))) { - typeToEntityToLayer.Add(typeof(TComponent), new Dictionary(128)); + typeToEntityToLayer.Add(typeof(TComponent), new Dictionary(128)); } foreach (var pair in layerIndexToComponentStore) @@ -74,38 +66,38 @@ namespace Encompass RegisterGeneralRendererWithLayer(renderer, newLayer); } - public void RegisterComponentWithLayer(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent + public void RegisterComponentWithLayer(int entityID, TComponent component, int layer) where TComponent : struct, IComponent { if (!layerIndexToComponentStore.ContainsKey(layer)) { throw new UndefinedLayerException("Layer {0} is not defined. Use WorldBuilder.RegisterDrawLayer to register the layer.", layer); } - if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) { UnRegisterComponentWithLayer(entity); } + if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entityID)) { UnRegisterComponentWithLayer(entityID); } var set = layerIndexToComponentStore[layer]; - set.Set(entity, component); + set.Set(entityID, component); - typeToEntityToLayer[typeof(TComponent)].Add(entity, layer); + typeToEntityToLayer[typeof(TComponent)].Add(entityID, layer); } - public void UnRegisterComponentWithLayer(Entity entity) where TComponent : struct, IComponent + public void UnRegisterComponentWithLayer(int entityID) where TComponent : struct, IComponent { if (!typeToEntityToLayer.ContainsKey(typeof(TComponent))) { return; } - if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) + if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entityID)) { - var layer = typeToEntityToLayer[typeof(TComponent)][entity]; - layerIndexToComponentStore[layer].ForceRemove(entity); + var layer = typeToEntityToLayer[typeof(TComponent)][entityID]; + layerIndexToComponentStore[layer].ForceRemove(entityID); } - typeToEntityToLayer[typeof(TComponent)].Remove(entity); + typeToEntityToLayer[typeof(TComponent)].Remove(entityID); } - public void UnRegisterEntityWithLayer(Entity entity) + public void UnRegisterEntityWithLayer(int entityID) { foreach (var store in layerIndexToComponentStore.Values) { - store.Remove(entity); + store.Remove(entityID); } } @@ -116,11 +108,11 @@ namespace Encompass Enumerable.Empty(); } - public IEnumerable<(Entity, Type, IComponent)> AllInLayer(int layer) + public IEnumerable<(int, Type, IComponent)> AllInLayer(int layer) { return layerIndexToComponentStore.ContainsKey(layer) ? layerIndexToComponentStore[layer].AllInterfaceTyped() : - Enumerable.Empty<(Entity, Type, IComponent)>(); + Enumerable.Empty<(int, Type, IComponent)>(); } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 5157fa5..10fd6a8 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -173,7 +173,7 @@ namespace Encompass /// protected Entity ReadEntity() where TComponent : struct, IComponent { - return ReadComponentHelper().Item2; + return entityManager.GetEntity(ReadComponentHelper().Item2); } /// @@ -183,7 +183,7 @@ namespace Encompass { foreach (var pair in ReadComponentsHelper()) { - yield return pair.Item2; + yield return entityManager.GetEntity(pair.Item2); } } @@ -194,7 +194,7 @@ namespace Encompass return componentManager.GetComponentsByType(); } - private IEnumerable<(TComponent, Entity)> ReadComponentsHelper() where TComponent : struct, IComponent + private IEnumerable<(TComponent, int)> ReadComponentsHelper() where TComponent : struct, IComponent { var pendingRead = readPendingTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); @@ -232,15 +232,21 @@ namespace Encompass /// protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity() where TComponent : struct, IComponent { - return ReadComponentsHelper(); + foreach (var (component, id) in ReadComponentsHelper()) + { + yield return (component, entityManager.GetEntity(id)); + } } internal IEnumerable<(TComponent, Entity)> InternalRead() where TComponent : struct, IComponent { - return componentManager.GetComponentsIncludingEntity(); + foreach (var (component, id) in componentManager.GetComponentsIncludingEntity()) + { + yield return (component, entityManager.GetEntity(id)); + } } - private (TComponent, Entity) ReadComponentHelper() where TComponent : struct, IComponent + private (TComponent, int) ReadComponentHelper() where TComponent : struct, IComponent { var pendingRead = readPendingTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); @@ -275,7 +281,8 @@ namespace Encompass /// protected (TComponent, Entity) ReadComponentIncludingEntity() where TComponent : struct, IComponent { - return ReadComponentHelper(); + var (component, id) = ReadComponentHelper(); + return (component, entityManager.GetEntity(id)); } /// diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index 36650c5..77a85c0 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -49,6 +49,16 @@ namespace Encompass return IDs.ContainsKey(id); } + public Entity GetEntity(int id) + { + if (!EntityExists(id)) + { + throw new Encompass.Exceptions.EntityNotFoundException("Entity with id {0} does not exist.", id); + } + + return IDs[id]; + } + public void MarkForDestroy(Entity entity) { entitiesMarkedForDestroy.Add(entity); diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 219c712..0968467 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -25,8 +25,8 @@ namespace Encompass { foreach (var entity in entities) { - var pendingBits = pendingBitLookup.EntityBitArray(entity); - var existingBits = existingBitLookup.EntityBitArray(entity); + var pendingBits = pendingBitLookup.EntityBitArray(entity.ID); + var existingBits = existingBitLookup.EntityBitArray(entity.ID); var pending = WithPendingMask.And(pendingBits); var existing = WithExistingMask.And(existingBits); diff --git a/encompass-cs/RenderManager.cs b/encompass-cs/RenderManager.cs index c1f7e92..529a431 100644 --- a/encompass-cs/RenderManager.cs +++ b/encompass-cs/RenderManager.cs @@ -5,12 +5,14 @@ namespace Encompass { internal class RenderManager { + private readonly EntityManager entityManager; private readonly DrawLayerManager drawLayerManager; private readonly Dictionary> drawComponentTypeToOrderedRenderer = new Dictionary>(256); - public RenderManager(DrawLayerManager drawLayerManager) + public RenderManager(EntityManager entityManager, DrawLayerManager drawLayerManager) { + this.entityManager = entityManager; this.drawLayerManager = drawLayerManager; } @@ -31,12 +33,12 @@ namespace Encompass { var generalRendererSet = drawLayerManager.GeneralRenderersByLayer(layer); - foreach (var (entity, componentType, component) in drawLayerManager.AllInLayer(layer)) + foreach (var (entityID, componentType, component) in drawLayerManager.AllInLayer(layer)) { if (drawComponentTypeToOrderedRenderer.ContainsKey(componentType)) { var internalRenderAction = drawComponentTypeToOrderedRenderer[componentType]; - internalRenderAction(entity, component); + internalRenderAction(entityManager.GetEntity(entityID), component); } } diff --git a/encompass-cs/Renderer.cs b/encompass-cs/Renderer.cs index c4a1dab..048d5d7 100644 --- a/encompass-cs/Renderer.cs +++ b/encompass-cs/Renderer.cs @@ -37,7 +37,10 @@ namespace Encompass protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity() where TComponent : struct, IComponent { - return componentManager.GetComponentsIncludingEntity(); + foreach (var (component, id) in componentManager.GetComponentsIncludingEntity()) + { + yield return (component, entityManager.GetEntity(id)); + } } protected TComponent ReadComponent() where TComponent : struct, IComponent diff --git a/encompass-cs/UberEngine.cs b/encompass-cs/UberEngine.cs index 7566b82..96a74b7 100644 --- a/encompass-cs/UberEngine.cs +++ b/encompass-cs/UberEngine.cs @@ -9,9 +9,9 @@ namespace Encompass { private IEnumerable _componentTypes; private IEnumerable _messageTypes; - private Entity _entity; + public Entity Entity { get; private set; } - public UberEngine(Entity entity, IEnumerable componentTypes, IEnumerable messageTypes) + public UberEngine(IEnumerable componentTypes, IEnumerable messageTypes) { _componentTypes = componentTypes; _messageTypes = messageTypes; @@ -19,14 +19,15 @@ namespace Encompass writeTypes.UnionWith(componentTypes); sendTypes.UnionWith(messageTypes); receiveTypes.UnionWith(messageTypes); - _entity = entity; } public void Write() { + Entity = CreateEntity(); + foreach (var type in _componentTypes) { - var instanceParam = new object[] { _entity, Activator.CreateInstance(type) }; + var instanceParam = new object[] { Entity, Activator.CreateInstance(type) }; var setComponentMethod = typeof(Engine).GetMethod("SetComponent", BindingFlags.NonPublic | BindingFlags.Instance); var genericSetComponentMethod = setComponentMethod.MakeGenericMethod(type); genericSetComponentMethod.Invoke(this, instanceParam); @@ -43,10 +44,10 @@ namespace Encompass CallGenericMethod(type, "ReadComponentsIncludingEntity", null); CallGenericMethod(type, "ReadEntity", null); CallGenericMethod(type, "ReadEntities", null); - CallGenericMethod(type, "GetComponent", new Type[] { typeof(Entity) }, new object[] { _entity }); - CallGenericMethod(type, "HasComponent", new Type[] { typeof(Entity) }, new object[] { _entity }); + CallGenericMethod(type, "GetComponent", new Type[] { typeof(Entity) }, new object[] { Entity }); + CallGenericMethod(type, "HasComponent", new Type[] { typeof(Entity) }, new object[] { Entity }); CallGenericMethod(type, "SomeComponent", null); - CallGenericMethod(type, "RemoveComponent", new Type[] { typeof(Entity) }, new object[] { _entity }); + CallGenericMethod(type, "RemoveComponent", new Type[] { typeof(Entity) }, new object[] { Entity }); CallGenericMethod(type, "DestroyWith", null); CallGenericMethod(type, "DestroyAllWith", null); } diff --git a/encompass-cs/UberRenderer.cs b/encompass-cs/UberRenderer.cs index be84634..be4bde4 100644 --- a/encompass-cs/UberRenderer.cs +++ b/encompass-cs/UberRenderer.cs @@ -6,13 +6,17 @@ namespace Encompass { class UberRenderer : Renderer { - private Entity _entity; private IEnumerable _componentTypes; + private Entity _entity; - public UberRenderer(Entity entity, IEnumerable componentTypes) + public UberRenderer(IEnumerable componentTypes) + { + _componentTypes = componentTypes; + } + + public void SetEntity(Entity entity) { _entity = entity; - _componentTypes = componentTypes; } public void Render() diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 3c82f8c..2a1c782 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -51,7 +51,7 @@ namespace Encompass componentManager = new ComponentManager(drawLayerManager, componentUpdateManager, typeToIndex); messageManager = new MessageManager(timeManager); entityManager = new EntityManager(componentManager, entityCapacity); - renderManager = new RenderManager(drawLayerManager); + renderManager = new RenderManager(entityManager, drawLayerManager); startingComponentStoreForComponentManager = new ComponentStore(typeToIndex); startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex); @@ -89,10 +89,8 @@ namespace Encompass RegisterComponentType(); componentTypesToRegister.Add(typeof(TComponent)); - startingComponentStoreForComponentManager.FinishRegistering(); - startingComponentStoreForComponentManager.Set(entity, component); - startingComponentStoreForComponentUpdateManager.FinishRegistering(); - startingComponentStoreForComponentUpdateManager.Set(entity, component); + startingComponentStoreForComponentManager.Set(entity.ID, component); + startingComponentStoreForComponentUpdateManager.Set(entity.ID, component); if (component is IDrawableComponent drawableComponent) { @@ -371,12 +369,6 @@ namespace Encompass engineOrder.Add(emitterEngine); } - componentManager.FinishRegistering(); - componentUpdateManager.FinishRegistering(); - drawLayerManager.FinishRegistering(); - startingComponentStoreForComponentManager.FinishRegistering(); - startingComponentStoreForComponentUpdateManager.FinishRegistering(); - PreloadJIT(componentTypesToRegister, messageTypes); foreach (var engine in engineGraph.TopologicalSort()) @@ -415,12 +407,11 @@ namespace Encompass var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex); var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); - var dummyRenderManager = new RenderManager(dummyDrawLayerManager); + var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager); var prepEngineOrder = new List(); - var entity = dummyEntityManager.CreateEntity(); - var uberEngine = new UberEngine(entity, componentTypes, messageTypes); + var uberEngine = new UberEngine(componentTypes, messageTypes); uberEngine.AssignEntityManager(dummyEntityManager); uberEngine.AssignComponentManager(dummyComponentManager); @@ -428,7 +419,7 @@ namespace Encompass uberEngine.AssignComponentUpdateManager(dummyComponentUpdateManager); uberEngine.AssignTimeManager(dummyTimeManager); - var uberRenderer = new UberRenderer(entity, componentTypes); + var uberRenderer = new UberRenderer(componentTypes); uberRenderer.AssignComponentManager(dummyComponentManager); uberRenderer.AssignEntityManager(dummyEntityManager); @@ -481,6 +472,7 @@ namespace Encompass dummyComponentManager.WriteComponents(); dummyComponentUpdateManager.Clear(); + uberRenderer.SetEntity(uberEngine.Entity); uberRenderer.Render(); } } From bd17a86dec0dcfa53138430ce539a67a7dc0ef8d Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Mon, 23 Dec 2019 19:04:26 -0800 Subject: [PATCH 17/37] rename pending to immediate --- encompass-cs/Attributes/ReadsImmediate.cs | 28 +++++ encompass-cs/Attributes/ReadsPending.cs | 28 ----- encompass-cs/Attributes/WritesImmediate.cs | 27 +++++ encompass-cs/Attributes/WritesPending.cs | 26 ----- encompass-cs/ComponentManager.cs | 4 +- encompass-cs/ComponentUpdateManager.cs | 70 ++++++------ encompass-cs/Engine.cs | 100 +++++++++--------- encompass-cs/EntitySetQuery.cs | 22 ++-- ...n.cs => IllegalWriteImmediateException.cs} | 6 +- .../IllegalWritePendingTypeException.cs | 4 +- encompass-cs/WorldBuilder.cs | 12 +-- test/ComponentTest.cs | 12 +-- test/EngineTest.cs | 74 ++++++------- test/SpawnerTest.cs | 2 +- test/WorldBuilderTest.cs | 14 +-- 15 files changed, 212 insertions(+), 217 deletions(-) create mode 100644 encompass-cs/Attributes/ReadsImmediate.cs delete mode 100644 encompass-cs/Attributes/ReadsPending.cs create mode 100644 encompass-cs/Attributes/WritesImmediate.cs delete mode 100644 encompass-cs/Attributes/WritesPending.cs rename encompass-cs/Exceptions/{IllegalWritePendingException.cs => IllegalWriteImmediateException.cs} (56%) diff --git a/encompass-cs/Attributes/ReadsImmediate.cs b/encompass-cs/Attributes/ReadsImmediate.cs new file mode 100644 index 0000000..e47d4ba --- /dev/null +++ b/encompass-cs/Attributes/ReadsImmediate.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Encompass.Exceptions; + +namespace Encompass +{ + [AttributeUsage(AttributeTargets.Class)] + public class ReadsImmediate : Attribute + { + public readonly HashSet readImmediateTypes = new HashSet(); + + public ReadsImmediate(params Type[] readImmediateTypes) + { + foreach (var readImmediateType in readImmediateTypes) + { + var isComponent = readImmediateType.GetInterfaces().Contains(typeof(IComponent)); + + if (!isComponent) + { + throw new IllegalReadTypeException("{0} must be a Component", readImmediateType.Name); + } + + this.readImmediateTypes.Add(readImmediateType); + } + } + } +} diff --git a/encompass-cs/Attributes/ReadsPending.cs b/encompass-cs/Attributes/ReadsPending.cs deleted file mode 100644 index e7e8653..0000000 --- a/encompass-cs/Attributes/ReadsPending.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Encompass.Exceptions; - -namespace Encompass -{ - [AttributeUsage(AttributeTargets.Class)] - public class ReadsPending : Attribute - { - public readonly HashSet readPendingTypes = new HashSet(); - - public ReadsPending(params Type[] readPendingTypes) - { - foreach (var readPendingType in readPendingTypes) - { - var isComponent = readPendingType.GetInterfaces().Contains(typeof(IComponent)); - - if (!isComponent) - { - throw new IllegalReadTypeException("{0} must be a Component", readPendingType.Name); - } - - this.readPendingTypes.Add(readPendingType); - } - } - } -} \ No newline at end of file diff --git a/encompass-cs/Attributes/WritesImmediate.cs b/encompass-cs/Attributes/WritesImmediate.cs new file mode 100644 index 0000000..66a46a6 --- /dev/null +++ b/encompass-cs/Attributes/WritesImmediate.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Encompass.Exceptions; + +namespace Encompass +{ + [AttributeUsage(AttributeTargets.Class)] + public class WritesImmediate : Attribute + { + public readonly HashSet writeImmediateTypes = new HashSet(); + + public WritesImmediate(params Type[] writeImmediateTypes) + { + foreach (var writeImmediateType in writeImmediateTypes) + { + var isComponent = writeImmediateType.GetInterfaces().Contains(typeof(IComponent)); + if (!isComponent) + { + throw new IllegalWriteImmediateTypeException("{0} must be a Component", writeImmediateType.Name); + } + + this.writeImmediateTypes.Add(writeImmediateType); + } + } + } +} diff --git a/encompass-cs/Attributes/WritesPending.cs b/encompass-cs/Attributes/WritesPending.cs deleted file mode 100644 index ea5a370..0000000 --- a/encompass-cs/Attributes/WritesPending.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Encompass.Exceptions; - -namespace Encompass -{ - public class WritesPending : Attribute - { - public readonly HashSet writePendingTypes = new HashSet(); - - public WritesPending(params Type[] writePendingTypes) - { - foreach (var writePendingType in writePendingTypes) - { - var isComponent = writePendingType.GetInterfaces().Contains(typeof(IComponent)); - if (!isComponent) - { - throw new IllegalWritePendingTypeException("{0} must be a Component", writePendingType.Name); - } - - this.writePendingTypes.Add(writePendingType); - } - } - } -} \ No newline at end of file diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 959157b..c8c0cc9 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -87,9 +87,9 @@ namespace Encompass entitiesMarkedForRemoval.Clear(); } - public bool RemovePending(Entity entity, int priority) where TComponent : struct, IComponent + public bool RemoveImmediate(Entity entity, int priority) where TComponent : struct, IComponent { - if (componentUpdateManager.RemovePending(entity, priority)) + if (componentUpdateManager.RemoveImmediate(entity, priority)) { drawLayerManager.UnRegisterComponentWithLayer(entity.ID); return true; diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 3f0173b..a2d9849 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -7,8 +7,7 @@ namespace Encompass internal class ComponentUpdateManager { private readonly ComponentStore existingComponentStore; - private readonly ComponentStore pendingComponentStore; - private readonly Dictionary> typeToEntityToPendingComponentPriority = new Dictionary>(128); + private readonly ComponentStore immediateComponentStore; public Dictionary TypeToIndex { get; } public ComponentStore UpToDateComponentStore { get; private set; } @@ -16,7 +15,7 @@ namespace Encompass public ComponentUpdateManager(Dictionary typeToIndex) { existingComponentStore = new ComponentStore(typeToIndex); - pendingComponentStore = new ComponentStore(typeToIndex); + immediateComponentStore = new ComponentStore(typeToIndex); UpToDateComponentStore = new ComponentStore(typeToIndex); TypeToIndex = typeToIndex; } @@ -24,20 +23,15 @@ namespace Encompass public void RegisterComponentType() where TComponent : struct, IComponent { existingComponentStore.RegisterComponentType(); - pendingComponentStore.RegisterComponentType(); + immediateComponentStore.RegisterComponentType(); UpToDateComponentStore.RegisterComponentType(); } internal void Clear() { existingComponentStore.ClearAll(); - pendingComponentStore.ClearAll(); + immediateComponentStore.ClearAll(); UpToDateComponentStore.ClearAll(); - - foreach (var dictionary in typeToEntityToPendingComponentPriority.Values) - { - dictionary.Clear(); - } } internal void SetStartingComponentStore(ComponentStore componentStore) @@ -47,26 +41,26 @@ namespace Encompass internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - RegisterExistingOrPendingComponentMessage(entity, component); + RegisterExistingOrImmediateComponentMessage(entity, component); existingComponentStore.Set(entity.ID, component); } - internal bool AddPendingComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { - if (pendingComponentStore.Set(entity.ID, component, priority)) + if (immediateComponentStore.Set(entity.ID, component, priority)) { - RegisterExistingOrPendingComponentMessage(entity, component); + RegisterExistingOrImmediateComponentMessage(entity, component); return true; } return false; } - internal bool RemovePending(Entity entity, int priority) where TComponent : struct, IComponent + internal bool RemoveImmediate(Entity entity, int priority) where TComponent : struct, IComponent { UpToDateComponentStore.Remove(entity.ID, priority); - return pendingComponentStore.Remove(entity.ID, priority); + return immediateComponentStore.Remove(entity.ID, priority); } internal bool Remove(Entity entity, int priority) where TComponent : struct, IComponent @@ -74,7 +68,7 @@ namespace Encompass return UpToDateComponentStore.Remove(entity.ID, priority); } - private void RegisterExistingOrPendingComponentMessage(Entity entity, TComponent component) where TComponent : struct, IComponent + private void RegisterExistingOrImmediateComponentMessage(Entity entity, TComponent component) where TComponent : struct, IComponent { UpToDateComponentStore.Set(entity.ID, component); } @@ -86,7 +80,7 @@ namespace Encompass // general component reads by type - internal IEnumerable<(TComponent, int)> ReadExistingAndPendingComponentsByType() where TComponent : struct, IComponent + internal IEnumerable<(TComponent, int)> ReadExistingAndImmediateComponentsByType() where TComponent : struct, IComponent { return UpToDateComponentStore.All(); } @@ -96,17 +90,17 @@ namespace Encompass return existingComponentStore.All(); } - internal IEnumerable<(TComponent, int)> ReadPendingComponentsByType() where TComponent : struct, IComponent + internal IEnumerable<(TComponent, int)> ReadImmediateComponentsByType() where TComponent : struct, IComponent { - return pendingComponentStore.All(); + return immediateComponentStore.All(); } // singular component reads by type - internal (TComponent, int) ReadFirstExistingOrPendingComponentByType() where TComponent : struct, IComponent + internal (TComponent, int) ReadFirstExistingOrImmediateComponentByType() where TComponent : struct, IComponent { - if (!SomeExistingOrPendingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - var enumerator = ReadExistingAndPendingComponentsByType().GetEnumerator(); + if (!SomeExistingOrImmediateComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + var enumerator = ReadExistingAndImmediateComponentsByType().GetEnumerator(); enumerator.MoveNext(); return enumerator.Current; } @@ -119,17 +113,17 @@ namespace Encompass return enumerator.Current; } - internal (TComponent, int) ReadFirstPendingComponentByType() where TComponent : struct, IComponent + internal (TComponent, int) ReadFirstImmediateComponentByType() where TComponent : struct, IComponent { - if (!SomePendingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - var enumerator = ReadPendingComponentsByType().GetEnumerator(); + if (!SomeImmediateComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + var enumerator = ReadImmediateComponentsByType().GetEnumerator(); enumerator.MoveNext(); return enumerator.Current; } // check if some component of type exists in the world - internal bool SomeExistingOrPendingComponent() where TComponent : struct, IComponent + internal bool SomeExistingOrImmediateComponent() where TComponent : struct, IComponent { return UpToDateComponentStore.Any(); } @@ -139,9 +133,9 @@ namespace Encompass return existingComponentStore.Any(); } - internal bool SomePendingComponent() where TComponent : struct, IComponent + internal bool SomeImmediateComponent() where TComponent : struct, IComponent { - return pendingComponentStore.Any(); + return immediateComponentStore.Any(); } // read components by entity and type @@ -151,19 +145,19 @@ namespace Encompass return existingComponentStore.Get(entity.ID); } - internal TComponent ReadPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal TComponent ReadImmediateComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - return pendingComponentStore.Get(entity.ID); + return immediateComponentStore.Get(entity.ID); } // check if entity has component of type - internal bool HasExistingOrPendingComponent(Entity entity) where TComponent : struct, IComponent + internal bool HasExistingOrImmediateComponent(Entity entity) where TComponent : struct, IComponent { return UpToDateComponentStore.Has(entity.ID); } - internal bool HasExistingOrPendingComponent(Entity entity, Type type) + internal bool HasExistingOrImmediateComponent(Entity entity, Type type) { return UpToDateComponentStore.Has(type, entity.ID); } @@ -178,17 +172,17 @@ namespace Encompass return existingComponentStore.Has(type, entity.ID); } - internal bool HasPendingComponent(Entity entity) where TComponent : struct, IComponent + internal bool HasImmediateComponent(Entity entity) where TComponent : struct, IComponent { - return pendingComponentStore.Has(entity.ID); + return immediateComponentStore.Has(entity.ID); } - internal bool HasPendingComponent(Entity entity, Type type) + internal bool HasImmediateComponent(Entity entity, Type type) { - return pendingComponentStore.Has(type, entity.ID); + return immediateComponentStore.Has(type, entity.ID); } - internal ComponentBitSet PendingBits { get { return pendingComponentStore.ComponentBitSet; } } + internal ComponentBitSet ImmediateBits { get { return immediateComponentStore.ComponentBitSet; } } internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 4a1033f..8d32f2d 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -17,11 +17,11 @@ namespace Encompass internal Guid ID; internal readonly HashSet readTypes = new HashSet(); - internal readonly HashSet readPendingTypes = new HashSet(); + internal readonly HashSet readImmediateTypes = new HashSet(); internal readonly HashSet sendTypes = new HashSet(); internal readonly HashSet receiveTypes = new HashSet(); internal readonly HashSet writeTypes = new HashSet(); - internal readonly HashSet writePendingTypes = new HashSet(); + internal readonly HashSet writeImmediateTypes = new HashSet(); internal readonly HashSet queryWithTypes = new HashSet(); internal readonly HashSet queryWithoutTypes = new HashSet(); internal readonly Dictionary writePriorities = new Dictionary(); @@ -51,10 +51,10 @@ namespace Encompass sendTypes = sendsAttribute.sendTypes; } - var activatesAttribute = GetType().GetCustomAttribute(false); + var activatesAttribute = GetType().GetCustomAttribute(false); if (activatesAttribute != null) { - writePendingTypes = activatesAttribute.writePendingTypes; + writeImmediateTypes = activatesAttribute.writeImmediateTypes; } var defaultWritePriorityAttribute = GetType().GetCustomAttribute(false); @@ -82,10 +82,10 @@ namespace Encompass readTypes = readsAttribute.readTypes; } - var readsPendingAttribute = GetType().GetCustomAttribute(false); - if (readsPendingAttribute != null) + var readsImmediateAttribute = GetType().GetCustomAttribute(false); + if (readsImmediateAttribute != null) { - readPendingTypes = readsPendingAttribute.readPendingTypes; + readImmediateTypes = readsImmediateAttribute.readImmediateTypes; } var queryWithAttribute = GetType().GetCustomAttribute(false); @@ -196,19 +196,19 @@ namespace Encompass private IEnumerable<(TComponent, int)> ReadComponentsHelper() where TComponent : struct, IComponent { - var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var immediateRead = readImmediateTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); - if (existingRead && pendingRead) + if (existingRead && immediateRead) { - return componentUpdateManager.ReadExistingAndPendingComponentsByType(); + return componentUpdateManager.ReadExistingAndImmediateComponentsByType(); } else if (existingRead) { return componentUpdateManager.ReadExistingComponentsByType(); } - else if (pendingRead) + else if (immediateRead) { - return componentUpdateManager.ReadPendingComponentsByType(); + return componentUpdateManager.ReadImmediateComponentsByType(); } else { @@ -248,19 +248,19 @@ namespace Encompass private (TComponent, int) ReadComponentHelper() where TComponent : struct, IComponent { - var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var immediateRead = readImmediateTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); - if (existingRead && pendingRead) + if (existingRead && immediateRead) { - return componentUpdateManager.ReadFirstExistingOrPendingComponentByType(); + return componentUpdateManager.ReadFirstExistingOrImmediateComponentByType(); } else if (existingRead) { return componentUpdateManager.ReadFirstExistingComponentByType(); } - else if (pendingRead) + else if (immediateRead) { - return componentUpdateManager.ReadFirstPendingComponentByType(); + return componentUpdateManager.ReadFirstImmediateComponentByType(); } else { @@ -290,19 +290,19 @@ namespace Encompass /// protected bool SomeComponent() where TComponent : struct, IComponent { - var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var immediateRead = readImmediateTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); - if (existingRead && pendingRead) + if (existingRead && immediateRead) { - return componentUpdateManager.SomeExistingOrPendingComponent(); + return componentUpdateManager.SomeExistingOrImmediateComponent(); } else if (existingRead) { return componentUpdateManager.SomeExistingComponent(); } - else if (pendingRead) + else if (immediateRead) { - return componentUpdateManager.SomePendingComponent(); + return componentUpdateManager.SomeImmediateComponent(); } else { @@ -312,13 +312,13 @@ namespace Encompass private TComponent GetComponentHelper(Entity entity) where TComponent : struct, IComponent { - var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var immediateRead = readImmediateTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); - if (existingRead && pendingRead) + if (existingRead && immediateRead) { - if (componentUpdateManager.HasPendingComponent(entity)) + if (componentUpdateManager.HasImmediateComponent(entity)) { - return componentUpdateManager.ReadPendingComponentByEntityAndType(entity); + return componentUpdateManager.ReadImmediateComponentByEntityAndType(entity); } else if (componentUpdateManager.HasExistingComponent(entity)) { @@ -333,9 +333,9 @@ namespace Encompass { return componentUpdateManager.ReadExistingComponentByEntityAndType(entity); } - else if (pendingRead) + else if (immediateRead) { - return componentUpdateManager.ReadPendingComponentByEntityAndType(entity); + return componentUpdateManager.ReadImmediateComponentByEntityAndType(entity); } else { @@ -365,20 +365,20 @@ namespace Encompass /// protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { - var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var immediateRead = readImmediateTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); - if (pendingRead && existingRead) + if (immediateRead && existingRead) { - return componentUpdateManager.HasExistingOrPendingComponent(entity); + return componentUpdateManager.HasExistingOrImmediateComponent(entity); } else if (existingRead) { return componentUpdateManager.HasExistingComponent(entity); } - else if (pendingRead) + else if (immediateRead) { - return componentUpdateManager.HasPendingComponent(entity); + return componentUpdateManager.HasImmediateComponent(entity); } else { @@ -394,20 +394,20 @@ namespace Encompass /// protected bool HasComponent(Entity entity, Type type) { - var pendingRead = readPendingTypes.Contains(type); + var immediateRead = readImmediateTypes.Contains(type); var existingRead = readTypes.Contains(type); - if (pendingRead && existingRead) + if (immediateRead && existingRead) { - return componentUpdateManager.HasExistingOrPendingComponent(entity, type); + return componentUpdateManager.HasExistingOrImmediateComponent(entity, type); } else if (existingRead) { return componentUpdateManager.HasExistingComponent(entity, type); } - else if (pendingRead) + else if (immediateRead) { - return componentUpdateManager.HasPendingComponent(entity, type); + return componentUpdateManager.HasImmediateComponent(entity, type); } else { @@ -431,9 +431,9 @@ namespace Encompass } bool written; - if (writePendingTypes.Contains(typeof(TComponent))) + if (writeImmediateTypes.Contains(typeof(TComponent))) { - written = AddPendingComponent(entity, component, priority); + written = AddImmediateComponent(entity, component, priority); } else { @@ -485,9 +485,9 @@ namespace Encompass componentUpdateManager.AddExistingComponent(entity, component); } - internal bool AddPendingComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { - return componentUpdateManager.AddPendingComponent(entity, component, priority); + return componentUpdateManager.AddImmediateComponent(entity, component, priority); } /// @@ -577,9 +577,9 @@ namespace Encompass throw new IllegalWriteException("Engine {0} tried to remove undeclared Component {1}. Declare with Writes attribute.", GetType().Name, typeof(TComponent).Name); } - if (writePendingTypes.Contains(typeof(TComponent))) + if (writeImmediateTypes.Contains(typeof(TComponent))) { - componentManager.RemovePending(entity, priority); + componentManager.RemoveImmediate(entity, priority); } else { @@ -649,7 +649,7 @@ namespace Encompass protected IEnumerable QueryEntities() { - foreach (var entity in entityQuery.FilterEntities(entityManager.Entities, componentUpdateManager.PendingBits, componentUpdateManager.ExistingBits)) + foreach (var entity in entityQuery.FilterEntities(entityManager.Entities, componentUpdateManager.ImmediateBits, componentUpdateManager.ExistingBits)) { yield return entity; } @@ -672,10 +672,10 @@ namespace Encompass withoutMask = withoutMask.Set(componentUpdateManager.TypeToIndex[type]); } - var pendingMask = BitSetBuilder.Zeroes(); - foreach (var type in readPendingTypes) + var immediateMask = BitSetBuilder.Zeroes(); + foreach (var type in readImmediateTypes) { - pendingMask = pendingMask.Set(componentUpdateManager.TypeToIndex[type]); + immediateMask = immediateMask.Set(componentUpdateManager.TypeToIndex[type]); } var existingMask = BitSetBuilder.Zeroes(); @@ -685,9 +685,9 @@ namespace Encompass } entityQuery = new EntitySetQuery( - withMask.And(pendingMask), + withMask.And(immediateMask), withMask.And(existingMask), - withoutMask.And(pendingMask), + withoutMask.And(immediateMask), withoutMask.And(existingMask), withMask.Not() ); diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index c13323b..afcd9ae 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -6,35 +6,35 @@ namespace Encompass { internal struct EntitySetQuery { - private BitSet WithPendingMask { get; } + private BitSet WithImmediateMask { get; } private BitSet WithExistingMask { get; } - private BitSet WithoutPendingMask { get; } + private BitSet WithoutImmediateMask { get; } private BitSet WithoutExistingMask { get; } private BitSet NotWithMask { get; } - internal EntitySetQuery(BitSet withPendingMask, BitSet withExistingMask, BitSet withoutPendingMask, BitSet withoutExistingMask, BitSet notWithMask) + internal EntitySetQuery(BitSet withImmediateMask, BitSet withExistingMask, BitSet withoutImmediateMask, BitSet withoutExistingMask, BitSet notWithMask) { - WithPendingMask = withPendingMask; + WithImmediateMask = withImmediateMask; WithExistingMask = withExistingMask; - WithoutPendingMask = withoutPendingMask; + WithoutImmediateMask = withoutImmediateMask; WithoutExistingMask = withoutExistingMask; NotWithMask = notWithMask; } - public IEnumerable FilterEntities(IEnumerable entities, ComponentBitSet pendingBitLookup, ComponentBitSet existingBitLookup) + public IEnumerable FilterEntities(IEnumerable entities, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) { foreach (var entity in entities) { - var pendingBits = pendingBitLookup.EntityBitArray(entity.ID); + var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); var existingBits = existingBitLookup.EntityBitArray(entity.ID); - var pending = WithPendingMask.And(pendingBits); + var immediate = WithImmediateMask.And(immediateBits); var existing = WithExistingMask.And(existingBits); - var withCheck = pending.Or(existing).Or(NotWithMask); + var withCheck = immediate.Or(existing).Or(NotWithMask); - var pendingForbidden = WithoutPendingMask.And(pendingBits).Not(); + var immediateForbidden = WithoutImmediateMask.And(immediateBits).Not(); var existingForbidden = WithoutExistingMask.And(existingBits).Not(); - var withoutCheck = pendingForbidden.And(existingForbidden); + var withoutCheck = immediateForbidden.And(existingForbidden); if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; } } diff --git a/encompass-cs/Exceptions/IllegalWritePendingException.cs b/encompass-cs/Exceptions/IllegalWriteImmediateException.cs similarity index 56% rename from encompass-cs/Exceptions/IllegalWritePendingException.cs rename to encompass-cs/Exceptions/IllegalWriteImmediateException.cs index 3983c23..3d991af 100644 --- a/encompass-cs/Exceptions/IllegalWritePendingException.cs +++ b/encompass-cs/Exceptions/IllegalWriteImmediateException.cs @@ -1,10 +1,10 @@ -using System; +using System; namespace Encompass.Exceptions { - public class IllegalWritePendingException : Exception + public class IllegalWriteImmediateException : Exception { - public IllegalWritePendingException( + public IllegalWriteImmediateException( string format, params object[] args ) : base(string.Format(format, args)) { } diff --git a/encompass-cs/Exceptions/IllegalWritePendingTypeException.cs b/encompass-cs/Exceptions/IllegalWritePendingTypeException.cs index 19554be..3d388f0 100644 --- a/encompass-cs/Exceptions/IllegalWritePendingTypeException.cs +++ b/encompass-cs/Exceptions/IllegalWritePendingTypeException.cs @@ -2,9 +2,9 @@ using System; namespace Encompass.Exceptions { - public class IllegalWritePendingTypeException : Exception + public class IllegalWriteImmediateTypeException : Exception { - public IllegalWritePendingTypeException( + public IllegalWriteImmediateTypeException( string format, params object[] args ) : base(string.Format(format, args)) { } diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 2a1c782..e18bec1 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -141,9 +141,9 @@ namespace Encompass RegisterMessageTypes(engine.receiveTypes.Union(engine.sendTypes)); - foreach (var writePendingType in engine.writePendingTypes.Intersect(engine.readPendingTypes)) + foreach (var writeImmediateType in engine.writeImmediateTypes.Intersect(engine.readImmediateTypes)) { - throw new EngineSelfCycleException("Engine {0} both writes and reads pending Component {1}", engine.GetType().Name, writePendingType.Name); + throw new EngineSelfCycleException("Engine {0} both writes and reads immediate Component {1}", engine.GetType().Name, writeImmediateType.Name); } foreach (var messageType in messageReceiveTypes.Intersect(messageSendTypes)) @@ -151,17 +151,17 @@ namespace Encompass throw new EngineSelfCycleException("Engine {0} both receives and sends Message {1}", engine.GetType().Name, messageType.Name); } - if (messageSendTypes.Count > 0 || engine.writePendingTypes.Count > 0) + if (messageSendTypes.Count > 0 || engine.writeImmediateTypes.Count > 0) { senders.Add(engine); } - foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readPendingTypes)) + foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readImmediateTypes)) { AddComponentTypeToRegister(componentType); } - foreach (var receiveType in engine.receiveTypes.Union(engine.readPendingTypes)) + foreach (var receiveType in engine.receiveTypes.Union(engine.readImmediateTypes)) { if (!typeToReaders.ContainsKey(receiveType)) { @@ -214,7 +214,7 @@ namespace Encompass { foreach (var senderEngine in senders) { - foreach (var messageType in senderEngine.sendTypes.Union(senderEngine.writePendingTypes)) + foreach (var messageType in senderEngine.sendTypes.Union(senderEngine.writeImmediateTypes)) { if (typeToReaders.ContainsKey(messageType)) { diff --git a/test/ComponentTest.cs b/test/ComponentTest.cs index c7c1c78..ce2fd5a 100644 --- a/test/ComponentTest.cs +++ b/test/ComponentTest.cs @@ -97,7 +97,7 @@ namespace Tests } [Reads(typeof(MockComponent))] - [WritesPending(typeof(MockComponent))] + [WritesImmediate(typeof(MockComponent))] [Writes(typeof(MockComponent))] class OverwriteEngine : Engine { @@ -110,7 +110,7 @@ namespace Tests } } - [ReadsPending(typeof(MockComponent))] + [ReadsImmediate(typeof(MockComponent))] [Reads(typeof(MockComponent))] class ReadMockComponentEngine : Engine { @@ -193,7 +193,7 @@ namespace Tests } } - [WritesPending(typeof(MockComponent))] + [WritesImmediate(typeof(MockComponent))] [Receives(typeof(AddMockComponentMessage))] [Writes(typeof(MockComponent))] class AddMockComponentEngine : Engine @@ -207,7 +207,7 @@ namespace Tests } } - [ReadsPending(typeof(MockComponent))] + [ReadsImmediate(typeof(MockComponent))] class HasMockComponentEngine : Engine { private Entity entity; @@ -464,8 +464,8 @@ namespace Tests } [Receives(typeof(CheckHasMockComponentMessage))] - [ReadsPending(typeof(MockComponent))] - class CheckHasPendingMockComponentEngine : Engine + [ReadsImmediate(typeof(MockComponent))] + class CheckHasImmediateMockComponentEngine : Engine { public override void Update(double dt) { diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 3fc6a9a..fe174c8 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -625,7 +625,7 @@ namespace Tests } [Reads(typeof(MockComponent))] - [WritesPending(typeof(MockComponent))] + [WritesImmediate(typeof(MockComponent))] [Writes(typeof(MockComponent))] class AddAndRemoveMockComponentEngine : Engine { @@ -641,8 +641,8 @@ namespace Tests static Entity entityResult; - [ReadsPending(typeof(MockComponent))] - class GetEntityFromPendingReadComponents : Engine + [ReadsImmediate(typeof(MockComponent))] + class GetEntityFromImmediateReadComponents : Engine { public override void Update(double dt) { @@ -651,11 +651,11 @@ namespace Tests } [Test] - public void GetEntityFromPendingComponentID() + public void GetEntityFromImmediateComponentID() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new AddAndRemoveMockComponentEngine()); - worldBuilder.AddEngine(new GetEntityFromPendingReadComponents()); + worldBuilder.AddEngine(new GetEntityFromImmediateReadComponents()); var entity = worldBuilder.CreateEntity(); worldBuilder.SetComponent(entity, new MockComponent()); @@ -756,7 +756,7 @@ namespace Tests } [Receives(typeof(MockMessage))] - [WritesPending(typeof(MockComponent))] + [WritesImmediate(typeof(MockComponent))] [Writes(typeof(MockComponent), 1)] class ActivateComponentEngine : Engine { @@ -770,7 +770,7 @@ namespace Tests } } - [ReadsPending(typeof(MockComponent))] + [ReadsImmediate(typeof(MockComponent))] [Writes(typeof(MockComponent), 0)] class RemoveComponentEngine : Engine { @@ -1251,9 +1251,9 @@ namespace Tests } [Reads(typeof(MockComponent))] - [WritesPending(typeof(MockComponentB))] + [WritesImmediate(typeof(MockComponentB))] [Writes(typeof(MockComponentB), 0)] - class AddPendingComponentEngine : Engine + class AddImmediateComponentEngine : Engine { public override void Update(double dt) { @@ -1264,13 +1264,13 @@ namespace Tests } } - [ReadsPending(typeof(MockComponentB))] + [ReadsImmediate(typeof(MockComponentB))] [QueryWith(typeof(MockComponentB))] - class EntityQueryWithPendingComponentsEngine : Engine + class EntityQueryWithImmediateComponentsEngine : Engine { private List entities; - public EntityQueryWithPendingComponentsEngine(List entities) + public EntityQueryWithImmediateComponentsEngine(List entities) { this.entities = entities; } @@ -1283,7 +1283,7 @@ namespace Tests } [Test] - public void EntitiesWithPendingComponents() + public void EntitiesWithImmediateComponents() { var worldBuilder = new WorldBuilder(); @@ -1293,8 +1293,8 @@ namespace Tests worldBuilder.SetComponent(entity, new MockComponent()); var queriedEntities = new List(); - worldBuilder.AddEngine(new AddPendingComponentEngine()); - worldBuilder.AddEngine(new EntityQueryWithPendingComponentsEngine(queriedEntities)); + worldBuilder.AddEngine(new AddImmediateComponentEngine()); + worldBuilder.AddEngine(new EntityQueryWithImmediateComponentsEngine(queriedEntities)); var world = worldBuilder.Build(); @@ -1303,13 +1303,13 @@ namespace Tests queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entity }); } - [ReadsPending(typeof(MockComponentB))] + [ReadsImmediate(typeof(MockComponentB))] [QueryWithout(typeof(MockComponentB))] - class EntityQueryWithoutPendingComponentsEngine : Engine + class EntityQueryWithoutImmediateComponentsEngine : Engine { private List entities; - public EntityQueryWithoutPendingComponentsEngine(List entities) + public EntityQueryWithoutImmediateComponentsEngine(List entities) { this.entities = entities; } @@ -1322,7 +1322,7 @@ namespace Tests } [Test] - public void EntitiesWithoutPendingComponents() + public void EntitiesWithoutImmediateComponents() { var worldBuilder = new WorldBuilder(); @@ -1332,8 +1332,8 @@ namespace Tests worldBuilder.SetComponent(entity, new MockComponent()); var queriedEntities = new List(); - worldBuilder.AddEngine(new AddPendingComponentEngine()); - worldBuilder.AddEngine(new EntityQueryWithoutPendingComponentsEngine(queriedEntities)); + worldBuilder.AddEngine(new AddImmediateComponentEngine()); + worldBuilder.AddEngine(new EntityQueryWithoutImmediateComponentsEngine(queriedEntities)); var world = worldBuilder.Build(); @@ -1343,10 +1343,10 @@ namespace Tests } [Reads(typeof(MockComponentC), typeof(MockComponentD))] - [WritesPending(typeof(MockComponent), typeof(MockComponentB))] + [WritesImmediate(typeof(MockComponent), typeof(MockComponentB))] [Writes(typeof(MockComponent), 0)] [Writes(typeof(MockComponentB), 0)] - class ConditionallyAddPendingComponentsEngine : Engine + class ConditionallyAddImmediateComponentsEngine : Engine { public override void Update(double dt) { @@ -1363,14 +1363,14 @@ namespace Tests } } - [ReadsPending(typeof(MockComponent), typeof(MockComponentB))] + [ReadsImmediate(typeof(MockComponent), typeof(MockComponentB))] [QueryWith(typeof(MockComponent))] [QueryWithout(typeof(MockComponentB))] - class EntityQueryWithAndWithoutPendingComponentsEngine : Engine + class EntityQueryWithAndWithoutImmediateComponentsEngine : Engine { private List entities; - public EntityQueryWithAndWithoutPendingComponentsEngine(List entities) + public EntityQueryWithAndWithoutImmediateComponentsEngine(List entities) { this.entities = entities; } @@ -1384,7 +1384,7 @@ namespace Tests } [Test] - public void EntitiesWithAndWithoutPendingComponents() + public void EntitiesWithAndWithoutImmediateComponents() { var worldBuilder = new WorldBuilder(); @@ -1396,8 +1396,8 @@ namespace Tests worldBuilder.SetComponent(entityC, new MockComponentD()); var queriedEntities = new List(); - worldBuilder.AddEngine(new ConditionallyAddPendingComponentsEngine()); - worldBuilder.AddEngine(new EntityQueryWithAndWithoutPendingComponentsEngine(queriedEntities)); + worldBuilder.AddEngine(new ConditionallyAddImmediateComponentsEngine()); + worldBuilder.AddEngine(new EntityQueryWithAndWithoutImmediateComponentsEngine(queriedEntities)); var world = worldBuilder.Build(); @@ -1407,9 +1407,9 @@ namespace Tests } [Reads(typeof(MockComponentC))] - [WritesPending(typeof(MockComponentB))] + [WritesImmediate(typeof(MockComponentB))] [Writes(typeof(MockComponentB), 0)] - class ConditionallyAddPendingComponentEngine : Engine + class ConditionallyAddImmediateComponentEngine : Engine { public override void Update(double dt) { @@ -1420,14 +1420,14 @@ namespace Tests } } - [ReadsPending(typeof(MockComponentB))] + [ReadsImmediate(typeof(MockComponentB))] [Reads(typeof(MockComponent))] [QueryWith(typeof(MockComponent), typeof(MockComponentB))] - class EntityQueryWithPendingAndNonPendingComponents : Engine + class EntityQueryWithImmediateAndNonImmediateComponents : Engine { private List entities; - public EntityQueryWithPendingAndNonPendingComponents(List entities) + public EntityQueryWithImmediateAndNonImmediateComponents(List entities) { this.entities = entities; } @@ -1440,7 +1440,7 @@ namespace Tests } [Test] - public void EntitiesWithPendingAndNonPendingComponents() + public void EntitiesWithImmediateAndNonImmediateComponents() { var worldBuilder = new WorldBuilder(); @@ -1453,8 +1453,8 @@ namespace Tests worldBuilder.SetComponent(entityC, new MockComponentD()); var queriedEntities = new List(); - worldBuilder.AddEngine(new ConditionallyAddPendingComponentEngine()); - worldBuilder.AddEngine(new EntityQueryWithPendingAndNonPendingComponents(queriedEntities)); + worldBuilder.AddEngine(new ConditionallyAddImmediateComponentEngine()); + worldBuilder.AddEngine(new EntityQueryWithImmediateAndNonImmediateComponents(queriedEntities)); var world = worldBuilder.Build(); diff --git a/test/SpawnerTest.cs b/test/SpawnerTest.cs index a7f8158..c0c5f0c 100644 --- a/test/SpawnerTest.cs +++ b/test/SpawnerTest.cs @@ -19,7 +19,7 @@ namespace Tests } } - [WritesPending(typeof(TestComponent))] + [WritesImmediate(typeof(TestComponent))] [Writes(typeof(TestComponent))] class TestSpawner : Spawner { diff --git a/test/WorldBuilderTest.cs b/test/WorldBuilderTest.cs index 8e75f8a..2fd62fd 100644 --- a/test/WorldBuilderTest.cs +++ b/test/WorldBuilderTest.cs @@ -153,7 +153,7 @@ namespace Tests [Receives(typeof(SetMessage))] [Writes(typeof(AComponent), 0)] - [WritesPending(typeof(AComponent))] + [WritesImmediate(typeof(AComponent))] class AEngine : Engine { public override void Update(double dt) @@ -167,7 +167,7 @@ namespace Tests [Receives(typeof(SetMessage))] [Writes(typeof(AComponent), 1)] - [WritesPending(typeof(AComponent))] + [WritesImmediate(typeof(AComponent))] class BEngine : Engine { public override void Update(double dt) @@ -181,7 +181,7 @@ namespace Tests static AComponent resultComponent; - [ReadsPending(typeof(AComponent))] + [ReadsImmediate(typeof(AComponent))] class ReadComponentEngine : Engine { public override void Update(double dt) @@ -222,7 +222,7 @@ namespace Tests [Receives(typeof(SetMessage))] [Writes(typeof(AComponent))] - [WritesPending(typeof(AComponent))] + [WritesImmediate(typeof(AComponent))] [Encompass.DefaultWritePriority(4)] class AEngine : Engine { @@ -238,7 +238,7 @@ namespace Tests [Receives(typeof(SetMessage))] [Writes(typeof(AComponent), 3)] - [WritesPending(typeof(AComponent))] + [WritesImmediate(typeof(AComponent))] class BEngine : Engine { public override void Update(double dt) @@ -252,7 +252,7 @@ namespace Tests [Receives(typeof(SetMessage))] [Writes(typeof(AComponent), 2)] - [WritesPending(typeof(AComponent))] + [WritesImmediate(typeof(AComponent))] class CEngine : Engine { public override void Update(double dt) @@ -266,7 +266,7 @@ namespace Tests static AComponent resultComponent; - [ReadsPending(typeof(AComponent))] + [ReadsImmediate(typeof(AComponent))] class ReadComponentEngine : Engine { public override void Update(double dt) From 7029b9f52e50995b0a3efb923d033eb461449557 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Mon, 23 Dec 2019 20:15:01 -0800 Subject: [PATCH 18/37] tightening up component write logic --- encompass-cs/ComponentUpdateManager.cs | 17 ++++++++++++++--- encompass-cs/UberEngine.cs | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index a2d9849..db166d2 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -59,13 +59,24 @@ namespace Encompass internal bool RemoveImmediate(Entity entity, int priority) where TComponent : struct, IComponent { - UpToDateComponentStore.Remove(entity.ID, priority); - return immediateComponentStore.Remove(entity.ID, priority); + if (immediateComponentStore.Remove(entity.ID, priority)) + { + UpToDateComponentStore.Remove(entity.ID, priority); + return true; + } + + return false; } internal bool Remove(Entity entity, int priority) where TComponent : struct, IComponent { - return UpToDateComponentStore.Remove(entity.ID, priority); + if (existingComponentStore.Remove(entity.ID, priority)) + { + UpToDateComponentStore.Remove(entity.ID, priority); + return true; + } + + return false; } private void RegisterExistingOrImmediateComponentMessage(Entity entity, TComponent component) where TComponent : struct, IComponent diff --git a/encompass-cs/UberEngine.cs b/encompass-cs/UberEngine.cs index 96a74b7..7f8259d 100644 --- a/encompass-cs/UberEngine.cs +++ b/encompass-cs/UberEngine.cs @@ -47,9 +47,9 @@ namespace Encompass CallGenericMethod(type, "GetComponent", new Type[] { typeof(Entity) }, new object[] { Entity }); CallGenericMethod(type, "HasComponent", new Type[] { typeof(Entity) }, new object[] { Entity }); CallGenericMethod(type, "SomeComponent", null); - CallGenericMethod(type, "RemoveComponent", new Type[] { typeof(Entity) }, new object[] { Entity }); CallGenericMethod(type, "DestroyWith", null); CallGenericMethod(type, "DestroyAllWith", null); + CallGenericMethod(type, "RemoveComponent", new Type[] { typeof(Entity) }, new object[] { Entity }); } foreach (var type in _messageTypes) From b644f4fb4bd063d67975adc09fe9d8e60c747fe0 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Mon, 23 Dec 2019 20:36:12 -0800 Subject: [PATCH 19/37] add extra component store --- encompass-cs/ComponentUpdateManager.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index db166d2..b5b181c 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -8,12 +8,14 @@ namespace Encompass { private readonly ComponentStore existingComponentStore; private readonly ComponentStore immediateComponentStore; - public Dictionary TypeToIndex { get; } - + private readonly ComponentStore existingAndImmediateComponentStore; public ComponentStore UpToDateComponentStore { get; private set; } + public Dictionary TypeToIndex { get; } + public ComponentUpdateManager(Dictionary typeToIndex) { + existingAndImmediateComponentStore = new ComponentStore(typeToIndex); existingComponentStore = new ComponentStore(typeToIndex); immediateComponentStore = new ComponentStore(typeToIndex); UpToDateComponentStore = new ComponentStore(typeToIndex); @@ -22,6 +24,7 @@ namespace Encompass public void RegisterComponentType() where TComponent : struct, IComponent { + existingAndImmediateComponentStore.RegisterComponentType(); existingComponentStore.RegisterComponentType(); immediateComponentStore.RegisterComponentType(); UpToDateComponentStore.RegisterComponentType(); @@ -29,6 +32,7 @@ namespace Encompass internal void Clear() { + existingAndImmediateComponentStore.ClearAll(); existingComponentStore.ClearAll(); immediateComponentStore.ClearAll(); UpToDateComponentStore.ClearAll(); @@ -61,6 +65,7 @@ namespace Encompass { if (immediateComponentStore.Remove(entity.ID, priority)) { + existingAndImmediateComponentStore.Remove(entity.ID, priority); UpToDateComponentStore.Remove(entity.ID, priority); return true; } @@ -81,6 +86,7 @@ namespace Encompass private void RegisterExistingOrImmediateComponentMessage(Entity entity, TComponent component) where TComponent : struct, IComponent { + existingAndImmediateComponentStore.Set(entity.ID, component); UpToDateComponentStore.Set(entity.ID, component); } @@ -93,7 +99,7 @@ namespace Encompass internal IEnumerable<(TComponent, int)> ReadExistingAndImmediateComponentsByType() where TComponent : struct, IComponent { - return UpToDateComponentStore.All(); + return existingAndImmediateComponentStore.All(); } internal IEnumerable<(TComponent, int)> ReadExistingComponentsByType() where TComponent : struct, IComponent @@ -136,7 +142,7 @@ namespace Encompass internal bool SomeExistingOrImmediateComponent() where TComponent : struct, IComponent { - return UpToDateComponentStore.Any(); + return existingAndImmediateComponentStore.Any(); } internal bool SomeExistingComponent() where TComponent : struct, IComponent @@ -165,12 +171,12 @@ namespace Encompass internal bool HasExistingOrImmediateComponent(Entity entity) where TComponent : struct, IComponent { - return UpToDateComponentStore.Has(entity.ID); + return existingAndImmediateComponentStore.Has(entity.ID); } internal bool HasExistingOrImmediateComponent(Entity entity, Type type) { - return UpToDateComponentStore.Has(type, entity.ID); + return existingAndImmediateComponentStore.Has(type, entity.ID); } internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent From 1a17f9a8e1f41787d908447ba9b709c31f006220 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Thu, 26 Dec 2019 20:18:46 -0800 Subject: [PATCH 20/37] changes to component update handling --- encompass-cs/ComponentUpdateManager.cs | 18 +++--- encompass-cs/Engine.cs | 13 +---- test/EngineTest.cs | 77 ++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index b5b181c..ea6d521 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -45,16 +45,17 @@ namespace Encompass internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - RegisterExistingOrImmediateComponentMessage(entity, component); - + existingAndImmediateComponentStore.Set(entity.ID, component); existingComponentStore.Set(entity.ID, component); + UpToDateComponentStore.Set(entity.ID, component); } internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { if (immediateComponentStore.Set(entity.ID, component, priority)) { - RegisterExistingOrImmediateComponentMessage(entity, component); + existingAndImmediateComponentStore.Set(entity.ID, component); + UpToDateComponentStore.Set(entity.ID, component); return true; } @@ -84,12 +85,6 @@ namespace Encompass return false; } - private void RegisterExistingOrImmediateComponentMessage(Entity entity, TComponent component) where TComponent : struct, IComponent - { - existingAndImmediateComponentStore.Set(entity.ID, component); - UpToDateComponentStore.Set(entity.ID, component); - } - public bool UpdateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { return UpToDateComponentStore.Set(entity.ID, component, priority); @@ -157,6 +152,11 @@ namespace Encompass // read components by entity and type + internal TComponent ReadImmediateOrExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return existingAndImmediateComponentStore.Get(entity.ID); + } + internal TComponent ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { return existingComponentStore.Get(entity.ID); diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 8d32f2d..79e8694 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -316,18 +316,7 @@ namespace Encompass var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && immediateRead) { - if (componentUpdateManager.HasImmediateComponent(entity)) - { - return componentUpdateManager.ReadImmediateComponentByEntityAndType(entity); - } - else if (componentUpdateManager.HasExistingComponent(entity)) - { - return componentUpdateManager.ReadExistingComponentByEntityAndType(entity); - } - else - { - throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID); - } + return componentUpdateManager.ReadImmediateOrExistingComponentByEntityAndType(entity); } else if (existingRead) { diff --git a/test/EngineTest.cs b/test/EngineTest.cs index fe174c8..c518f1b 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1462,6 +1462,83 @@ namespace Tests queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB }); } + + [ReadsImmediate(typeof(MockComponentB))] + class ReadImmediateComponentsEngine : Engine + { + private List _components; + + public ReadImmediateComponentsEngine(List components) + { + _components = components; + } + public override void Update(double dt) + { + _components.AddRange(ReadComponents()); + } + } + + [Test] + public void ReadImmediateComponents() + { + var worldBuilder = new WorldBuilder(); + + var _components = new List(); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.SetComponent(entity, new MockComponent()); + + worldBuilder.AddEngine(new AddImmediateComponentEngine()); + worldBuilder.AddEngine(new ReadImmediateComponentsEngine(_components)); + + var world = worldBuilder.Build(); + world.Update(0.01); + + _components.Should().NotBeEmpty(); + } + + [ReadsImmediate(typeof(MockComponentB))] + [Reads(typeof(MockComponent))] + class HasAndGetImmediateComponentEngine : Engine + { + private List _components; + + public HasAndGetImmediateComponentEngine(List components) + { + _components = components; + } + + public override void Update(double dt) + { + foreach (var (component, entity) in ReadComponentsIncludingEntity()) + { + if (HasComponent(entity)) + { + _components.Add(GetComponent(entity)); + + } + } + } + } + + [Test] + public void HasAndGetImmediateComponent() + { + var worldBuilder = new WorldBuilder(); + + var _components = new List(); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.SetComponent(entity, new MockComponent()); + + worldBuilder.AddEngine(new AddImmediateComponentEngine()); + worldBuilder.AddEngine(new HasAndGetImmediateComponentEngine(_components)); + + var world = worldBuilder.Build(); + world.Update(0.01); + + _components.Should().NotBeEmpty(); + } } } } From e3343e4a00bcf6b61308f8480067d8dfe0c10b31 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Thu, 26 Dec 2019 21:25:24 -0800 Subject: [PATCH 21/37] fix non immediate component remove --- encompass-cs/ComponentManager.cs | 4 +- encompass-cs/ComponentUpdateManager.cs | 8 +--- test/EngineTest.cs | 52 ++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index c8c0cc9..5518bba 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -97,14 +97,12 @@ namespace Encompass return false; } - public bool Remove(Entity entity, int priority) where TComponent : struct, IComponent + public void Remove(Entity entity, int priority) where TComponent : struct, IComponent { if (componentUpdateManager.Remove(entity, priority)) { drawLayerManager.UnRegisterComponentWithLayer(entity.ID); - return true; } - return false; } } } diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index ea6d521..49ec6eb 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -76,13 +76,7 @@ namespace Encompass internal bool Remove(Entity entity, int priority) where TComponent : struct, IComponent { - if (existingComponentStore.Remove(entity.ID, priority)) - { - UpToDateComponentStore.Remove(entity.ID, priority); - return true; - } - - return false; + return UpToDateComponentStore.Remove(entity.ID, priority); } public bool UpdateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent diff --git a/test/EngineTest.cs b/test/EngineTest.cs index c518f1b..a240adf 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1539,6 +1539,58 @@ namespace Tests _components.Should().NotBeEmpty(); } + + struct MockTimerComponent : IComponent + { + public MockTimerComponent(double time) + { + Timer = time; + } + + public double Timer { get; set; } + } + + [Reads(typeof(MockTimerComponent))] + [Writes(typeof(MockTimerComponent))] + class ReadWhileRemovingComponentsEngine : Engine + { + public override void Update(double dt) + { + foreach (var (component, entity) in ReadComponentsIncludingEntity()) + { + var updatedComponent = component; + updatedComponent.Timer -= dt; + + if (updatedComponent.Timer <= 0) + { + RemoveComponent(entity); + + } + else + { + SetComponent(entity, updatedComponent); + } + } + } + } + + [Test] + public void ReadWhileRemovingComponents() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.SetComponent(entity, new MockTimerComponent(0.5)); + + var entityB = worldBuilder.CreateEntity(); + worldBuilder.SetComponent(entityB, new MockTimerComponent(0.4)); + + worldBuilder.AddEngine(new ReadWhileRemovingComponentsEngine()); + + var world = worldBuilder.Build(); + Assert.DoesNotThrow(() => world.Update(0.2)); + Assert.DoesNotThrow(() => world.Update(0.25)); + } } } } From 41e9dd44519fabbf359bb6d8441cd43b1bc257d9 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sat, 28 Dec 2019 13:53:02 -0800 Subject: [PATCH 22/37] tracking for non-immediate components --- encompass-cs/ComponentManager.cs | 2 + encompass-cs/Engine.cs | 40 ++++++++++++++---- encompass-cs/EntitySetQuery.cs | 33 +++++++++------ encompass-cs/TrackingManager.cs | 72 ++++++++++++++++++++++++++++++++ encompass-cs/World.cs | 4 ++ encompass-cs/WorldBuilder.cs | 15 +++++++ test/EngineTest.cs | 14 +++---- 7 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 encompass-cs/TrackingManager.cs diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 5518bba..47935bc 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -11,6 +11,8 @@ namespace Encompass private readonly ComponentStore componentStore; private readonly HashSet entitiesMarkedForRemoval = new HashSet(); + internal ComponentBitSet ComponentBitSet { get { return componentStore.ComponentBitSet; } } + public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager, Dictionary typeToIndex) { this.drawLayerManager = drawLayerManager; diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 79e8694..8bda3e9 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -38,9 +38,13 @@ namespace Encompass private ComponentManager componentManager; private ComponentUpdateManager componentUpdateManager; private TimeManager timeManager; + private TrackingManager trackingManager; private EntitySetQuery entityQuery; + private HashSet _trackedEntities = new HashSet(); + protected IEnumerable TrackedEntities { get { return _trackedEntities; } } + protected Engine() { ID = Guid.NewGuid(); @@ -146,6 +150,11 @@ namespace Encompass this.timeManager = timeManager; } + internal void AssignTrackingManager(TrackingManager trackingManager) + { + this.trackingManager = trackingManager; + } + /// /// Runs once per World update with the calculated delta-time. /// @@ -422,13 +431,18 @@ namespace Encompass bool written; if (writeImmediateTypes.Contains(typeof(TComponent))) { - written = AddImmediateComponent(entity, component, priority); + written = componentUpdateManager.AddImmediateComponent(entity, component, priority); } else { written = componentUpdateManager.UpdateComponent(entity, component, priority); } + if (!componentUpdateManager.HasExistingComponent(entity)) + { + trackingManager.RegisterAddition(entity, typeof(TComponent)); + } + if (written && component is IDrawableComponent drawableComponent) { componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer); @@ -474,11 +488,6 @@ namespace Encompass componentUpdateManager.AddExistingComponent(entity, component); } - internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent - { - return componentUpdateManager.AddImmediateComponent(entity, component, priority); - } - /// /// Reads all messages of the specified Type. /// @@ -574,6 +583,11 @@ namespace Encompass { componentManager.Remove(entity, priority); } + + if (componentUpdateManager.HasExistingComponent(entity)) + { + trackingManager.RegisterRemoval(entity, typeof(TComponent)); + } } /// @@ -636,11 +650,19 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } - protected IEnumerable QueryEntities() + internal void CheckTrackEntity(Entity entity) { - foreach (var entity in entityQuery.FilterEntities(entityManager.Entities, componentUpdateManager.ImmediateBits, componentUpdateManager.ExistingBits)) + if (entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) { - yield return entity; + _trackedEntities.Add(entity); + } + } + + internal void CheckUntrackEntity(Entity entity) + { + if (!entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) + { + _trackedEntities.Remove(entity); } } diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index afcd9ae..663766f 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -21,23 +21,30 @@ namespace Encompass NotWithMask = notWithMask; } - public IEnumerable FilterEntities(IEnumerable entities, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) + public bool CheckEntity(Entity entity, ComponentBitSet componentBitSet) { - foreach (var entity in entities) - { - var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); - var existingBits = existingBitLookup.EntityBitArray(entity.ID); + var existingBits = componentBitSet.EntityBitArray(entity.ID); + var existing = WithExistingMask.And(existingBits).Or(NotWithMask); - var immediate = WithImmediateMask.And(immediateBits); - var existing = WithExistingMask.And(existingBits); - var withCheck = immediate.Or(existing).Or(NotWithMask); + var existingForbidden = WithoutExistingMask.And(existingBits).Not(); - var immediateForbidden = WithoutImmediateMask.And(immediateBits).Not(); - var existingForbidden = WithoutExistingMask.And(existingBits).Not(); - var withoutCheck = immediateForbidden.And(existingForbidden); + return existing.And(existingForbidden).AllTrue(); + } - if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; } - } + public bool CheckEntityImmediate(Entity entity, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) + { + var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); + var existingBits = existingBitLookup.EntityBitArray(entity.ID); + + var immediate = WithImmediateMask.And(immediateBits); + var existing = WithExistingMask.And(existingBits); + var withCheck = immediate.Or(existing).Or(NotWithMask); + + var immediateForbidden = WithoutImmediateMask.And(immediateBits).Not(); + var existingForbidden = WithoutExistingMask.And(existingBits).Not(); + var withoutCheck = immediateForbidden.And(existingForbidden); + + return withCheck.And(withoutCheck).AllTrue(); } } } diff --git a/encompass-cs/TrackingManager.cs b/encompass-cs/TrackingManager.cs new file mode 100644 index 0000000..35eaf2d --- /dev/null +++ b/encompass-cs/TrackingManager.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace Encompass +{ + internal class TrackingManager + { + private Dictionary> _pendingComponentTypesToEngines = new Dictionary>(); + private Dictionary> _componentTypesToEngines = new Dictionary>(); + + private HashSet<(Entity, Type)> _additions = new HashSet<(Entity, Type)>(); + private HashSet<(Entity, Type)> _removals = new HashSet<(Entity, Type)>(); + + public void RegisterComponentTypeToEngine(Type type, Engine engine) + { + if (!_componentTypesToEngines.ContainsKey(type)) { _componentTypesToEngines.Add(type, new HashSet()); } + _componentTypesToEngines[type].Add(engine); + } + + public void RegisterAddition(Entity entity, Type type) + { + _additions.Add((entity, type)); + } + + public void RegisterRemoval(Entity entity, Type type) + { + _removals.Add((entity, type)); + } + + public void InitializeTracking(IEnumerable entities) + { + foreach (var entity in entities) + { + foreach (var engineSet in _componentTypesToEngines.Values) + { + foreach (var engine in engineSet) + { + engine.CheckTrackEntity(entity); + } + } + } + } + + public void UpdateTracking() + { + // TODO: optimize so we only check each entity/engine pair once + foreach (var (entity, componentType) in _additions) + { + if (_componentTypesToEngines.ContainsKey(componentType)) + { + foreach (var engine in _componentTypesToEngines[componentType]) + { + engine.CheckTrackEntity(entity); + } + } + } + _additions.Clear(); + + foreach (var (entity, componentType) in _removals) + { + if (_componentTypesToEngines.ContainsKey(componentType)) + { + foreach (var engine in _componentTypesToEngines[componentType]) + { + engine.CheckUntrackEntity(entity); + } + } + } + _removals.Clear(); + } + } +} diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 1154ca3..0e3707b 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -10,6 +10,7 @@ namespace Encompass private readonly List enginesInOrder; private readonly EntityManager entityManager; private readonly ComponentManager componentManager; + private readonly TrackingManager trackingManager; private readonly MessageManager messageManager; private readonly ComponentUpdateManager componentUpdateManager; private readonly TimeManager timeManager; @@ -19,6 +20,7 @@ namespace Encompass List enginesInOrder, EntityManager entityManager, ComponentManager componentManager, + TrackingManager trackingManager, MessageManager messageManager, ComponentUpdateManager componentUpdateManager, TimeManager timeManager, @@ -28,6 +30,7 @@ namespace Encompass this.enginesInOrder = enginesInOrder; this.entityManager = entityManager; this.componentManager = componentManager; + this.trackingManager = trackingManager; this.messageManager = messageManager; this.componentUpdateManager = componentUpdateManager; this.timeManager = timeManager; @@ -40,6 +43,7 @@ namespace Encompass /// The time in seconds that has passed since the previous frame. public void Update(double dt) { + trackingManager.UpdateTracking(); messageManager.ProcessDelayedMessages(dt); timeManager.Update(dt); diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index e18bec1..98326e5 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -31,6 +31,7 @@ namespace Encompass private readonly TimeManager timeManager; private readonly DrawLayerManager drawLayerManager; private readonly RenderManager renderManager; + private readonly TrackingManager trackingManager; private readonly Dictionary> typeToReaders = new Dictionary>(); @@ -52,6 +53,7 @@ namespace Encompass messageManager = new MessageManager(timeManager); entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(entityManager, drawLayerManager); + trackingManager = new TrackingManager(); startingComponentStoreForComponentManager = new ComponentStore(typeToIndex); startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex); @@ -132,6 +134,7 @@ namespace Encompass engine.AssignMessageManager(messageManager); engine.AssignComponentUpdateManager(componentUpdateManager); engine.AssignTimeManager(timeManager); + engine.AssignTrackingManager(trackingManager); engines.Add(engine); engineGraph.AddNode(engine); @@ -156,6 +159,11 @@ namespace Encompass senders.Add(engine); } + foreach (var componentType in engine.queryWithTypes.Union(engine.queryWithoutTypes)) + { + trackingManager.RegisterComponentTypeToEngine(componentType, engine); + } + foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readImmediateTypes)) { AddComponentTypeToRegister(componentType); @@ -381,6 +389,7 @@ namespace Encompass engineOrder, entityManager, componentManager, + trackingManager, messageManager, componentUpdateManager, timeManager, @@ -390,6 +399,8 @@ namespace Encompass componentUpdateManager.SetStartingComponentStore(startingComponentStoreForComponentUpdateManager); componentManager.SetComponentStore(startingComponentStoreForComponentManager); + trackingManager.InitializeTracking(entityManager.Entities); + return world; } @@ -406,6 +417,7 @@ namespace Encompass var dummyDrawLayerManager = new DrawLayerManager(typeToIndex); var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex); var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex); + var dummyTrackingManager = new TrackingManager(); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager); @@ -418,6 +430,7 @@ namespace Encompass uberEngine.AssignMessageManager(dummyMessageManager); uberEngine.AssignComponentUpdateManager(dummyComponentUpdateManager); uberEngine.AssignTimeManager(dummyTimeManager); + uberEngine.AssignTrackingManager(dummyTrackingManager); var uberRenderer = new UberRenderer(componentTypes); uberRenderer.AssignComponentManager(dummyComponentManager); @@ -446,6 +459,7 @@ namespace Encompass emitterEngine.AssignMessageManager(dummyMessageManager); emitterEngine.AssignComponentUpdateManager(dummyComponentUpdateManager); emitterEngine.AssignTimeManager(dummyTimeManager); + emitterEngine.AssignTrackingManager(dummyTrackingManager); prepEngineOrder.Add(emitterEngine); } @@ -456,6 +470,7 @@ namespace Encompass prepEngineOrder, dummyEntityManager, dummyComponentManager, + dummyTrackingManager, dummyMessageManager, dummyComponentUpdateManager, dummyTimeManager, diff --git a/test/EngineTest.cs b/test/EngineTest.cs index a240adf..c1eb422 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1116,7 +1116,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - foreach (var entity in QueryEntities()) + foreach (var entity in TrackedEntities) { entities.Add(entity); } @@ -1164,7 +1164,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1211,7 +1211,7 @@ namespace Tests { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1278,7 +1278,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1317,7 +1317,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1379,7 +1379,7 @@ namespace Tests { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1435,7 +1435,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } From 9b0bf34c7325fdaa4e597675b96cad7e52f51e0d Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sat, 28 Dec 2019 14:30:26 -0800 Subject: [PATCH 23/37] immediate tracking --- encompass-cs/Engine.cs | 25 ++++++++++++++++++----- encompass-cs/EntitySetQuery.cs | 2 +- encompass-cs/TrackingManager.cs | 35 +++++++++++++++++++++++++++++---- encompass-cs/WorldBuilder.cs | 4 ++++ test/EngineTest.cs | 17 +++++++++++++++- 5 files changed, 72 insertions(+), 11 deletions(-) diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 8bda3e9..c37a54a 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -432,6 +432,10 @@ namespace Encompass if (writeImmediateTypes.Contains(typeof(TComponent))) { written = componentUpdateManager.AddImmediateComponent(entity, component, priority); + if (written) + { + trackingManager.ImmediateUpdateTracking(entity, typeof(TComponent)); + } } else { @@ -577,7 +581,10 @@ namespace Encompass if (writeImmediateTypes.Contains(typeof(TComponent))) { - componentManager.RemoveImmediate(entity, priority); + if (componentManager.RemoveImmediate(entity, priority)) + { + trackingManager.ImmediateUpdateTracking(entity, typeof(TComponent)); + } } else { @@ -650,20 +657,28 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } - internal void CheckTrackEntity(Entity entity) + internal void CheckAndUpdateTracking(Entity entity) { - if (entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) + if (_trackedEntities.Contains(entity) && !entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) + { + _trackedEntities.Remove(entity); + } + else if (!_trackedEntities.Contains(entity) && entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) { _trackedEntities.Add(entity); } } - internal void CheckUntrackEntity(Entity entity) + internal void ImmediateCheckAndUpdateTracking(Entity entity) { - if (!entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) + if (_trackedEntities.Contains(entity) && !entityQuery.ImmediateCheckEntity(entity, componentUpdateManager.ImmediateBits, componentUpdateManager.ExistingBits)) { _trackedEntities.Remove(entity); } + else if (!_trackedEntities.Contains(entity) && entityQuery.ImmediateCheckEntity(entity, componentUpdateManager.ImmediateBits, componentUpdateManager.ExistingBits)) + { + _trackedEntities.Add(entity); + } } /// diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 663766f..02f903b 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -31,7 +31,7 @@ namespace Encompass return existing.And(existingForbidden).AllTrue(); } - public bool CheckEntityImmediate(Entity entity, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) + public bool ImmediateCheckEntity(Entity entity, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) { var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); var existingBits = existingBitLookup.EntityBitArray(entity.ID); diff --git a/encompass-cs/TrackingManager.cs b/encompass-cs/TrackingManager.cs index 35eaf2d..1651719 100644 --- a/encompass-cs/TrackingManager.cs +++ b/encompass-cs/TrackingManager.cs @@ -5,18 +5,26 @@ namespace Encompass { internal class TrackingManager { - private Dictionary> _pendingComponentTypesToEngines = new Dictionary>(); + private Dictionary> _immediateComponentTypesToEngines = new Dictionary>(); private Dictionary> _componentTypesToEngines = new Dictionary>(); private HashSet<(Entity, Type)> _additions = new HashSet<(Entity, Type)>(); private HashSet<(Entity, Type)> _removals = new HashSet<(Entity, Type)>(); + private HashSet<(Entity, Engine)> _pairsToCheck = new HashSet<(Entity, Engine)>(); + public void RegisterComponentTypeToEngine(Type type, Engine engine) { if (!_componentTypesToEngines.ContainsKey(type)) { _componentTypesToEngines.Add(type, new HashSet()); } _componentTypesToEngines[type].Add(engine); } + public void RegisterImmediateComponentTypeToEngine(Type type, Engine engine) + { + if (!_immediateComponentTypesToEngines.ContainsKey(type)) { _immediateComponentTypesToEngines.Add(type, new HashSet()); } + _immediateComponentTypesToEngines[type].Add(engine); + } + public void RegisterAddition(Entity entity, Type type) { _additions.Add((entity, type)); @@ -35,12 +43,23 @@ namespace Encompass { foreach (var engine in engineSet) { - engine.CheckTrackEntity(entity); + engine.CheckAndUpdateTracking(entity); } } } } + public void ImmediateUpdateTracking(Entity entity, Type componentType) + { + if (_immediateComponentTypesToEngines.ContainsKey(componentType)) + { + foreach (var engine in _componentTypesToEngines[componentType]) + { + engine.ImmediateCheckAndUpdateTracking(entity); + } + } + } + public void UpdateTracking() { // TODO: optimize so we only check each entity/engine pair once @@ -50,7 +69,8 @@ namespace Encompass { foreach (var engine in _componentTypesToEngines[componentType]) { - engine.CheckTrackEntity(entity); + _pairsToCheck.Add((entity, engine)); + // engine.CheckTrackEntity(entity); } } } @@ -62,11 +82,18 @@ namespace Encompass { foreach (var engine in _componentTypesToEngines[componentType]) { - engine.CheckUntrackEntity(entity); + _pairsToCheck.Add((entity, engine)); + //engine.CheckUntrackEntity(entity); } } } _removals.Clear(); + + foreach (var (entity, engine) in _pairsToCheck) + { + engine.CheckAndUpdateTracking(entity); + } + _pairsToCheck.Clear(); } } } diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 98326e5..ec31c39 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -162,6 +162,10 @@ namespace Encompass foreach (var componentType in engine.queryWithTypes.Union(engine.queryWithoutTypes)) { trackingManager.RegisterComponentTypeToEngine(componentType, engine); + if (engine.readImmediateTypes.Contains(componentType)) + { + trackingManager.RegisterImmediateComponentTypeToEngine(componentType, engine); + } } foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readImmediateTypes)) diff --git a/test/EngineTest.cs b/test/EngineTest.cs index c1eb422..1836d1d 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1103,6 +1103,7 @@ namespace Tests struct MockComponentD : IComponent { } [Reads(typeof(MockComponent), typeof(MockComponentB))] + [Writes(typeof(MockComponentB))] [QueryWith(typeof(MockComponent), typeof(MockComponentB))] class EntityQueryWithComponentsEngine : Engine { @@ -1119,6 +1120,7 @@ namespace Tests foreach (var entity in TrackedEntities) { entities.Add(entity); + RemoveComponent(entity); } } } @@ -1148,9 +1150,14 @@ namespace Tests world.Update(0.01); queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB }); + + world.Update(0.01); + + queriedEntities.Should().BeEmpty(); } [Reads(typeof(MockComponent))] + [Writes(typeof(MockComponent))] [QueryWithout(typeof(MockComponent))] class EntityQueryWithoutComponentsEngine : Engine { @@ -1164,7 +1171,11 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(TrackedEntities); + foreach (var entity in TrackedEntities) + { + entities.Add(entity); + SetComponent(entity, new MockComponent()); + } } } @@ -1193,6 +1204,10 @@ namespace Tests world.Update(0.01); queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityC }); + + world.Update(0.01); + + queriedEntities.Should().BeEmpty(); } [Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))] From 936b97c4eca9acf4c9af67e4ef1aa4a901c11d99 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sat, 28 Dec 2019 15:08:07 -0800 Subject: [PATCH 24/37] better error message when component is not found on entity --- .../Collections/TypedComponentStore.cs | 1 + .../Exceptions/ComponentNotFoundException.cs | 12 --- .../ComponentTypeMismatchException.cs | 12 --- .../RepeatUpdateComponentException.cs | 12 --- test/EngineTest.cs | 83 ++++++++++++++++++- 5 files changed, 83 insertions(+), 37 deletions(-) delete mode 100644 encompass-cs/Exceptions/ComponentNotFoundException.cs delete mode 100644 encompass-cs/Exceptions/ComponentTypeMismatchException.cs delete mode 100644 encompass-cs/Exceptions/RepeatUpdateComponentException.cs diff --git a/encompass-cs/Collections/TypedComponentStore.cs b/encompass-cs/Collections/TypedComponentStore.cs index 07db0f1..89dfd8f 100644 --- a/encompass-cs/Collections/TypedComponentStore.cs +++ b/encompass-cs/Collections/TypedComponentStore.cs @@ -22,6 +22,7 @@ namespace Encompass public TComponent Get(int entityID) { + if (!store.ContainsKey(entityID)) { throw new Exceptions.NoComponentOfTypeOnEntityException("No component of type {0} exists on Entity with ID {1}", typeof(TComponent), entityID); } return store[entityID]; } diff --git a/encompass-cs/Exceptions/ComponentNotFoundException.cs b/encompass-cs/Exceptions/ComponentNotFoundException.cs deleted file mode 100644 index 5f1a988..0000000 --- a/encompass-cs/Exceptions/ComponentNotFoundException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Encompass.Exceptions -{ - public class ComponentNotFoundException : Exception - { - public ComponentNotFoundException( - string format, - params object[] args - ) : base(string.Format(format, args)) { } - } -} diff --git a/encompass-cs/Exceptions/ComponentTypeMismatchException.cs b/encompass-cs/Exceptions/ComponentTypeMismatchException.cs deleted file mode 100644 index 17caf96..0000000 --- a/encompass-cs/Exceptions/ComponentTypeMismatchException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Encompass.Exceptions -{ - public class ComponentTypeMismatchException : Exception - { - public ComponentTypeMismatchException( - string format, - params object[] args - ) : base(string.Format(format, args)) { } - } -} diff --git a/encompass-cs/Exceptions/RepeatUpdateComponentException.cs b/encompass-cs/Exceptions/RepeatUpdateComponentException.cs deleted file mode 100644 index 4b178fb..0000000 --- a/encompass-cs/Exceptions/RepeatUpdateComponentException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Encompass.Exceptions -{ - public class RepeatUpdateComponentException : Exception - { - public RepeatUpdateComponentException( - string format, - params object[] args - ) : base(string.Format(format, args)) { } - } -} diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 1836d1d..e9068d9 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -833,7 +833,7 @@ namespace Tests worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine()); var entity = worldBuilder.CreateEntity(); - worldBuilder.SetComponent(entity, new MockComponent { }); + worldBuilder.SetComponent(entity, new MockComponent()); var world = worldBuilder.Build(); world.Update(0.01); @@ -841,7 +841,69 @@ namespace Tests entity.Should().BeEquivalentTo(readEntity); } + struct MockComponentB : IComponent + { + public MockComponentB(int value) + { + this.value = value; + } + int value; + } + + static MockComponentB getComponentResult; + + [Reads(typeof(MockComponent), typeof(MockComponentB))] + class GetComponentEngine : Engine + { + public override void Update(double dt) + { + getComponentResult = GetComponent(ReadEntity()); + } + } + + [Test] + public void GetComponent() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + + worldBuilder.SetComponent(entity, new MockComponent()); + worldBuilder.SetComponent(entity, new MockComponentB(3)); + worldBuilder.AddEngine(new GetComponentEngine()); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + getComponentResult.Should().BeEquivalentTo(new MockComponentB(3)); + } + + [Reads(typeof(MockComponent), typeof(MockComponentB))] + class GetComponentExceptionEngine : Engine + { + public override void Update(double dt) + { + foreach (var entity in ReadEntities()) + { + GetComponent(entity); + } + } + } + + [Test] + public void GetComponentWhenComponentIsNotOnEntity() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(new GetComponentExceptionEngine()); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.SetComponent(entity, new MockComponent()); + + var world = worldBuilder.Build(); + Assert.Throws(() => world.Update(0.01)); + } static Entity[] readEntities; @@ -1606,6 +1668,25 @@ namespace Tests Assert.DoesNotThrow(() => world.Update(0.2)); Assert.DoesNotThrow(() => world.Update(0.25)); } + + [Test] + public void DestroyedEntitiesAreRemovedFromTracking() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.SetComponent(entity, new MockComponent()); + + worldBuilder.AddEngine(new DestroyWithEngine()); + worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine()); + + var world = worldBuilder.Build(); + + world.Update(0.01); + world.Update(0.01); + + readEntities.Should().BeEmpty(); + } } } } From 05fa578652361790e954708390f7a2f15b49484a Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sat, 28 Dec 2019 16:16:21 -0800 Subject: [PATCH 25/37] untrack destroyed entities --- encompass-cs/Engine.cs | 5 +++++ encompass-cs/EntityManager.cs | 3 ++- encompass-cs/TrackingManager.cs | 2 -- encompass-cs/World.cs | 2 +- encompass-cs/WorldBuilder.cs | 4 ++-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index c37a54a..58bf6bb 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -718,5 +718,10 @@ namespace Encompass withMask.Not() ); } + + internal void RegisterDestroyedEntity(Entity entity) + { + _trackedEntities.Remove(entity); + } } } diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index 77a85c0..f80e5cc 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -64,10 +64,11 @@ namespace Encompass entitiesMarkedForDestroy.Add(entity); } - public void DestroyMarkedEntities() + public void DestroyMarkedEntities(IEnumerable engines) { foreach (var entity in entitiesMarkedForDestroy) { + foreach (var engine in engines) { engine.RegisterDestroyedEntity(entity); } componentManager.MarkAllComponentsOnEntityForRemoval(entity); IDs.Remove(entity.ID); idManager.Free(entity.ID); diff --git a/encompass-cs/TrackingManager.cs b/encompass-cs/TrackingManager.cs index 1651719..61600e4 100644 --- a/encompass-cs/TrackingManager.cs +++ b/encompass-cs/TrackingManager.cs @@ -70,7 +70,6 @@ namespace Encompass foreach (var engine in _componentTypesToEngines[componentType]) { _pairsToCheck.Add((entity, engine)); - // engine.CheckTrackEntity(entity); } } } @@ -83,7 +82,6 @@ namespace Encompass foreach (var engine in _componentTypesToEngines[componentType]) { _pairsToCheck.Add((entity, engine)); - //engine.CheckUntrackEntity(entity); } } } diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 0e3707b..a97ffe4 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -60,7 +60,7 @@ namespace Encompass } messageManager.ClearMessages(); - entityManager.DestroyMarkedEntities(); + entityManager.DestroyMarkedEntities(enginesInOrder); componentManager.WriteComponents(); componentManager.RemoveMarkedComponents(); diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index ec31c39..d85ed87 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -48,12 +48,12 @@ namespace Encompass this.entityCapacity = entityCapacity; drawLayerManager = new DrawLayerManager(typeToIndex); timeManager = new TimeManager(); + trackingManager = new TrackingManager(); componentUpdateManager = new ComponentUpdateManager(typeToIndex); componentManager = new ComponentManager(drawLayerManager, componentUpdateManager, typeToIndex); messageManager = new MessageManager(timeManager); entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(entityManager, drawLayerManager); - trackingManager = new TrackingManager(); startingComponentStoreForComponentManager = new ComponentStore(typeToIndex); startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex); @@ -419,9 +419,9 @@ namespace Encompass var dummyTimeManager = new TimeManager(); var dummyMessageManager = new MessageManager(dummyTimeManager); var dummyDrawLayerManager = new DrawLayerManager(typeToIndex); + var dummyTrackingManager = new TrackingManager(); var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex); var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex); - var dummyTrackingManager = new TrackingManager(); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager); From d4115b231cef9e0a1ed01eaccfc4f56a2d05d5bd Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sat, 28 Dec 2019 21:39:35 -0800 Subject: [PATCH 26/37] replace bitset --- encompass-cs/Collections/BitArrayPool.cs | 33 ---- encompass-cs/Collections/BitSet.cs | 182 -------------------- encompass-cs/Collections/ComponentBitSet.cs | 11 +- encompass-cs/Collections/ComponentStore.cs | 4 +- encompass-cs/Engine.cs | 20 +-- encompass-cs/EntitySetQuery.cs | 36 ++-- encompass-cs/encompass-cs.csproj | 9 +- test/BitSetTest.cs | 62 ------- 8 files changed, 39 insertions(+), 318 deletions(-) delete mode 100644 encompass-cs/Collections/BitArrayPool.cs delete mode 100644 encompass-cs/Collections/BitSet.cs delete mode 100644 test/BitSetTest.cs diff --git a/encompass-cs/Collections/BitArrayPool.cs b/encompass-cs/Collections/BitArrayPool.cs deleted file mode 100644 index 0f0165f..0000000 --- a/encompass-cs/Collections/BitArrayPool.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections; -using System.Collections.Generic; - -namespace Encompass -{ - internal class BitArrayPool - { - private Stack bitArrays; - - public BitArrayPool(int capacity) - { - bitArrays = new Stack(capacity); - - for (var i = 0; i < capacity; i++) - { - bitArrays.Push(new BitArray(128)); - } - } - - public BitArray Obtain(int size) - { - var bitArray = bitArrays.Pop(); - bitArray.Length = size; - bitArray.SetAll(false); - return bitArray; - } - - public void Free(BitArray bitArray) - { - bitArrays.Push(bitArray); - } - } -} diff --git a/encompass-cs/Collections/BitSet.cs b/encompass-cs/Collections/BitSet.cs deleted file mode 100644 index abe9960..0000000 --- a/encompass-cs/Collections/BitSet.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Numerics; - -namespace Encompass.Collections -{ - public static class BitSetBuilder - { - public static BitSet Zeroes() - { - return new BitSet(VectorBuilder.Zeroes(), VectorBuilder.Zeroes(), VectorBuilder.Zeroes(), VectorBuilder.Zeroes()); - } - - public static BitSet Ones() - { - return new BitSet(VectorBuilder.Ones(), VectorBuilder.Ones(), VectorBuilder.Ones(), VectorBuilder.Ones()); - } - } - - public static class VectorBuilder - { - static readonly uint[] zeroes = new uint[Vector.Count]; // max size of a Vector is 8 uints - static readonly uint[] ones = Enumerable.Repeat(uint.MaxValue, Vector.Count).ToArray(); - static uint[] builderInts = new uint[Vector.Count]; - - private static void ResetBuilder() - { - for (var i = 0; i < Vector.Count; i++) - { - builderInts[i] = 0; - } - } - - public static Vector Zeroes() - { - return new Vector(zeroes); - } - - public static Vector Ones() - { - return new Vector(ones); - } - - public static Vector Build(int index) - { - if (index > Vector.Count * 32) { throw new System.ArgumentOutOfRangeException(nameof(index)); } - ResetBuilder(); - builderInts[index / 32] |= (uint)(1 << (index % 32)); - return new Vector(builderInts); - } - } - - public struct BitSet - { - public static int VectorLength { get { return Vector.Count * 32; } } - - public Vector A { get; } - public Vector B { get; } - public Vector C { get; } - public Vector D { get; } - - internal BitSet(Vector a, Vector b, Vector c, Vector d) - { - A = a; - B = b; - C = c; - D = d; - } - - public BitSet And(BitSet other) - { - return new BitSet(A & other.A, B & other.B, C & other.C, D & other.D); - } - - public BitSet Or(BitSet other) - { - return new BitSet(A | other.A, B | other.B, C | other.C, D | other.D); - } - - public BitSet Not() - { - return new BitSet(~A, ~B, ~C, ~D); - } - - public BitSet Set(int index) - { - if (index < VectorLength) - { - return new BitSet(A | VectorBuilder.Build(index % VectorLength), B, C, D); - } - else if (index < VectorLength * 2) - { - return new BitSet(A, B | VectorBuilder.Build(index % VectorLength), C, D); - } - else if (index < VectorLength * 3) - { - return new BitSet(A, B, C | VectorBuilder.Build(index % VectorLength), D); - } - else if (index < VectorLength * 4) - { - return new BitSet(A, B, C, D | VectorBuilder.Build(index % VectorLength)); - } - else - { - throw new System.ArgumentOutOfRangeException(nameof(index)); - } - } - - public BitSet UnSet(int index) - { - if (index < VectorLength) - { - return new BitSet(A & ~VectorBuilder.Build(index % VectorLength), B, C, D); - } - else if (index < VectorLength * 2) - { - return new BitSet(A, B & ~VectorBuilder.Build(index % VectorLength), C, D); - } - else if (index < VectorLength * 3) - { - return new BitSet(A, B, C & ~VectorBuilder.Build(index % VectorLength), D); - } - else if (index < VectorLength * 4) - { - return new BitSet(A, B, C, D & ~VectorBuilder.Build(index % VectorLength)); - } - else - { - throw new System.ArgumentOutOfRangeException(nameof(index)); - } - } - - public bool Get(int index) - { - var vectorIndex = index % VectorLength; - if (index < VectorLength) - { - return (A[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else if (index < VectorLength * 2) - { - return (B[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else if (index < VectorLength * 3) - { - return (C[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else if (index < VectorLength * 4) - { - return (D[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0; - } - else - { - throw new System.ArgumentOutOfRangeException(nameof(index)); - } - } - - public bool AllTrue() - { - for (var i = 0; i < Vector.Count; i++) - { - if (A[i] != uint.MaxValue) { return false; } - if (B[i] != uint.MaxValue) { return false; } - if (C[i] != uint.MaxValue) { return false; } - if (D[i] != uint.MaxValue) { return false; } - } - return true; - } - - public bool AllFalse() - { - for (var i = 0; i < Vector.Count; i++) - { - if (A[i] != 0) { return false; } - if (B[i] != 0) { return false; } - if (C[i] != 0) { return false; } - if (D[i] != 0) { return false; } - } - return true; - } - } -} diff --git a/encompass-cs/Collections/ComponentBitSet.cs b/encompass-cs/Collections/ComponentBitSet.cs index c85f71f..1d0c39d 100644 --- a/encompass-cs/Collections/ComponentBitSet.cs +++ b/encompass-cs/Collections/ComponentBitSet.cs @@ -1,13 +1,12 @@ -using Encompass.Collections; +using MoonTools.FastCollections; using System; -using System.Collections; using System.Collections.Generic; namespace Encompass { internal class ComponentBitSet { - Dictionary entities = new Dictionary(); + Dictionary entities = new Dictionary(); Dictionary TypeToIndex { get; } public ComponentBitSet(Dictionary typeToIndex) @@ -22,7 +21,7 @@ namespace Encompass public void AddEntity(int entityID) { - entities.Add(entityID, BitSetBuilder.Zeroes()); + entities.Add(entityID, BitSet512.Zero); } public void Set(int entityID) where TComponent : struct, IComponent @@ -47,9 +46,9 @@ namespace Encompass } } - public BitSet EntityBitArray(int entityID) + public BitSet512 EntityBitArray(int entityID) { - return entities.ContainsKey(entityID) ? entities[entityID] : BitSetBuilder.Zeroes(); + return entities.ContainsKey(entityID) ? entities[entityID] : BitSet512.Zero; } } } diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 9e6aed6..03904b1 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -1,4 +1,4 @@ -using Encompass.Collections; +using MoonTools.FastCollections; using System; using System.Collections.Generic; @@ -47,7 +47,7 @@ namespace Encompass return Stores.ContainsKey(type) && Stores[type].Has(entityID); } - public BitSet EntityBitArray(int entityID) + public BitSet512 EntityBitArray(int entityID) { return ComponentBitSet.EntityBitArray(entityID); } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 58bf6bb..c748cea 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Collections.Generic; using System.Linq; using Encompass.Exceptions; -using Encompass.Collections; +using MoonTools.FastCollections; namespace Encompass { @@ -686,36 +686,36 @@ namespace Encompass /// internal void BuildEntityQuery() { - var withMask = BitSetBuilder.Zeroes(); + var withMask = BitSet512.Zero; foreach (var type in queryWithTypes) { withMask = withMask.Set(componentUpdateManager.TypeToIndex[type]); } - var withoutMask = BitSetBuilder.Zeroes(); + var withoutMask = BitSet512.Zero; foreach (var type in queryWithoutTypes) { withoutMask = withoutMask.Set(componentUpdateManager.TypeToIndex[type]); } - var immediateMask = BitSetBuilder.Zeroes(); + var immediateMask = BitSet512.Zero; foreach (var type in readImmediateTypes) { immediateMask = immediateMask.Set(componentUpdateManager.TypeToIndex[type]); } - var existingMask = BitSetBuilder.Zeroes(); + var existingMask = BitSet512.Zero; foreach (var type in readTypes) { existingMask = existingMask.Set(componentUpdateManager.TypeToIndex[type]); } entityQuery = new EntitySetQuery( - withMask.And(immediateMask), - withMask.And(existingMask), - withoutMask.And(immediateMask), - withoutMask.And(existingMask), - withMask.Not() + withMask & immediateMask, + withMask & existingMask, + withoutMask & immediateMask, + withoutMask & existingMask, + ~withMask ); } diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 02f903b..04c0432 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -1,18 +1,16 @@ -using System.Collections; -using System.Collections.Generic; -using Encompass.Collections; +using MoonTools.FastCollections; namespace Encompass { internal struct EntitySetQuery { - private BitSet WithImmediateMask { get; } - private BitSet WithExistingMask { get; } - private BitSet WithoutImmediateMask { get; } - private BitSet WithoutExistingMask { get; } - private BitSet NotWithMask { get; } + private BitSet512 WithImmediateMask { get; } + private BitSet512 WithExistingMask { get; } + private BitSet512 WithoutImmediateMask { get; } + private BitSet512 WithoutExistingMask { get; } + private BitSet512 NotWithMask { get; } - internal EntitySetQuery(BitSet withImmediateMask, BitSet withExistingMask, BitSet withoutImmediateMask, BitSet withoutExistingMask, BitSet notWithMask) + internal EntitySetQuery(BitSet512 withImmediateMask, BitSet512 withExistingMask, BitSet512 withoutImmediateMask, BitSet512 withoutExistingMask, BitSet512 notWithMask) { WithImmediateMask = withImmediateMask; WithExistingMask = withExistingMask; @@ -24,11 +22,11 @@ namespace Encompass public bool CheckEntity(Entity entity, ComponentBitSet componentBitSet) { var existingBits = componentBitSet.EntityBitArray(entity.ID); - var existing = WithExistingMask.And(existingBits).Or(NotWithMask); + var existing = (WithExistingMask & existingBits) | NotWithMask; - var existingForbidden = WithoutExistingMask.And(existingBits).Not(); + var existingForbidden = ~(WithoutExistingMask & existingBits); - return existing.And(existingForbidden).AllTrue(); + return (existing & existingForbidden).AllTrue(); } public bool ImmediateCheckEntity(Entity entity, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) @@ -36,15 +34,15 @@ namespace Encompass var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); var existingBits = existingBitLookup.EntityBitArray(entity.ID); - var immediate = WithImmediateMask.And(immediateBits); - var existing = WithExistingMask.And(existingBits); - var withCheck = immediate.Or(existing).Or(NotWithMask); + var immediate = WithImmediateMask & immediateBits; + var existing = WithExistingMask & existingBits; + var withCheck = immediate | existing | NotWithMask; - var immediateForbidden = WithoutImmediateMask.And(immediateBits).Not(); - var existingForbidden = WithoutExistingMask.And(existingBits).Not(); - var withoutCheck = immediateForbidden.And(existingForbidden); + var immediateForbidden = ~(WithoutImmediateMask & immediateBits); + var existingForbidden = ~(WithoutExistingMask & existingBits); + var withoutCheck = immediateForbidden & existingForbidden; - return withCheck.And(withoutCheck).AllTrue(); + return (withCheck & withoutCheck).AllTrue(); } } } diff --git a/encompass-cs/encompass-cs.csproj b/encompass-cs/encompass-cs.csproj index cb51bab..37a5ba2 100644 --- a/encompass-cs/encompass-cs.csproj +++ b/encompass-cs/encompass-cs.csproj @@ -9,7 +9,7 @@ Moonside Games Encompass ECS https://github.com/encompass-ecs - + Evan Hemsley 2019 Encompass is an engine-agnostic Hyper ECS framework to help you code games, or other kinds of simulations. true @@ -19,11 +19,12 @@ True - + - - + + + \ No newline at end of file diff --git a/test/BitSetTest.cs b/test/BitSetTest.cs deleted file mode 100644 index dfcfb6d..0000000 --- a/test/BitSetTest.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Encompass.Collections; -using FluentAssertions; -using NUnit.Framework; - -namespace Tests -{ - public class BitSetTest - { - [Test] - public void Zeroes() - { - var bitSet = BitSetBuilder.Zeroes(); - bitSet.AllFalse().Should().BeTrue(); - } - - [Test] - public void Ones() - { - var bitSet = BitSetBuilder.Ones(); - bitSet.AllTrue().Should().BeTrue(); - } - - [Test] - public void Set() - { - var bitSet = BitSetBuilder.Zeroes().Set(5); - bitSet.AllFalse().Should().BeFalse(); - - bitSet = BitSetBuilder.Zeroes().Set(132); - bitSet.AllFalse().Should().BeFalse(); - - bitSet = BitSetBuilder.Zeroes().Set(268); - bitSet.AllFalse().Should().BeFalse(); - - bitSet = BitSetBuilder.Zeroes().Set(450); - bitSet.AllFalse().Should().BeFalse(); - } - - [Test] - public void UnSet() - { - var bitSet = BitSetBuilder.Ones().UnSet(285); - bitSet.Get(285).Should().BeFalse(); - bitSet.Set(285).AllTrue().Should().BeTrue(); - } - - [Test] - public void Get() - { - var bitSet = BitSetBuilder.Zeroes().Set(359); - bitSet.Get(359).Should().BeTrue(); - bitSet.UnSet(359).AllFalse().Should().BeTrue(); - } - - [Test] - public void Not() - { - var bitSet = BitSetBuilder.Ones().Not(); - bitSet.AllFalse().Should().BeTrue(); - } - } -} From 28f2ba969aa110e6090070ddf4fb2c8880f4e177 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sat, 28 Dec 2019 22:20:17 -0800 Subject: [PATCH 27/37] remove unnecessary component store --- encompass-cs/ComponentUpdateManager.cs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 49ec6eb..26b08ed 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -1,21 +1,18 @@ using System; using System.Collections.Generic; -using System.Linq; namespace Encompass { internal class ComponentUpdateManager { - private readonly ComponentStore existingComponentStore; + private ComponentStore existingComponentStore; private readonly ComponentStore immediateComponentStore; - private readonly ComponentStore existingAndImmediateComponentStore; public ComponentStore UpToDateComponentStore { get; private set; } public Dictionary TypeToIndex { get; } public ComponentUpdateManager(Dictionary typeToIndex) { - existingAndImmediateComponentStore = new ComponentStore(typeToIndex); existingComponentStore = new ComponentStore(typeToIndex); immediateComponentStore = new ComponentStore(typeToIndex); UpToDateComponentStore = new ComponentStore(typeToIndex); @@ -24,7 +21,6 @@ namespace Encompass public void RegisterComponentType() where TComponent : struct, IComponent { - existingAndImmediateComponentStore.RegisterComponentType(); existingComponentStore.RegisterComponentType(); immediateComponentStore.RegisterComponentType(); UpToDateComponentStore.RegisterComponentType(); @@ -32,7 +28,6 @@ namespace Encompass internal void Clear() { - existingAndImmediateComponentStore.ClearAll(); existingComponentStore.ClearAll(); immediateComponentStore.ClearAll(); UpToDateComponentStore.ClearAll(); @@ -45,7 +40,6 @@ namespace Encompass internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - existingAndImmediateComponentStore.Set(entity.ID, component); existingComponentStore.Set(entity.ID, component); UpToDateComponentStore.Set(entity.ID, component); } @@ -54,7 +48,6 @@ namespace Encompass { if (immediateComponentStore.Set(entity.ID, component, priority)) { - existingAndImmediateComponentStore.Set(entity.ID, component); UpToDateComponentStore.Set(entity.ID, component); return true; } @@ -66,7 +59,6 @@ namespace Encompass { if (immediateComponentStore.Remove(entity.ID, priority)) { - existingAndImmediateComponentStore.Remove(entity.ID, priority); UpToDateComponentStore.Remove(entity.ID, priority); return true; } @@ -88,7 +80,7 @@ namespace Encompass internal IEnumerable<(TComponent, int)> ReadExistingAndImmediateComponentsByType() where TComponent : struct, IComponent { - return existingAndImmediateComponentStore.All(); + return UpToDateComponentStore.All(); } internal IEnumerable<(TComponent, int)> ReadExistingComponentsByType() where TComponent : struct, IComponent @@ -131,7 +123,7 @@ namespace Encompass internal bool SomeExistingOrImmediateComponent() where TComponent : struct, IComponent { - return existingAndImmediateComponentStore.Any(); + return UpToDateComponentStore.Any(); } internal bool SomeExistingComponent() where TComponent : struct, IComponent @@ -148,7 +140,7 @@ namespace Encompass internal TComponent ReadImmediateOrExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - return existingAndImmediateComponentStore.Get(entity.ID); + return UpToDateComponentStore.Get(entity.ID); } internal TComponent ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent @@ -165,12 +157,12 @@ namespace Encompass internal bool HasExistingOrImmediateComponent(Entity entity) where TComponent : struct, IComponent { - return existingAndImmediateComponentStore.Has(entity.ID); + return UpToDateComponentStore.Has(entity.ID); } internal bool HasExistingOrImmediateComponent(Entity entity, Type type) { - return existingAndImmediateComponentStore.Has(type, entity.ID); + return UpToDateComponentStore.Has(type, entity.ID); } internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent From a2d29a6591ba2e36495e31c58061aec1ae5d1e81 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 01:48:38 -0800 Subject: [PATCH 28/37] slight component manager optimization --- encompass-cs/ComponentUpdateManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 26b08ed..dc6c30d 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -30,7 +30,6 @@ namespace Encompass { existingComponentStore.ClearAll(); immediateComponentStore.ClearAll(); - UpToDateComponentStore.ClearAll(); } internal void SetStartingComponentStore(ComponentStore componentStore) @@ -41,7 +40,6 @@ namespace Encompass internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { existingComponentStore.Set(entity.ID, component); - UpToDateComponentStore.Set(entity.ID, component); } internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent From f7095ffdfb9b3297c9ba8b2631d6f8a582b9b431 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 13:24:51 -0800 Subject: [PATCH 29/37] refactor to remove ComponentUpdateManager --- encompass-cs/ComponentManager.cs | 180 +++++++++++++++++++---- encompass-cs/ComponentUpdateManager.cs | 189 ------------------------- encompass-cs/Engine.cs | 68 ++++----- encompass-cs/Renderer.cs | 2 +- encompass-cs/World.cs | 7 +- encompass-cs/WorldBuilder.cs | 20 +-- 6 files changed, 189 insertions(+), 277 deletions(-) delete mode 100644 encompass-cs/ComponentUpdateManager.cs diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 47935bc..e407408 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -6,28 +6,37 @@ namespace Encompass internal class ComponentManager { private readonly DrawLayerManager drawLayerManager; - private readonly ComponentUpdateManager componentUpdateManager; - private readonly ComponentStore componentStore; + private readonly ComponentStore existingComponentStore; + private readonly ComponentStore immediateComponentStore; + private ComponentStore upToDateComponentStore; + + public Dictionary TypeToIndex { get; } + private readonly HashSet entitiesMarkedForRemoval = new HashSet(); - internal ComponentBitSet ComponentBitSet { get { return componentStore.ComponentBitSet; } } + internal ComponentBitSet ImmediateBits { get { return immediateComponentStore.ComponentBitSet; } } + internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } - public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager, Dictionary typeToIndex) + public ComponentManager(DrawLayerManager drawLayerManager, Dictionary typeToIndex) { this.drawLayerManager = drawLayerManager; - this.componentUpdateManager = componentUpdateManager; - componentStore = new ComponentStore(typeToIndex); + existingComponentStore = new ComponentStore(typeToIndex); + immediateComponentStore = new ComponentStore(typeToIndex); + upToDateComponentStore = new ComponentStore(typeToIndex); + TypeToIndex = typeToIndex; } public void RegisterComponentType() where TComponent : struct, IComponent { - componentStore.RegisterComponentType(); + existingComponentStore.RegisterComponentType(); + immediateComponentStore.RegisterComponentType(); + upToDateComponentStore.RegisterComponentType(); } internal void SetComponentStore(ComponentStore componentStore) { - this.componentStore.SwapWith(componentStore); + existingComponentStore.SwapWith(componentStore); } internal void RegisterDrawableComponent(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent @@ -35,24 +44,146 @@ namespace Encompass drawLayerManager.RegisterComponentWithLayer(entity.ID, component, layer); } - internal void AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent - { - componentStore.Set(entity.ID, component); - } - internal void WriteComponents() { - componentStore.SwapWith(componentUpdateManager.UpToDateComponentStore); + SetComponentStore(upToDateComponentStore); + upToDateComponentStore.ClearAll(); + immediateComponentStore.ClearAll(); + } + + internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent + { + upToDateComponentStore.Set(entity.ID, component); + } + + internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + { + if (immediateComponentStore.Set(entity.ID, component, priority)) + { + upToDateComponentStore.Set(entity.ID, component); + return true; + } + + return false; + } + + public bool UpdateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + { + return upToDateComponentStore.Set(entity.ID, component, priority); + } + + // existing or immediate reads + + internal IEnumerable<(TComponent, int)> ReadExistingAndImmediateComponentsByType() where TComponent : struct, IComponent + { + return upToDateComponentStore.All(); + } + + internal (TComponent, int) ReadFirstExistingOrImmediateComponentByType() where TComponent : struct, IComponent + { + if (!SomeExistingOrImmediateComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + var enumerator = ReadExistingAndImmediateComponentsByType().GetEnumerator(); + enumerator.MoveNext(); + return enumerator.Current; + } + + internal bool SomeExistingOrImmediateComponent() where TComponent : struct, IComponent + { + return upToDateComponentStore.Any(); + } + + // existing reads + + internal (TComponent, int) ReadFirstExistingComponentByType() where TComponent : struct, IComponent + { + if (!SomeExistingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + var enumerator = GetComponentsIncludingEntity().GetEnumerator(); + enumerator.MoveNext(); + return enumerator.Current; + } + + internal bool SomeExistingComponent() where TComponent : struct, IComponent + { + return existingComponentStore.Any(); + } + + // immediate reads + + internal IEnumerable<(TComponent, int)> ReadImmediateComponentsByType() where TComponent : struct, IComponent + { + return immediateComponentStore.All(); + } + + internal (TComponent, int) ReadFirstImmediateComponentByType() where TComponent : struct, IComponent + { + if (!SomeImmediateComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + var enumerator = ReadImmediateComponentsByType().GetEnumerator(); + enumerator.MoveNext(); + return enumerator.Current; + } + + internal bool SomeImmediateComponent() where TComponent : struct, IComponent + { + return immediateComponentStore.Any(); + } + + // component getters + + internal TComponent ReadImmediateOrExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return upToDateComponentStore.Get(entity.ID); + } + + internal TComponent ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return existingComponentStore.Get(entity.ID); + } + + internal TComponent ReadImmediateComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return immediateComponentStore.Get(entity.ID); + } + + // has checkers + + internal bool HasExistingOrImmediateComponent(Entity entity) where TComponent : struct, IComponent + { + return upToDateComponentStore.Has(entity.ID); + } + + internal bool HasExistingOrImmediateComponent(Entity entity, Type type) + { + return upToDateComponentStore.Has(type, entity.ID); + } + + internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent + { + return existingComponentStore.Has(entity.ID); + } + + internal bool HasExistingComponent(Entity entity, Type type) + { + return existingComponentStore.Has(type, entity.ID); + } + + internal bool HasImmediateComponent(Entity entity) where TComponent : struct, IComponent + { + return immediateComponentStore.Has(entity.ID); + } + + internal bool HasImmediateComponent(Entity entity, Type type) + { + return immediateComponentStore.Has(type, entity.ID); } internal IEnumerable<(TComponent, int)> GetComponentsIncludingEntity() where TComponent : struct, IComponent { - return componentStore.All(); + return existingComponentStore.All(); } internal IEnumerable GetComponentsByType() where TComponent : struct, IComponent { - foreach (var pair in componentStore.All()) + foreach (var pair in existingComponentStore.All()) { yield return pair.Item1; } @@ -60,17 +191,12 @@ namespace Encompass internal TComponent GetComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - return componentStore.Get(entity.ID); + return existingComponentStore.Get(entity.ID); } internal bool EntityHasComponentOfType(Entity entity) where TComponent : struct, IComponent { - return componentStore.Has(entity.ID); - } - - internal bool ComponentOfTypeExists() where TComponent : struct, IComponent - { - return componentStore.Any(); + return existingComponentStore.Has(entity.ID); } internal void MarkAllComponentsOnEntityForRemoval(Entity entity) @@ -82,7 +208,8 @@ namespace Encompass { foreach (var entityID in entitiesMarkedForRemoval) { - componentStore.Remove(entityID); + existingComponentStore.Remove(entityID); + upToDateComponentStore.Remove(entityID); drawLayerManager.UnRegisterEntityWithLayer(entityID); } @@ -91,8 +218,9 @@ namespace Encompass public bool RemoveImmediate(Entity entity, int priority) where TComponent : struct, IComponent { - if (componentUpdateManager.RemoveImmediate(entity, priority)) + if (immediateComponentStore.Remove(entity.ID, priority)) { + upToDateComponentStore.Remove(entity.ID, priority); drawLayerManager.UnRegisterComponentWithLayer(entity.ID); return true; } @@ -101,7 +229,7 @@ namespace Encompass public void Remove(Entity entity, int priority) where TComponent : struct, IComponent { - if (componentUpdateManager.Remove(entity, priority)) + if (upToDateComponentStore.Remove(entity.ID, priority)) { drawLayerManager.UnRegisterComponentWithLayer(entity.ID); } diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs deleted file mode 100644 index dc6c30d..0000000 --- a/encompass-cs/ComponentUpdateManager.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Encompass -{ - internal class ComponentUpdateManager - { - private ComponentStore existingComponentStore; - private readonly ComponentStore immediateComponentStore; - public ComponentStore UpToDateComponentStore { get; private set; } - - public Dictionary TypeToIndex { get; } - - public ComponentUpdateManager(Dictionary typeToIndex) - { - existingComponentStore = new ComponentStore(typeToIndex); - immediateComponentStore = new ComponentStore(typeToIndex); - UpToDateComponentStore = new ComponentStore(typeToIndex); - TypeToIndex = typeToIndex; - } - - public void RegisterComponentType() where TComponent : struct, IComponent - { - existingComponentStore.RegisterComponentType(); - immediateComponentStore.RegisterComponentType(); - UpToDateComponentStore.RegisterComponentType(); - } - - internal void Clear() - { - existingComponentStore.ClearAll(); - immediateComponentStore.ClearAll(); - } - - internal void SetStartingComponentStore(ComponentStore componentStore) - { - UpToDateComponentStore = componentStore; - } - - internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent - { - existingComponentStore.Set(entity.ID, component); - } - - internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent - { - if (immediateComponentStore.Set(entity.ID, component, priority)) - { - UpToDateComponentStore.Set(entity.ID, component); - return true; - } - - return false; - } - - internal bool RemoveImmediate(Entity entity, int priority) where TComponent : struct, IComponent - { - if (immediateComponentStore.Remove(entity.ID, priority)) - { - UpToDateComponentStore.Remove(entity.ID, priority); - return true; - } - - return false; - } - - internal bool Remove(Entity entity, int priority) where TComponent : struct, IComponent - { - return UpToDateComponentStore.Remove(entity.ID, priority); - } - - public bool UpdateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent - { - return UpToDateComponentStore.Set(entity.ID, component, priority); - } - - // general component reads by type - - internal IEnumerable<(TComponent, int)> ReadExistingAndImmediateComponentsByType() where TComponent : struct, IComponent - { - return UpToDateComponentStore.All(); - } - - internal IEnumerable<(TComponent, int)> ReadExistingComponentsByType() where TComponent : struct, IComponent - { - return existingComponentStore.All(); - } - - internal IEnumerable<(TComponent, int)> ReadImmediateComponentsByType() where TComponent : struct, IComponent - { - return immediateComponentStore.All(); - } - - // singular component reads by type - - internal (TComponent, int) ReadFirstExistingOrImmediateComponentByType() where TComponent : struct, IComponent - { - if (!SomeExistingOrImmediateComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - var enumerator = ReadExistingAndImmediateComponentsByType().GetEnumerator(); - enumerator.MoveNext(); - return enumerator.Current; - } - - internal (TComponent, int) ReadFirstExistingComponentByType() where TComponent : struct, IComponent - { - if (!SomeExistingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - var enumerator = ReadExistingComponentsByType().GetEnumerator(); - enumerator.MoveNext(); - return enumerator.Current; - } - - internal (TComponent, int) ReadFirstImmediateComponentByType() where TComponent : struct, IComponent - { - if (!SomeImmediateComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - var enumerator = ReadImmediateComponentsByType().GetEnumerator(); - enumerator.MoveNext(); - return enumerator.Current; - } - - // check if some component of type exists in the world - - internal bool SomeExistingOrImmediateComponent() where TComponent : struct, IComponent - { - return UpToDateComponentStore.Any(); - } - - internal bool SomeExistingComponent() where TComponent : struct, IComponent - { - return existingComponentStore.Any(); - } - - internal bool SomeImmediateComponent() where TComponent : struct, IComponent - { - return immediateComponentStore.Any(); - } - - // read components by entity and type - - internal TComponent ReadImmediateOrExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent - { - return UpToDateComponentStore.Get(entity.ID); - } - - internal TComponent ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent - { - return existingComponentStore.Get(entity.ID); - } - - internal TComponent ReadImmediateComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent - { - return immediateComponentStore.Get(entity.ID); - } - - // check if entity has component of type - - internal bool HasExistingOrImmediateComponent(Entity entity) where TComponent : struct, IComponent - { - return UpToDateComponentStore.Has(entity.ID); - } - - internal bool HasExistingOrImmediateComponent(Entity entity, Type type) - { - return UpToDateComponentStore.Has(type, entity.ID); - } - - internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent - { - return existingComponentStore.Has(entity.ID); - } - - internal bool HasExistingComponent(Entity entity, Type type) - { - return existingComponentStore.Has(type, entity.ID); - } - - internal bool HasImmediateComponent(Entity entity) where TComponent : struct, IComponent - { - return immediateComponentStore.Has(entity.ID); - } - - internal bool HasImmediateComponent(Entity entity, Type type) - { - return immediateComponentStore.Has(type, entity.ID); - } - - internal ComponentBitSet ImmediateBits { get { return immediateComponentStore.ComponentBitSet; } } - internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } - } -} diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index c748cea..356394c 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -36,7 +36,6 @@ namespace Encompass private EntityManager entityManager; private MessageManager messageManager; private ComponentManager componentManager; - private ComponentUpdateManager componentUpdateManager; private TimeManager timeManager; private TrackingManager trackingManager; @@ -140,11 +139,6 @@ namespace Encompass this.messageManager = messageManager; } - internal void AssignComponentUpdateManager(ComponentUpdateManager componentUpdateManager) - { - this.componentUpdateManager = componentUpdateManager; - } - internal void AssignTimeManager(TimeManager timeManager) { this.timeManager = timeManager; @@ -209,15 +203,15 @@ namespace Encompass var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && immediateRead) { - return componentUpdateManager.ReadExistingAndImmediateComponentsByType(); + return componentManager.ReadExistingAndImmediateComponentsByType(); } else if (existingRead) { - return componentUpdateManager.ReadExistingComponentsByType(); + return componentManager.GetComponentsIncludingEntity(); } else if (immediateRead) { - return componentUpdateManager.ReadImmediateComponentsByType(); + return componentManager.ReadImmediateComponentsByType(); } else { @@ -261,15 +255,15 @@ namespace Encompass var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && immediateRead) { - return componentUpdateManager.ReadFirstExistingOrImmediateComponentByType(); + return componentManager.ReadFirstExistingOrImmediateComponentByType(); } else if (existingRead) { - return componentUpdateManager.ReadFirstExistingComponentByType(); + return componentManager.ReadFirstExistingComponentByType(); } else if (immediateRead) { - return componentUpdateManager.ReadFirstImmediateComponentByType(); + return componentManager.ReadFirstImmediateComponentByType(); } else { @@ -303,15 +297,15 @@ namespace Encompass var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && immediateRead) { - return componentUpdateManager.SomeExistingOrImmediateComponent(); + return componentManager.SomeExistingOrImmediateComponent(); } else if (existingRead) { - return componentUpdateManager.SomeExistingComponent(); + return componentManager.SomeExistingComponent(); } else if (immediateRead) { - return componentUpdateManager.SomeImmediateComponent(); + return componentManager.SomeImmediateComponent(); } else { @@ -325,15 +319,15 @@ namespace Encompass var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && immediateRead) { - return componentUpdateManager.ReadImmediateOrExistingComponentByEntityAndType(entity); + return componentManager.ReadImmediateOrExistingComponentByEntityAndType(entity); } else if (existingRead) { - return componentUpdateManager.ReadExistingComponentByEntityAndType(entity); + return componentManager.ReadExistingComponentByEntityAndType(entity); } else if (immediateRead) { - return componentUpdateManager.ReadImmediateComponentByEntityAndType(entity); + return componentManager.ReadImmediateComponentByEntityAndType(entity); } else { @@ -368,15 +362,15 @@ namespace Encompass if (immediateRead && existingRead) { - return componentUpdateManager.HasExistingOrImmediateComponent(entity); + return componentManager.HasExistingOrImmediateComponent(entity); } else if (existingRead) { - return componentUpdateManager.HasExistingComponent(entity); + return componentManager.HasExistingComponent(entity); } else if (immediateRead) { - return componentUpdateManager.HasImmediateComponent(entity); + return componentManager.HasImmediateComponent(entity); } else { @@ -397,15 +391,15 @@ namespace Encompass if (immediateRead && existingRead) { - return componentUpdateManager.HasExistingOrImmediateComponent(entity, type); + return componentManager.HasExistingOrImmediateComponent(entity, type); } else if (existingRead) { - return componentUpdateManager.HasExistingComponent(entity, type); + return componentManager.HasExistingComponent(entity, type); } else if (immediateRead) { - return componentUpdateManager.HasImmediateComponent(entity, type); + return componentManager.HasImmediateComponent(entity, type); } else { @@ -431,7 +425,7 @@ namespace Encompass bool written; if (writeImmediateTypes.Contains(typeof(TComponent))) { - written = componentUpdateManager.AddImmediateComponent(entity, component, priority); + written = componentManager.AddImmediateComponent(entity, component, priority); if (written) { trackingManager.ImmediateUpdateTracking(entity, typeof(TComponent)); @@ -439,10 +433,10 @@ namespace Encompass } else { - written = componentUpdateManager.UpdateComponent(entity, component, priority); + written = componentManager.UpdateComponent(entity, component, priority); } - if (!componentUpdateManager.HasExistingComponent(entity)) + if (!componentManager.HasExistingComponent(entity)) { trackingManager.RegisterAddition(entity, typeof(TComponent)); } @@ -489,7 +483,7 @@ namespace Encompass internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - componentUpdateManager.AddExistingComponent(entity, component); + componentManager.AddExistingComponent(entity, component); } /// @@ -591,7 +585,7 @@ namespace Encompass componentManager.Remove(entity, priority); } - if (componentUpdateManager.HasExistingComponent(entity)) + if (componentManager.HasExistingComponent(entity)) { trackingManager.RegisterRemoval(entity, typeof(TComponent)); } @@ -659,11 +653,11 @@ namespace Encompass internal void CheckAndUpdateTracking(Entity entity) { - if (_trackedEntities.Contains(entity) && !entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) + if (_trackedEntities.Contains(entity) && !entityQuery.CheckEntity(entity, componentManager.ExistingBits)) { _trackedEntities.Remove(entity); } - else if (!_trackedEntities.Contains(entity) && entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) + else if (!_trackedEntities.Contains(entity) && entityQuery.CheckEntity(entity, componentManager.ExistingBits)) { _trackedEntities.Add(entity); } @@ -671,11 +665,11 @@ namespace Encompass internal void ImmediateCheckAndUpdateTracking(Entity entity) { - if (_trackedEntities.Contains(entity) && !entityQuery.ImmediateCheckEntity(entity, componentUpdateManager.ImmediateBits, componentUpdateManager.ExistingBits)) + if (_trackedEntities.Contains(entity) && !entityQuery.ImmediateCheckEntity(entity, componentManager.ImmediateBits, componentManager.ExistingBits)) { _trackedEntities.Remove(entity); } - else if (!_trackedEntities.Contains(entity) && entityQuery.ImmediateCheckEntity(entity, componentUpdateManager.ImmediateBits, componentUpdateManager.ExistingBits)) + else if (!_trackedEntities.Contains(entity) && entityQuery.ImmediateCheckEntity(entity, componentManager.ImmediateBits, componentManager.ExistingBits)) { _trackedEntities.Add(entity); } @@ -689,25 +683,25 @@ namespace Encompass var withMask = BitSet512.Zero; foreach (var type in queryWithTypes) { - withMask = withMask.Set(componentUpdateManager.TypeToIndex[type]); + withMask = withMask.Set(componentManager.TypeToIndex[type]); } var withoutMask = BitSet512.Zero; foreach (var type in queryWithoutTypes) { - withoutMask = withoutMask.Set(componentUpdateManager.TypeToIndex[type]); + withoutMask = withoutMask.Set(componentManager.TypeToIndex[type]); } var immediateMask = BitSet512.Zero; foreach (var type in readImmediateTypes) { - immediateMask = immediateMask.Set(componentUpdateManager.TypeToIndex[type]); + immediateMask = immediateMask.Set(componentManager.TypeToIndex[type]); } var existingMask = BitSet512.Zero; foreach (var type in readTypes) { - existingMask = existingMask.Set(componentUpdateManager.TypeToIndex[type]); + existingMask = existingMask.Set(componentManager.TypeToIndex[type]); } entityQuery = new EntitySetQuery( diff --git a/encompass-cs/Renderer.cs b/encompass-cs/Renderer.cs index 048d5d7..e0999af 100644 --- a/encompass-cs/Renderer.cs +++ b/encompass-cs/Renderer.cs @@ -69,7 +69,7 @@ namespace Encompass protected bool SomeComponent() where TComponent : struct, IComponent { - return componentManager.ComponentOfTypeExists(); + return componentManager.SomeExistingComponent(); } } } diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index a97ffe4..677bf8f 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -12,7 +12,6 @@ namespace Encompass private readonly ComponentManager componentManager; private readonly TrackingManager trackingManager; private readonly MessageManager messageManager; - private readonly ComponentUpdateManager componentUpdateManager; private readonly TimeManager timeManager; private readonly RenderManager renderManager; @@ -22,7 +21,6 @@ namespace Encompass ComponentManager componentManager, TrackingManager trackingManager, MessageManager messageManager, - ComponentUpdateManager componentUpdateManager, TimeManager timeManager, RenderManager renderManager ) @@ -32,7 +30,6 @@ namespace Encompass this.componentManager = componentManager; this.trackingManager = trackingManager; this.messageManager = messageManager; - this.componentUpdateManager = componentUpdateManager; this.timeManager = timeManager; this.renderManager = renderManager; } @@ -62,10 +59,8 @@ namespace Encompass messageManager.ClearMessages(); entityManager.DestroyMarkedEntities(enginesInOrder); - componentManager.WriteComponents(); componentManager.RemoveMarkedComponents(); - - componentUpdateManager.Clear(); + componentManager.WriteComponents(); } /// diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index d85ed87..35fdb5d 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -27,7 +27,6 @@ namespace Encompass private readonly ComponentManager componentManager; private readonly EntityManager entityManager; private readonly MessageManager messageManager; - private readonly ComponentUpdateManager componentUpdateManager; private readonly TimeManager timeManager; private readonly DrawLayerManager drawLayerManager; private readonly RenderManager renderManager; @@ -49,8 +48,7 @@ namespace Encompass drawLayerManager = new DrawLayerManager(typeToIndex); timeManager = new TimeManager(); trackingManager = new TrackingManager(); - componentUpdateManager = new ComponentUpdateManager(typeToIndex); - componentManager = new ComponentManager(drawLayerManager, componentUpdateManager, typeToIndex); + componentManager = new ComponentManager(drawLayerManager, typeToIndex); messageManager = new MessageManager(timeManager); entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(entityManager, drawLayerManager); @@ -107,7 +105,6 @@ namespace Encompass { typeToIndex.Add(typeof(TComponent), typeToIndex.Count); componentManager.RegisterComponentType(); - componentUpdateManager.RegisterComponentType(); startingComponentStoreForComponentManager.RegisterComponentType(); startingComponentStoreForComponentUpdateManager.RegisterComponentType(); } @@ -132,7 +129,6 @@ namespace Encompass engine.AssignEntityManager(entityManager); engine.AssignComponentManager(componentManager); engine.AssignMessageManager(messageManager); - engine.AssignComponentUpdateManager(componentUpdateManager); engine.AssignTimeManager(timeManager); engine.AssignTrackingManager(trackingManager); @@ -395,12 +391,10 @@ namespace Encompass componentManager, trackingManager, messageManager, - componentUpdateManager, timeManager, renderManager ); - componentUpdateManager.SetStartingComponentStore(startingComponentStoreForComponentUpdateManager); componentManager.SetComponentStore(startingComponentStoreForComponentManager); trackingManager.InitializeTracking(entityManager.Entities); @@ -420,8 +414,7 @@ namespace Encompass var dummyMessageManager = new MessageManager(dummyTimeManager); var dummyDrawLayerManager = new DrawLayerManager(typeToIndex); var dummyTrackingManager = new TrackingManager(); - var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex); - var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex); + var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, typeToIndex); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager); @@ -432,7 +425,6 @@ namespace Encompass uberEngine.AssignEntityManager(dummyEntityManager); uberEngine.AssignComponentManager(dummyComponentManager); uberEngine.AssignMessageManager(dummyMessageManager); - uberEngine.AssignComponentUpdateManager(dummyComponentUpdateManager); uberEngine.AssignTimeManager(dummyTimeManager); uberEngine.AssignTrackingManager(dummyTrackingManager); @@ -446,10 +438,6 @@ namespace Encompass var componentManagerRegisterGenericMethod = componentManagerRegisterMethod.MakeGenericMethod(type); componentManagerRegisterGenericMethod.Invoke(dummyComponentManager, null); - var componentUpdateManagerRegisterMethod = typeof(ComponentUpdateManager).GetMethod("RegisterComponentType"); - var componentUpdateManagerRegisterGenericMethod = componentUpdateManagerRegisterMethod.MakeGenericMethod(type); - componentUpdateManagerRegisterGenericMethod.Invoke(dummyComponentUpdateManager, null); - if (type.GetInterface("IDrawableComponent") != null) { var drawLayerManagerRegisterMethod = typeof(DrawLayerManager).GetMethod("RegisterOrderedDrawable"); @@ -461,7 +449,6 @@ namespace Encompass emitterEngine.AssignEntityManager(dummyEntityManager); emitterEngine.AssignComponentManager(dummyComponentManager); emitterEngine.AssignMessageManager(dummyMessageManager); - emitterEngine.AssignComponentUpdateManager(dummyComponentUpdateManager); emitterEngine.AssignTimeManager(dummyTimeManager); emitterEngine.AssignTrackingManager(dummyTrackingManager); @@ -476,20 +463,17 @@ namespace Encompass dummyComponentManager, dummyTrackingManager, dummyMessageManager, - dummyComponentUpdateManager, dummyTimeManager, dummyRenderManager ); uberEngine.Write(); dummyComponentManager.WriteComponents(); - dummyComponentUpdateManager.Clear(); dummyWorld.Update(1); uberEngine.Write(); dummyComponentManager.WriteComponents(); - dummyComponentUpdateManager.Clear(); uberRenderer.SetEntity(uberEngine.Entity); uberRenderer.Render(); From 1eb72874c102b6c68b3368f8b350047ea7a1d345 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 13:54:08 -0800 Subject: [PATCH 30/37] replace entity storage with int --- encompass-cs/ComponentManager.cs | 80 +++++++++++----------- encompass-cs/Engine.cs | 87 +++++++++++++----------- encompass-cs/Engines/ComponentEmitter.cs | 2 +- encompass-cs/EntityManager.cs | 30 ++++---- encompass-cs/EntitySetQuery.cs | 10 +-- encompass-cs/Renderer.cs | 4 +- encompass-cs/TrackingManager.cs | 24 +++---- encompass-cs/WorldBuilder.cs | 4 +- 8 files changed, 125 insertions(+), 116 deletions(-) diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index e407408..a97096e 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -39,9 +39,9 @@ namespace Encompass existingComponentStore.SwapWith(componentStore); } - internal void RegisterDrawableComponent(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent + internal void RegisterDrawableComponent(int entityID, TComponent component, int layer) where TComponent : struct, IComponent { - drawLayerManager.RegisterComponentWithLayer(entity.ID, component, layer); + drawLayerManager.RegisterComponentWithLayer(entityID, component, layer); } internal void WriteComponents() @@ -51,25 +51,25 @@ namespace Encompass immediateComponentStore.ClearAll(); } - internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent + internal void AddExistingComponent(int entityID, TComponent component) where TComponent : struct, IComponent { - upToDateComponentStore.Set(entity.ID, component); + upToDateComponentStore.Set(entityID, component); } - internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + internal bool AddImmediateComponent(int entityID, TComponent component, int priority) where TComponent : struct, IComponent { - if (immediateComponentStore.Set(entity.ID, component, priority)) + if (immediateComponentStore.Set(entityID, component, priority)) { - upToDateComponentStore.Set(entity.ID, component); + upToDateComponentStore.Set(entityID, component); return true; } return false; } - public bool UpdateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + public bool UpdateComponent(int entityID, TComponent component, int priority) where TComponent : struct, IComponent { - return upToDateComponentStore.Set(entity.ID, component, priority); + return upToDateComponentStore.Set(entityID, component, priority); } // existing or immediate reads @@ -129,51 +129,51 @@ namespace Encompass // component getters - internal TComponent ReadImmediateOrExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal TComponent ReadImmediateOrExistingComponentByEntityAndType(int entityID) where TComponent : struct, IComponent { - return upToDateComponentStore.Get(entity.ID); + return upToDateComponentStore.Get(entityID); } - internal TComponent ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal TComponent ReadExistingComponentByEntityAndType(int entityID) where TComponent : struct, IComponent { - return existingComponentStore.Get(entity.ID); + return existingComponentStore.Get(entityID); } - internal TComponent ReadImmediateComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal TComponent ReadImmediateComponentByEntityAndType(int entityID) where TComponent : struct, IComponent { - return immediateComponentStore.Get(entity.ID); + return immediateComponentStore.Get(entityID); } // has checkers - internal bool HasExistingOrImmediateComponent(Entity entity) where TComponent : struct, IComponent + internal bool HasExistingOrImmediateComponent(int entityID) where TComponent : struct, IComponent { - return upToDateComponentStore.Has(entity.ID); + return upToDateComponentStore.Has(entityID); } - internal bool HasExistingOrImmediateComponent(Entity entity, Type type) + internal bool HasExistingOrImmediateComponent(int entityID, Type type) { - return upToDateComponentStore.Has(type, entity.ID); + return upToDateComponentStore.Has(type, entityID); } - internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent + internal bool HasExistingComponent(int entityID) where TComponent : struct, IComponent { - return existingComponentStore.Has(entity.ID); + return existingComponentStore.Has(entityID); } - internal bool HasExistingComponent(Entity entity, Type type) + internal bool HasExistingComponent(int entityID, Type type) { - return existingComponentStore.Has(type, entity.ID); + return existingComponentStore.Has(type, entityID); } - internal bool HasImmediateComponent(Entity entity) where TComponent : struct, IComponent + internal bool HasImmediateComponent(int entityID) where TComponent : struct, IComponent { - return immediateComponentStore.Has(entity.ID); + return immediateComponentStore.Has(entityID); } - internal bool HasImmediateComponent(Entity entity, Type type) + internal bool HasImmediateComponent(int entityID, Type type) { - return immediateComponentStore.Has(type, entity.ID); + return immediateComponentStore.Has(type, entityID); } internal IEnumerable<(TComponent, int)> GetComponentsIncludingEntity() where TComponent : struct, IComponent @@ -189,19 +189,19 @@ namespace Encompass } } - internal TComponent GetComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal TComponent GetComponentByEntityAndType(int entityID) where TComponent : struct, IComponent { - return existingComponentStore.Get(entity.ID); + return existingComponentStore.Get(entityID); } - internal bool EntityHasComponentOfType(Entity entity) where TComponent : struct, IComponent + internal bool EntityHasComponentOfType(int entityID) where TComponent : struct, IComponent { - return existingComponentStore.Has(entity.ID); + return existingComponentStore.Has(entityID); } - internal void MarkAllComponentsOnEntityForRemoval(Entity entity) + internal void MarkAllComponentsOnEntityForRemoval(int entityID) { - entitiesMarkedForRemoval.Add(entity.ID); + entitiesMarkedForRemoval.Add(entityID); } internal void RemoveMarkedComponents() @@ -216,22 +216,22 @@ namespace Encompass entitiesMarkedForRemoval.Clear(); } - public bool RemoveImmediate(Entity entity, int priority) where TComponent : struct, IComponent + public bool RemoveImmediate(int entityID, int priority) where TComponent : struct, IComponent { - if (immediateComponentStore.Remove(entity.ID, priority)) + if (immediateComponentStore.Remove(entityID, priority)) { - upToDateComponentStore.Remove(entity.ID, priority); - drawLayerManager.UnRegisterComponentWithLayer(entity.ID); + upToDateComponentStore.Remove(entityID, priority); + drawLayerManager.UnRegisterComponentWithLayer(entityID); return true; } return false; } - public void Remove(Entity entity, int priority) where TComponent : struct, IComponent + public void Remove(int entityID, int priority) where TComponent : struct, IComponent { - if (upToDateComponentStore.Remove(entity.ID, priority)) + if (upToDateComponentStore.Remove(entityID, priority)) { - drawLayerManager.UnRegisterComponentWithLayer(entity.ID); + drawLayerManager.UnRegisterComponentWithLayer(entityID); } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 356394c..ec341d7 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -41,8 +41,17 @@ namespace Encompass private EntitySetQuery entityQuery; - private HashSet _trackedEntities = new HashSet(); - protected IEnumerable TrackedEntities { get { return _trackedEntities; } } + private HashSet _trackedEntities = new HashSet(); + protected IEnumerable TrackedEntities + { + get + { + foreach (var entityID in _trackedEntities) + { + yield return entityManager.GetEntity(entityID); + } + } + } protected Engine() { @@ -313,21 +322,21 @@ namespace Encompass } } - private TComponent GetComponentHelper(Entity entity) where TComponent : struct, IComponent + private TComponent GetComponentHelper(int entityID) where TComponent : struct, IComponent { var immediateRead = readImmediateTypes.Contains(typeof(TComponent)); var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && immediateRead) { - return componentManager.ReadImmediateOrExistingComponentByEntityAndType(entity); + return componentManager.ReadImmediateOrExistingComponentByEntityAndType(entityID); } else if (existingRead) { - return componentManager.ReadExistingComponentByEntityAndType(entity); + return componentManager.ReadExistingComponentByEntityAndType(entityID); } else if (immediateRead) { - return componentManager.ReadImmediateComponentByEntityAndType(entity); + return componentManager.ReadImmediateComponentByEntityAndType(entityID); } else { @@ -346,7 +355,7 @@ namespace Encompass /// protected TComponent GetComponent(Entity entity) where TComponent : struct, IComponent { - return GetComponentHelper(entity); + return GetComponentHelper(entity.ID); } /// @@ -362,15 +371,15 @@ namespace Encompass if (immediateRead && existingRead) { - return componentManager.HasExistingOrImmediateComponent(entity); + return componentManager.HasExistingOrImmediateComponent(entity.ID); } else if (existingRead) { - return componentManager.HasExistingComponent(entity); + return componentManager.HasExistingComponent(entity.ID); } else if (immediateRead) { - return componentManager.HasImmediateComponent(entity); + return componentManager.HasImmediateComponent(entity.ID); } else { @@ -391,15 +400,15 @@ namespace Encompass if (immediateRead && existingRead) { - return componentManager.HasExistingOrImmediateComponent(entity, type); + return componentManager.HasExistingOrImmediateComponent(entity.ID, type); } else if (existingRead) { - return componentManager.HasExistingComponent(entity, type); + return componentManager.HasExistingComponent(entity.ID, type); } else if (immediateRead) { - return componentManager.HasImmediateComponent(entity, type); + return componentManager.HasImmediateComponent(entity.ID, type); } else { @@ -425,25 +434,25 @@ namespace Encompass bool written; if (writeImmediateTypes.Contains(typeof(TComponent))) { - written = componentManager.AddImmediateComponent(entity, component, priority); + written = componentManager.AddImmediateComponent(entity.ID, component, priority); if (written) { - trackingManager.ImmediateUpdateTracking(entity, typeof(TComponent)); + trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent)); } } else { - written = componentManager.UpdateComponent(entity, component, priority); + written = componentManager.UpdateComponent(entity.ID, component, priority); } - if (!componentManager.HasExistingComponent(entity)) + if (!componentManager.HasExistingComponent(entity.ID)) { - trackingManager.RegisterAddition(entity, typeof(TComponent)); + trackingManager.RegisterAddition(entity.ID, typeof(TComponent)); } if (written && component is IDrawableComponent drawableComponent) { - componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer); + componentManager.RegisterDrawableComponent(entity.ID, component, drawableComponent.Layer); } } @@ -481,9 +490,9 @@ namespace Encompass messageManager.AddMessageIgnoringTimeDilation(message, time); } - internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent + internal void AddExistingComponent(int entityID, TComponent component) where TComponent : struct, IComponent { - componentManager.AddExistingComponent(entity, component); + componentManager.AddExistingComponent(entityID, component); } /// @@ -535,7 +544,7 @@ namespace Encompass /// protected void Destroy(Entity entity) { - entityManager.MarkForDestroy(entity); + entityManager.MarkForDestroy(entity.ID); } /// @@ -575,19 +584,19 @@ namespace Encompass if (writeImmediateTypes.Contains(typeof(TComponent))) { - if (componentManager.RemoveImmediate(entity, priority)) + if (componentManager.RemoveImmediate(entity.ID, priority)) { - trackingManager.ImmediateUpdateTracking(entity, typeof(TComponent)); + trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent)); } } else { - componentManager.Remove(entity, priority); + componentManager.Remove(entity.ID, priority); } - if (componentManager.HasExistingComponent(entity)) + if (componentManager.HasExistingComponent(entity.ID)) { - trackingManager.RegisterRemoval(entity, typeof(TComponent)); + trackingManager.RegisterRemoval(entity.ID, typeof(TComponent)); } } @@ -651,27 +660,27 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } - internal void CheckAndUpdateTracking(Entity entity) + internal void CheckAndUpdateTracking(int entityID) { - if (_trackedEntities.Contains(entity) && !entityQuery.CheckEntity(entity, componentManager.ExistingBits)) + if (_trackedEntities.Contains(entityID) && !entityQuery.CheckEntity(entityID, componentManager.ExistingBits)) { - _trackedEntities.Remove(entity); + _trackedEntities.Remove(entityID); } - else if (!_trackedEntities.Contains(entity) && entityQuery.CheckEntity(entity, componentManager.ExistingBits)) + else if (!_trackedEntities.Contains(entityID) && entityQuery.CheckEntity(entityID, componentManager.ExistingBits)) { - _trackedEntities.Add(entity); + _trackedEntities.Add(entityID); } } - internal void ImmediateCheckAndUpdateTracking(Entity entity) + internal void ImmediateCheckAndUpdateTracking(int entityID) { - if (_trackedEntities.Contains(entity) && !entityQuery.ImmediateCheckEntity(entity, componentManager.ImmediateBits, componentManager.ExistingBits)) + if (_trackedEntities.Contains(entityID) && !entityQuery.ImmediateCheckEntity(entityID, componentManager.ImmediateBits, componentManager.ExistingBits)) { - _trackedEntities.Remove(entity); + _trackedEntities.Remove(entityID); } - else if (!_trackedEntities.Contains(entity) && entityQuery.ImmediateCheckEntity(entity, componentManager.ImmediateBits, componentManager.ExistingBits)) + else if (!_trackedEntities.Contains(entityID) && entityQuery.ImmediateCheckEntity(entityID, componentManager.ImmediateBits, componentManager.ExistingBits)) { - _trackedEntities.Add(entity); + _trackedEntities.Add(entityID); } } @@ -713,9 +722,9 @@ namespace Encompass ); } - internal void RegisterDestroyedEntity(Entity entity) + internal void RegisterDestroyedEntity(int entityID) { - _trackedEntities.Remove(entity); + _trackedEntities.Remove(entityID); } } } diff --git a/encompass-cs/Engines/ComponentEmitter.cs b/encompass-cs/Engines/ComponentEmitter.cs index 95b61b0..2a7bd2d 100644 --- a/encompass-cs/Engines/ComponentEmitter.cs +++ b/encompass-cs/Engines/ComponentEmitter.cs @@ -6,7 +6,7 @@ { foreach (var (component, entity) in InternalRead()) { - AddExistingComponent(entity, component); + AddExistingComponent(entity.ID, component); } } } diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index f80e5cc..d9e9337 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -7,15 +7,15 @@ namespace Encompass { private readonly int entityCapacity; private readonly IDManager idManager = new IDManager(); - private readonly Dictionary IDs = new Dictionary(32768); - - private readonly HashSet entitiesMarkedForDestroy = new HashSet(); + private readonly HashSet IDs = new HashSet(); + + private readonly HashSet entitiesMarkedForDestroy = new HashSet(); private readonly ComponentManager componentManager; - public IEnumerable Entities + public IEnumerable EntityIDs { - get { return IDs.Values; } + get { return IDs; } } public EntityManager(ComponentManager componentManager, int entityCapacity) @@ -35,7 +35,7 @@ namespace Encompass { var id = NextID(); var entity = new Entity(id); - IDs.Add(id, entity); + IDs.Add(id); return entity; } else @@ -46,7 +46,7 @@ namespace Encompass public bool EntityExists(int id) { - return IDs.ContainsKey(id); + return IDs.Contains(id); } public Entity GetEntity(int id) @@ -56,22 +56,22 @@ namespace Encompass throw new Encompass.Exceptions.EntityNotFoundException("Entity with id {0} does not exist.", id); } - return IDs[id]; + return new Entity(id); } - public void MarkForDestroy(Entity entity) + public void MarkForDestroy(int entityID) { - entitiesMarkedForDestroy.Add(entity); + entitiesMarkedForDestroy.Add(entityID); } public void DestroyMarkedEntities(IEnumerable engines) { - foreach (var entity in entitiesMarkedForDestroy) + foreach (var entityID in entitiesMarkedForDestroy) { - foreach (var engine in engines) { engine.RegisterDestroyedEntity(entity); } - componentManager.MarkAllComponentsOnEntityForRemoval(entity); - IDs.Remove(entity.ID); - idManager.Free(entity.ID); + foreach (var engine in engines) { engine.RegisterDestroyedEntity(entityID); } + componentManager.MarkAllComponentsOnEntityForRemoval(entityID); + IDs.Remove(entityID); + idManager.Free(entityID); } entitiesMarkedForDestroy.Clear(); diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 04c0432..978a13b 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -19,9 +19,9 @@ namespace Encompass NotWithMask = notWithMask; } - public bool CheckEntity(Entity entity, ComponentBitSet componentBitSet) + public bool CheckEntity(int entityID, ComponentBitSet componentBitSet) { - var existingBits = componentBitSet.EntityBitArray(entity.ID); + var existingBits = componentBitSet.EntityBitArray(entityID); var existing = (WithExistingMask & existingBits) | NotWithMask; var existingForbidden = ~(WithoutExistingMask & existingBits); @@ -29,10 +29,10 @@ namespace Encompass return (existing & existingForbidden).AllTrue(); } - public bool ImmediateCheckEntity(Entity entity, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) + public bool ImmediateCheckEntity(int entityID, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) { - var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); - var existingBits = existingBitLookup.EntityBitArray(entity.ID); + var immediateBits = immediateBitLookup.EntityBitArray(entityID); + var existingBits = existingBitLookup.EntityBitArray(entityID); var immediate = WithImmediateMask & immediateBits; var existing = WithExistingMask & existingBits; diff --git a/encompass-cs/Renderer.cs b/encompass-cs/Renderer.cs index e0999af..3dba03c 100644 --- a/encompass-cs/Renderer.cs +++ b/encompass-cs/Renderer.cs @@ -59,12 +59,12 @@ namespace Encompass protected TComponent GetComponent(Entity entity) where TComponent : struct, IComponent { - return componentManager.GetComponentByEntityAndType(entity); + return componentManager.GetComponentByEntityAndType(entity.ID); } protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { - return componentManager.EntityHasComponentOfType(entity); + return componentManager.EntityHasComponentOfType(entity.ID); } protected bool SomeComponent() where TComponent : struct, IComponent diff --git a/encompass-cs/TrackingManager.cs b/encompass-cs/TrackingManager.cs index 61600e4..c9e7830 100644 --- a/encompass-cs/TrackingManager.cs +++ b/encompass-cs/TrackingManager.cs @@ -8,10 +8,10 @@ namespace Encompass private Dictionary> _immediateComponentTypesToEngines = new Dictionary>(); private Dictionary> _componentTypesToEngines = new Dictionary>(); - private HashSet<(Entity, Type)> _additions = new HashSet<(Entity, Type)>(); - private HashSet<(Entity, Type)> _removals = new HashSet<(Entity, Type)>(); + private HashSet<(int, Type)> _additions = new HashSet<(int, Type)>(); + private HashSet<(int, Type)> _removals = new HashSet<(int, Type)>(); - private HashSet<(Entity, Engine)> _pairsToCheck = new HashSet<(Entity, Engine)>(); + private HashSet<(int, Engine)> _pairsToCheck = new HashSet<(int, Engine)>(); public void RegisterComponentTypeToEngine(Type type, Engine engine) { @@ -25,37 +25,37 @@ namespace Encompass _immediateComponentTypesToEngines[type].Add(engine); } - public void RegisterAddition(Entity entity, Type type) + public void RegisterAddition(int entityID, Type type) { - _additions.Add((entity, type)); + _additions.Add((entityID, type)); } - public void RegisterRemoval(Entity entity, Type type) + public void RegisterRemoval(int entityID, Type type) { - _removals.Add((entity, type)); + _removals.Add((entityID, type)); } - public void InitializeTracking(IEnumerable entities) + public void InitializeTracking(IEnumerable entityIDs) { - foreach (var entity in entities) + foreach (var entityID in entityIDs) { foreach (var engineSet in _componentTypesToEngines.Values) { foreach (var engine in engineSet) { - engine.CheckAndUpdateTracking(entity); + engine.CheckAndUpdateTracking(entityID); } } } } - public void ImmediateUpdateTracking(Entity entity, Type componentType) + public void ImmediateUpdateTracking(int entityID, Type componentType) { if (_immediateComponentTypesToEngines.ContainsKey(componentType)) { foreach (var engine in _componentTypesToEngines[componentType]) { - engine.ImmediateCheckAndUpdateTracking(entity); + engine.ImmediateCheckAndUpdateTracking(entityID); } } } diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 35fdb5d..16b9ff8 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -94,7 +94,7 @@ namespace Encompass if (component is IDrawableComponent drawableComponent) { - componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer); + componentManager.RegisterDrawableComponent(entity.ID, component, drawableComponent.Layer); drawLayerManager.RegisterOrderedDrawable(); } } @@ -397,7 +397,7 @@ namespace Encompass componentManager.SetComponentStore(startingComponentStoreForComponentManager); - trackingManager.InitializeTracking(entityManager.Entities); + trackingManager.InitializeTracking(entityManager.EntityIDs); return world; } From a8669a6c670167cef5d504febd6ae2d83705f37b Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 16:28:22 -0800 Subject: [PATCH 31/37] starting replay system to avoid replaying every component every frame --- .../Collections/ComponentDeltaStore.cs | 60 +++++++++++++++++++ encompass-cs/Collections/ComponentStore.cs | 16 +++-- encompass-cs/Collections/Replayer.cs | 51 ++++++++++++++++ encompass-cs/ComponentManager.cs | 6 +- encompass-cs/Engine.cs | 13 ---- encompass-cs/Engines/ComponentEmitter.cs | 13 ---- encompass-cs/WorldBuilder.cs | 13 ---- 7 files changed, 126 insertions(+), 46 deletions(-) create mode 100644 encompass-cs/Collections/ComponentDeltaStore.cs create mode 100644 encompass-cs/Collections/Replayer.cs delete mode 100644 encompass-cs/Engines/ComponentEmitter.cs diff --git a/encompass-cs/Collections/ComponentDeltaStore.cs b/encompass-cs/Collections/ComponentDeltaStore.cs new file mode 100644 index 0000000..958c3af --- /dev/null +++ b/encompass-cs/Collections/ComponentDeltaStore.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace Encompass +{ + internal class ComponentDeltaStore : ComponentStore + { + private readonly Dictionary _replayers = new Dictionary(); + private readonly HashSet _currentReplayers = new HashSet(); + + public IEnumerable CurrentReplayers { get { return _currentReplayers; } } + + public ComponentDeltaStore(Dictionary typeToIndex) : base(typeToIndex) + { + } + + public override void RegisterComponentType() + { + base.RegisterComponentType(); + if (!_replayers.ContainsKey(typeof(TComponent))) + { + _replayers.Add(typeof(TComponent), new Replayer(this)); + } + } + + public override bool Set(int entityID, TComponent component, int priority) + { + var result = base.Set(entityID, component, priority); + if (result) + { + var replayer = _replayers[typeof(TComponent)]; + _currentReplayers.Add(replayer); + replayer.UnMarkRemoval(entityID); + } + return result; + } + + public override bool Remove(int entityID, int priority) + { + var result = base.Remove(entityID, priority); + if (result) + { + var replayer = _replayers[typeof(TComponent)]; + _currentReplayers.Add(replayer); + replayer.MarkRemoval(entityID); + } + return result; + } + + public override void ClearAll() + { + base.ClearAll(); + foreach (var replayer in _currentReplayers) + { + replayer.Clear(); + } + _currentReplayers.Clear(); + } + } +} diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 03904b1..337f610 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -22,7 +22,7 @@ namespace Encompass } } - public void RegisterComponentType() where TComponent : struct, IComponent + public virtual void RegisterComponentType() where TComponent : struct, IComponent { if (!Stores.ContainsKey(typeof(TComponent))) { @@ -63,7 +63,7 @@ namespace Encompass ComponentBitSet.Set(entityID); } - public bool Set(int entityID, TComponent component, int priority) where TComponent : struct, IComponent + public virtual bool Set(int entityID, TComponent component, int priority) where TComponent : struct, IComponent { if (Lookup().Set(entityID, component, priority)) { @@ -73,7 +73,7 @@ namespace Encompass return false; } - public bool Remove(int entityID, int priority) where TComponent : struct, IComponent + public virtual bool Remove(int entityID, int priority) where TComponent : struct, IComponent { if (Lookup().Remove(entityID, priority)) { @@ -123,7 +123,7 @@ namespace Encompass Lookup().Clear(); } - public void ClearAll() + public virtual void ClearAll() { ComponentBitSet.Clear(); foreach (var store in Stores.Values) @@ -137,5 +137,13 @@ namespace Encompass (Stores, other.Stores) = (other.Stores, Stores); (ComponentBitSet, other.ComponentBitSet) = (other.ComponentBitSet, ComponentBitSet); } + + public void UpdateUsing(ComponentDeltaStore delta) + { + foreach (var replayer in delta.CurrentReplayers) + { + replayer.Replay(this); + } + } } } diff --git a/encompass-cs/Collections/Replayer.cs b/encompass-cs/Collections/Replayer.cs new file mode 100644 index 0000000..b5cd3f7 --- /dev/null +++ b/encompass-cs/Collections/Replayer.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; + +namespace Encompass +{ + internal abstract class Replayer + { + public abstract void Replay(ComponentStore store); + public abstract void MarkRemoval(int entityID); + public abstract void UnMarkRemoval(int entityID); + public abstract void Clear(); + } + + internal class Replayer : Replayer where TComponent : struct, IComponent + { + private readonly ComponentDeltaStore _deltaStore; + private readonly HashSet _removals = new HashSet(); + + public Replayer(ComponentDeltaStore componentStore) + { + _deltaStore = componentStore; + } + + public override void Replay(ComponentStore store) + { + foreach (var (component, entityID) in _deltaStore.All()) + { + store.Set(entityID, component); + } + + foreach (var entityID in _removals) + { + store.ForceRemove(entityID); + } + } + + public override void Clear() + { + _removals.Clear(); + } + + public override void MarkRemoval(int entityID) + { + _removals.Add(entityID); + } + + public override void UnMarkRemoval(int entityID) + { + _removals.Remove(entityID); + } + } +} diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index a97096e..f913e70 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -8,7 +8,7 @@ namespace Encompass private readonly DrawLayerManager drawLayerManager; private readonly ComponentStore existingComponentStore; - private readonly ComponentStore immediateComponentStore; + private readonly ComponentDeltaStore immediateComponentStore; private ComponentStore upToDateComponentStore; public Dictionary TypeToIndex { get; } @@ -22,7 +22,7 @@ namespace Encompass { this.drawLayerManager = drawLayerManager; existingComponentStore = new ComponentStore(typeToIndex); - immediateComponentStore = new ComponentStore(typeToIndex); + immediateComponentStore = new ComponentDeltaStore(typeToIndex); upToDateComponentStore = new ComponentStore(typeToIndex); TypeToIndex = typeToIndex; } @@ -47,7 +47,7 @@ namespace Encompass internal void WriteComponents() { SetComponentStore(upToDateComponentStore); - upToDateComponentStore.ClearAll(); + upToDateComponentStore.UpdateUsing(immediateComponentStore); immediateComponentStore.ClearAll(); } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index ec341d7..9b2b2b0 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -250,14 +250,6 @@ namespace Encompass } } - internal IEnumerable<(TComponent, Entity)> InternalRead() where TComponent : struct, IComponent - { - foreach (var (component, id) in componentManager.GetComponentsIncludingEntity()) - { - yield return (component, entityManager.GetEntity(id)); - } - } - private (TComponent, int) ReadComponentHelper() where TComponent : struct, IComponent { var immediateRead = readImmediateTypes.Contains(typeof(TComponent)); @@ -490,11 +482,6 @@ namespace Encompass messageManager.AddMessageIgnoringTimeDilation(message, time); } - internal void AddExistingComponent(int entityID, TComponent component) where TComponent : struct, IComponent - { - componentManager.AddExistingComponent(entityID, component); - } - /// /// Reads all messages of the specified Type. /// diff --git a/encompass-cs/Engines/ComponentEmitter.cs b/encompass-cs/Engines/ComponentEmitter.cs deleted file mode 100644 index 2a7bd2d..0000000 --- a/encompass-cs/Engines/ComponentEmitter.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Encompass -{ - internal class ComponentEmitter : Engine where TComponent : struct, IComponent - { - public override void Update(double dt) - { - foreach (var (component, entity) in InternalRead()) - { - AddExistingComponent(entity.ID, component); - } - } - } -} diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 16b9ff8..5fae89a 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -371,10 +371,6 @@ namespace Encompass var method = typeof(WorldBuilder).GetMethod("RegisterComponentType", BindingFlags.NonPublic | BindingFlags.Instance); var generic = method.MakeGenericMethod(registeredComponentType); generic.Invoke(this, null); - - var emitterEngine = (Engine)Activator.CreateInstance(typeof(ComponentEmitter<>).MakeGenericType(registeredComponentType)); - AddEngine(emitterEngine); - engineOrder.Add(emitterEngine); } PreloadJIT(componentTypesToRegister, messageTypes); @@ -444,15 +440,6 @@ namespace Encompass var drawLayerManagerRegisterGenericMethod = drawLayerManagerRegisterMethod.MakeGenericMethod(type); drawLayerManagerRegisterGenericMethod.Invoke(dummyDrawLayerManager, null); } - - var emitterEngine = (Engine)Activator.CreateInstance(typeof(ComponentEmitter<>).MakeGenericType(type)); - emitterEngine.AssignEntityManager(dummyEntityManager); - emitterEngine.AssignComponentManager(dummyComponentManager); - emitterEngine.AssignMessageManager(dummyMessageManager); - emitterEngine.AssignTimeManager(dummyTimeManager); - emitterEngine.AssignTrackingManager(dummyTrackingManager); - - prepEngineOrder.Add(emitterEngine); } prepEngineOrder.Add(uberEngine); From 4d47a60125beaf8650b036fce14e8bf7b26f9a0d Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 17:37:19 -0800 Subject: [PATCH 32/37] fix some bugs with new replay structure --- .../Collections/ComponentDeltaStore.cs | 9 +++++++++ encompass-cs/Collections/ComponentStore.cs | 2 +- encompass-cs/ComponentManager.cs | 10 ++++++++-- encompass-cs/WorldBuilder.cs | 19 ++++++++++--------- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/encompass-cs/Collections/ComponentDeltaStore.cs b/encompass-cs/Collections/ComponentDeltaStore.cs index 958c3af..b4f5d89 100644 --- a/encompass-cs/Collections/ComponentDeltaStore.cs +++ b/encompass-cs/Collections/ComponentDeltaStore.cs @@ -47,6 +47,15 @@ namespace Encompass return result; } + public override void Remove(int entityID) + { + base.Remove(entityID); + foreach (var replayer in CurrentReplayers) + { + replayer.MarkRemoval(entityID); + } + } + public override void ClearAll() { base.ClearAll(); diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 337f610..d879c4e 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -88,7 +88,7 @@ namespace Encompass Lookup().ForceRemove(entityID); } - public void Remove(int entityID) + public virtual void Remove(int entityID) { foreach (var entry in Stores.Values) { diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index f913e70..26836bd 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -34,11 +34,16 @@ namespace Encompass upToDateComponentStore.RegisterComponentType(); } - internal void SetComponentStore(ComponentStore componentStore) + internal void SetExistingComponentStore(ComponentStore componentStore) { existingComponentStore.SwapWith(componentStore); } + internal void SetUpToDateComponentStore(ComponentStore componentStore) + { + upToDateComponentStore.SwapWith(componentStore); + } + internal void RegisterDrawableComponent(int entityID, TComponent component, int layer) where TComponent : struct, IComponent { drawLayerManager.RegisterComponentWithLayer(entityID, component, layer); @@ -46,7 +51,7 @@ namespace Encompass internal void WriteComponents() { - SetComponentStore(upToDateComponentStore); + SetExistingComponentStore(upToDateComponentStore); upToDateComponentStore.UpdateUsing(immediateComponentStore); immediateComponentStore.ClearAll(); } @@ -209,6 +214,7 @@ namespace Encompass foreach (var entityID in entitiesMarkedForRemoval) { existingComponentStore.Remove(entityID); + immediateComponentStore.Remove(entityID); upToDateComponentStore.Remove(entityID); drawLayerManager.UnRegisterEntityWithLayer(entityID); } diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 5fae89a..368d694 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -21,8 +21,8 @@ namespace Encompass private readonly int entityCapacity; private readonly List engines = new List(); private readonly DirectedGraph engineGraph = GraphBuilder.DirectedGraph(); - private readonly ComponentStore startingComponentStoreForComponentManager; - private readonly ComponentStore startingComponentStoreForComponentUpdateManager; + private readonly ComponentStore startingExistingComponentStore; + private readonly ComponentStore startingUpToDateComponentStore; private readonly ComponentManager componentManager; private readonly EntityManager entityManager; @@ -53,8 +53,8 @@ namespace Encompass entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(entityManager, drawLayerManager); - startingComponentStoreForComponentManager = new ComponentStore(typeToIndex); - startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex); + startingExistingComponentStore = new ComponentStore(typeToIndex); + startingUpToDateComponentStore = new ComponentStore(typeToIndex); } /// @@ -89,8 +89,8 @@ namespace Encompass RegisterComponentType(); componentTypesToRegister.Add(typeof(TComponent)); - startingComponentStoreForComponentManager.Set(entity.ID, component); - startingComponentStoreForComponentUpdateManager.Set(entity.ID, component); + startingExistingComponentStore.Set(entity.ID, component); + startingUpToDateComponentStore.Set(entity.ID, component); if (component is IDrawableComponent drawableComponent) { @@ -105,8 +105,8 @@ namespace Encompass { typeToIndex.Add(typeof(TComponent), typeToIndex.Count); componentManager.RegisterComponentType(); - startingComponentStoreForComponentManager.RegisterComponentType(); - startingComponentStoreForComponentUpdateManager.RegisterComponentType(); + startingExistingComponentStore.RegisterComponentType(); + startingUpToDateComponentStore.RegisterComponentType(); } } @@ -391,7 +391,8 @@ namespace Encompass renderManager ); - componentManager.SetComponentStore(startingComponentStoreForComponentManager); + componentManager.SetExistingComponentStore(startingExistingComponentStore); + componentManager.SetUpToDateComponentStore(startingUpToDateComponentStore); trackingManager.InitializeTracking(entityManager.EntityIDs); From 6f00191258aab2d416b1350683b59a304197c041 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 19:08:32 -0800 Subject: [PATCH 33/37] tightening up replay behavior --- .../Collections/ComponentDeltaStore.cs | 8 ++++++ encompass-cs/Collections/ComponentStore.cs | 3 ++- encompass-cs/ComponentManager.cs | 27 ++++++++++++------- test/ComponentTest.cs | 2 ++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/encompass-cs/Collections/ComponentDeltaStore.cs b/encompass-cs/Collections/ComponentDeltaStore.cs index b4f5d89..9b212d0 100644 --- a/encompass-cs/Collections/ComponentDeltaStore.cs +++ b/encompass-cs/Collections/ComponentDeltaStore.cs @@ -23,6 +23,14 @@ namespace Encompass } } + public override void Set(int entityID, TComponent component) + { + base.Set(entityID, component); + var replayer = _replayers[typeof(TComponent)]; + _currentReplayers.Add(replayer); + replayer.UnMarkRemoval(entityID); + } + public override bool Set(int entityID, TComponent component, int priority) { var result = base.Set(entityID, component, priority); diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index d879c4e..9097c52 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -57,7 +57,7 @@ namespace Encompass return Lookup().Get(entityID); } - public void Set(int entityID, TComponent component) where TComponent : struct, IComponent + public virtual void Set(int entityID, TComponent component) where TComponent : struct, IComponent { Lookup().Set(entityID, component); ComponentBitSet.Set(entityID); @@ -86,6 +86,7 @@ namespace Encompass public void ForceRemove(int entityID) where TComponent : struct, IComponent { Lookup().ForceRemove(entityID); + ComponentBitSet.RemoveComponent(entityID); } public virtual void Remove(int entityID) diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 26836bd..93bd754 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -8,7 +8,8 @@ namespace Encompass private readonly DrawLayerManager drawLayerManager; private readonly ComponentStore existingComponentStore; - private readonly ComponentDeltaStore immediateComponentStore; + private readonly ComponentStore immediateComponentStore; + private readonly ComponentDeltaStore replayStore; private ComponentStore upToDateComponentStore; public Dictionary TypeToIndex { get; } @@ -22,7 +23,8 @@ namespace Encompass { this.drawLayerManager = drawLayerManager; existingComponentStore = new ComponentStore(typeToIndex); - immediateComponentStore = new ComponentDeltaStore(typeToIndex); + immediateComponentStore = new ComponentStore(typeToIndex); + replayStore = new ComponentDeltaStore(typeToIndex); upToDateComponentStore = new ComponentStore(typeToIndex); TypeToIndex = typeToIndex; } @@ -31,6 +33,7 @@ namespace Encompass { existingComponentStore.RegisterComponentType(); immediateComponentStore.RegisterComponentType(); + replayStore.RegisterComponentType(); upToDateComponentStore.RegisterComponentType(); } @@ -51,20 +54,16 @@ namespace Encompass internal void WriteComponents() { - SetExistingComponentStore(upToDateComponentStore); - upToDateComponentStore.UpdateUsing(immediateComponentStore); + existingComponentStore.UpdateUsing(replayStore); immediateComponentStore.ClearAll(); - } - - internal void AddExistingComponent(int entityID, TComponent component) where TComponent : struct, IComponent - { - upToDateComponentStore.Set(entityID, component); + replayStore.ClearAll(); } internal bool AddImmediateComponent(int entityID, TComponent component, int priority) where TComponent : struct, IComponent { if (immediateComponentStore.Set(entityID, component, priority)) { + replayStore.Set(entityID, component); upToDateComponentStore.Set(entityID, component); return true; } @@ -74,7 +73,12 @@ namespace Encompass public bool UpdateComponent(int entityID, TComponent component, int priority) where TComponent : struct, IComponent { - return upToDateComponentStore.Set(entityID, component, priority); + var result = upToDateComponentStore.Set(entityID, component, priority); + if (result) + { + replayStore.Set(entityID, component); + } + return result; } // existing or immediate reads @@ -215,6 +219,7 @@ namespace Encompass { existingComponentStore.Remove(entityID); immediateComponentStore.Remove(entityID); + replayStore.Remove(entityID); upToDateComponentStore.Remove(entityID); drawLayerManager.UnRegisterEntityWithLayer(entityID); } @@ -226,6 +231,7 @@ namespace Encompass { if (immediateComponentStore.Remove(entityID, priority)) { + replayStore.Remove(entityID, priority); upToDateComponentStore.Remove(entityID, priority); drawLayerManager.UnRegisterComponentWithLayer(entityID); return true; @@ -237,6 +243,7 @@ namespace Encompass { if (upToDateComponentStore.Remove(entityID, priority)) { + replayStore.Remove(entityID, priority); drawLayerManager.UnRegisterComponentWithLayer(entityID); } } diff --git a/test/ComponentTest.cs b/test/ComponentTest.cs index ce2fd5a..327869e 100644 --- a/test/ComponentTest.cs +++ b/test/ComponentTest.cs @@ -75,6 +75,7 @@ namespace Tests var world = worldBuilder.Build(); world.Update(0.01); + world.Update(0.01); } [Test] @@ -258,6 +259,7 @@ namespace Tests var world = worldBuilder.Build(); + world.Update(0.01); world.Update(0.01); Assert.AreEqual(mockComponent, gottenMockComponent); From 3ef34d37aeab04205e7d6f4aaa5cbf6d401bf022 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 19:43:05 -0800 Subject: [PATCH 34/37] fix bug where priorities were not cleared at end of frame --- encompass-cs/Collections/ComponentStore.cs | 8 ++++++++ encompass-cs/Collections/TypedComponentStore.cs | 6 ++++++ encompass-cs/ComponentManager.cs | 2 ++ test/ComponentTest.cs | 14 ++++++++++---- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 9097c52..0645510 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -124,6 +124,14 @@ namespace Encompass Lookup().Clear(); } + public virtual void ClearAllPriorities() + { + foreach (var store in Stores.Values) + { + store.ClearPriorities(); + } + } + public virtual void ClearAll() { ComponentBitSet.Clear(); diff --git a/encompass-cs/Collections/TypedComponentStore.cs b/encompass-cs/Collections/TypedComponentStore.cs index 89dfd8f..78a8f20 100644 --- a/encompass-cs/Collections/TypedComponentStore.cs +++ b/encompass-cs/Collections/TypedComponentStore.cs @@ -11,6 +11,7 @@ namespace Encompass public abstract bool Remove(int entity, int priority); public abstract void ForceRemove(int entity); public abstract void Clear(); + public abstract void ClearPriorities(); } internal class TypedComponentStore : TypedComponentStore where TComponent : struct, IComponent @@ -73,6 +74,11 @@ namespace Encompass priorities.Clear(); } + public override void ClearPriorities() + { + priorities.Clear(); + } + public IEnumerable<(TComponent, int)> All() { foreach (var kvp in store) diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 93bd754..ded956e 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -55,6 +55,8 @@ namespace Encompass internal void WriteComponents() { existingComponentStore.UpdateUsing(replayStore); + existingComponentStore.ClearAllPriorities(); + upToDateComponentStore.ClearAllPriorities(); immediateComponentStore.ClearAll(); replayStore.ClearAll(); } diff --git a/test/ComponentTest.cs b/test/ComponentTest.cs index 327869e..1b94e51 100644 --- a/test/ComponentTest.cs +++ b/test/ComponentTest.cs @@ -98,7 +98,6 @@ namespace Tests } [Reads(typeof(MockComponent))] - [WritesImmediate(typeof(MockComponent))] [Writes(typeof(MockComponent))] class OverwriteEngine : Engine { @@ -106,12 +105,11 @@ namespace Tests { foreach (var (mockComponent, entity) in ReadComponentsIncludingEntity()) { - SetComponent(entity, new MockComponent { myInt = 420 }); + SetComponent(entity, new MockComponent { myInt = mockComponent.myInt + 1 }); } } } - [ReadsImmediate(typeof(MockComponent))] [Reads(typeof(MockComponent))] class ReadMockComponentEngine : Engine { @@ -129,12 +127,20 @@ namespace Tests worldBuilder.AddEngine(new ReadMockComponentEngine()); var entity = worldBuilder.CreateEntity(); - worldBuilder.SetComponent(entity, new MockComponent { }); + worldBuilder.SetComponent(entity, new MockComponent { myInt = 420 }); var world = worldBuilder.Build(); world.Update(0.01); Assert.That(gottenMockComponent.myInt, Is.EqualTo(420)); + + world.Update(0.01); + + Assert.That(gottenMockComponent.myInt, Is.EqualTo(421)); + + world.Update(0.01); + + Assert.That(gottenMockComponent.myInt, Is.EqualTo(422)); } [Reads(typeof(MockComponent))] From df7abf4cf30ee2aa83fa3129fce1d5fac7b39619 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 20:36:23 -0800 Subject: [PATCH 35/37] adds ability to retrieve messages by entity --- encompass-cs/Collections/MessageStore.cs | 5 ++ encompass-cs/Collections/TypedMessageStore.cs | 16 +++++ encompass-cs/Engine.cs | 5 ++ encompass-cs/Interfaces/IHasEntity.cs | 7 ++ encompass-cs/MessageManager.cs | 7 +- test/EngineTest.cs | 68 +++++++++++++++++++ 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 encompass-cs/Interfaces/IHasEntity.cs diff --git a/encompass-cs/Collections/MessageStore.cs b/encompass-cs/Collections/MessageStore.cs index 12aef7b..9fb586a 100644 --- a/encompass-cs/Collections/MessageStore.cs +++ b/encompass-cs/Collections/MessageStore.cs @@ -48,6 +48,11 @@ namespace Encompass return Lookup().Any(); } + public IEnumerable WithEntity(int entityID) where TMessage : struct, IMessage, IHasEntity + { + return Lookup().WithEntity(entityID); + } + public void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta) { foreach (var store in Stores.Values) diff --git a/encompass-cs/Collections/TypedMessageStore.cs b/encompass-cs/Collections/TypedMessageStore.cs index 06fc648..aef3a5c 100644 --- a/encompass-cs/Collections/TypedMessageStore.cs +++ b/encompass-cs/Collections/TypedMessageStore.cs @@ -13,6 +13,7 @@ namespace Encompass private readonly List store = new List(128); private readonly List<(TMessage, double)> delayedStore = new List<(TMessage, double)>(128); private readonly List<(TMessage, double)> delayedStoreIgnoringTimeDilation = new List<(TMessage, double)>(128); + private readonly Dictionary> entityToMessage = new Dictionary>(); public override void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta) { @@ -54,6 +55,12 @@ namespace Encompass public void Add(TMessage message) { store.Add(message); + if (message is IHasEntity entityMessage) + { + var entityID = entityMessage.Entity.ID; + if (!entityToMessage.ContainsKey(entityID)) { entityToMessage.Add(entityID, new List()); } + entityToMessage[entityID].Add(message); + } } public void Add(TMessage message, double time) @@ -81,9 +88,18 @@ namespace Encompass return store; } + public IEnumerable WithEntity(int entityID) + { + return entityToMessage[entityID]; + } + public override void Clear() { store.Clear(); + foreach (var set in entityToMessage.Values) + { + set.Clear(); + } } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 9b2b2b0..9520561 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -647,6 +647,11 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } + protected IEnumerable MessagesWithEntity(Entity entity) where TMessage : struct, IMessage, IHasEntity + { + return messageManager.WithEntity(entity.ID); + } + internal void CheckAndUpdateTracking(int entityID) { if (_trackedEntities.Contains(entityID) && !entityQuery.CheckEntity(entityID, componentManager.ExistingBits)) diff --git a/encompass-cs/Interfaces/IHasEntity.cs b/encompass-cs/Interfaces/IHasEntity.cs new file mode 100644 index 0000000..9d3493e --- /dev/null +++ b/encompass-cs/Interfaces/IHasEntity.cs @@ -0,0 +1,7 @@ +namespace Encompass +{ + public interface IHasEntity + { + Entity Entity { get; } + } +} diff --git a/encompass-cs/MessageManager.cs b/encompass-cs/MessageManager.cs index 7da56bf..1256946 100644 --- a/encompass-cs/MessageManager.cs +++ b/encompass-cs/MessageManager.cs @@ -14,7 +14,7 @@ namespace Encompass internal void AddMessage(TMessage message) where TMessage : struct, IMessage { - messageStore.AddMessage(message); + messageStore.AddMessage(message); } internal void AddMessage(TMessage message, double time) where TMessage : struct, IMessage @@ -51,5 +51,10 @@ namespace Encompass { return messageStore.First(); } + + internal IEnumerable WithEntity(int entityID) where TMessage : struct, IMessage, IHasEntity + { + return messageStore.WithEntity(entityID); + } } } diff --git a/test/EngineTest.cs b/test/EngineTest.cs index e9068d9..7e095dd 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -415,6 +415,74 @@ namespace Tests Assert.Throws(() => world.Update(0.01f)); } + struct EntityMessage : IMessage, IHasEntity + { + public EntityMessage(Entity entity, int myInt) + { + Entity = entity; + MyInt = myInt; + } + + public Entity Entity { get; } + public int MyInt { get; } + } + + [Sends(typeof(EntityMessage), typeof(MockMessage))] + class EntityMessageEmitterEngine : Engine + { + private Entity _entity; + + public EntityMessageEmitterEngine(Entity entity) + { + _entity = entity; + } + + public override void Update(double dt) + { + SendMessage(new EntityMessage(_entity, 2)); + SendMessage(new EntityMessage(_entity, 4)); + SendMessage(new EntityMessage(_entity, 5)); + SendMessage(new MockMessage()); + } + } + + static List entityMessageResults; + + [Receives(typeof(EntityMessage))] + class EntityMessageReceiverEngine : Engine + { + private Entity _entity; + + public EntityMessageReceiverEngine(Entity entity) + { + _entity = entity; + } + + public override void Update(double dt) + { + entityMessageResults = MessagesWithEntity(_entity).ToList(); + } + } + + [Test] + public void MessagesWithEntity() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.AddEngine(new EntityMessageEmitterEngine(entity)); + worldBuilder.AddEngine(new EntityMessageReceiverEngine(entity)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + entityMessageResults.Should().HaveCount(3); + entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 2)); + entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 4)); + entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 5)); + } + class SomeComponentTestEngine : Engine { public override void Update(double dt) From 9057228795a13e1bd3e1fccf92047710dd165073 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 20:41:35 -0800 Subject: [PATCH 36/37] fix bug when no messages existed for queried entity --- encompass-cs/Collections/TypedMessageStore.cs | 2 +- test/EngineTest.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/encompass-cs/Collections/TypedMessageStore.cs b/encompass-cs/Collections/TypedMessageStore.cs index aef3a5c..54aebe6 100644 --- a/encompass-cs/Collections/TypedMessageStore.cs +++ b/encompass-cs/Collections/TypedMessageStore.cs @@ -90,7 +90,7 @@ namespace Encompass public IEnumerable WithEntity(int entityID) { - return entityToMessage[entityID]; + return entityToMessage.ContainsKey(entityID) ? entityToMessage[entityID] : System.Linq.Enumerable.Empty(); } public override void Clear() diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 7e095dd..74dae7e 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -483,6 +483,20 @@ namespace Tests entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 5)); } + [Test] + public void NoMessagesWithEntity() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.AddEngine(new EntityMessageReceiverEngine(entity)); + + var world = worldBuilder.Build(); + world.Update(0.01); + + entityMessageResults.Should().BeEmpty(); + } + class SomeComponentTestEngine : Engine { public override void Update(double dt) From 926c05a4dbc98785e71ef59a3e7202de9473b817 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 29 Dec 2019 23:55:48 -0800 Subject: [PATCH 37/37] rename to ReadMessagesWithEntity --- encompass-cs/Engine.cs | 8 +++++++- encompass-cs/UberEngine.cs | 4 ++++ test/EngineTest.cs | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 9520561..aca9b61 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -647,7 +647,13 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } - protected IEnumerable MessagesWithEntity(Entity entity) where TMessage : struct, IMessage, IHasEntity + /// + /// Efficiently reads Messages of a given type that all reference the same Entity. + /// + /// The Message subtype. + /// The entity that all messages in the IEnumerable refer to. + /// + protected IEnumerable ReadMessagesWithEntity(Entity entity) where TMessage : struct, IMessage, IHasEntity { return messageManager.WithEntity(entity.ID); } diff --git a/encompass-cs/UberEngine.cs b/encompass-cs/UberEngine.cs index 7f8259d..7abfc7d 100644 --- a/encompass-cs/UberEngine.cs +++ b/encompass-cs/UberEngine.cs @@ -60,6 +60,10 @@ namespace Encompass CallGenericMethod(type, "ReadMessage", null); CallGenericMethod(type, "ReadMessages", null); CallGenericMethod(type, "SomeMessage", null); + if (typeof(IHasEntity).IsAssignableFrom(type)) + { + CallGenericMethod(type, "ReadMessagesWithEntity", new Type[] { typeof(Entity) }, new object[] { Entity }); + } } } diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 74dae7e..de4d956 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -460,7 +460,7 @@ namespace Tests public override void Update(double dt) { - entityMessageResults = MessagesWithEntity(_entity).ToList(); + entityMessageResults = ReadMessagesWithEntity(_entity).ToList(); } }