From 4f75aeebf18b5ac8b1ff98b2acd5ab1314129658 Mon Sep 17 00:00:00 2001 From: thatcosmonaut Date: Mon, 13 Jan 2020 14:59:18 -0800 Subject: [PATCH 01/12] add SomeMessageWithEntity method to Engine --- encompass-cs/Collections/MessageStore.cs | 5 +++++ encompass-cs/Collections/TypedMessageStore.cs | 5 +++++ encompass-cs/Engine.cs | 5 +++++ encompass-cs/MessageManager.cs | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/encompass-cs/Collections/MessageStore.cs b/encompass-cs/Collections/MessageStore.cs index 9fb586a..5e7186f 100644 --- a/encompass-cs/Collections/MessageStore.cs +++ b/encompass-cs/Collections/MessageStore.cs @@ -53,6 +53,11 @@ namespace Encompass return Lookup().WithEntity(entityID); } + public bool SomeWithEntity(int entityID) where TMessage : struct, IMessage, IHasEntity + { + return Lookup().SomeWithEntity(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 54aebe6..bfa7a8b 100644 --- a/encompass-cs/Collections/TypedMessageStore.cs +++ b/encompass-cs/Collections/TypedMessageStore.cs @@ -93,6 +93,11 @@ namespace Encompass return entityToMessage.ContainsKey(entityID) ? entityToMessage[entityID] : System.Linq.Enumerable.Empty(); } + public bool SomeWithEntity(int entityID) + { + return entityToMessage.ContainsKey(entityID) && entityToMessage[entityID].Count > 0; + } + public override void Clear() { store.Clear(); diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index aca9b61..e546e5c 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -658,6 +658,11 @@ namespace Encompass return messageManager.WithEntity(entity.ID); } + protected bool SomeMessageWithEntity(Entity entity) where TMessage : struct, IMessage, IHasEntity + { + return messageManager.SomeWithEntity(entity.ID); + } + internal void CheckAndUpdateTracking(int entityID) { if (_trackedEntities.Contains(entityID) && !entityQuery.CheckEntity(entityID, componentManager.ExistingBits)) diff --git a/encompass-cs/MessageManager.cs b/encompass-cs/MessageManager.cs index 1256946..3876f03 100644 --- a/encompass-cs/MessageManager.cs +++ b/encompass-cs/MessageManager.cs @@ -56,5 +56,10 @@ namespace Encompass { return messageStore.WithEntity(entityID); } + + internal bool SomeWithEntity(int entityID) where TMessage : struct, IMessage, IHasEntity + { + return messageStore.SomeWithEntity(entityID); + } } } From c1e1f7f5caee53c709e5f0eddafae2a48f342163 Mon Sep 17 00:00:00 2001 From: thatcosmonaut Date: Tue, 28 Jan 2020 14:05:11 -0800 Subject: [PATCH 02/12] ordered renderers register components with world builder --- encompass-cs/WorldBuilder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 368d694..059d03f 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -196,6 +196,8 @@ namespace Encompass /// public OrderedRenderer AddOrderedRenderer(OrderedRenderer renderer) where TComponent : struct, IComponent, IDrawableComponent { + RegisterComponentType(); + componentTypesToRegister.Add(typeof(TComponent)); renderer.AssignEntityManager(entityManager); renderer.AssignComponentManager(componentManager); renderManager.RegisterOrderedRenderer(renderer.InternalRender); From 4be62122ab2b8aa25fd5fe3c99da1aade0f944c0 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Wed, 11 Mar 2020 17:02:00 -0700 Subject: [PATCH 03/12] fix lookup crash on component that is only read by renderers --- encompass-cs/Collections/ComponentStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 0645510..dbe1a95 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -33,7 +33,7 @@ namespace Encompass private TypedComponentStore Lookup() where TComponent : struct, IComponent { - //RegisterComponentType(); + if (!Stores.ContainsKey(typeof(TComponent))) { RegisterComponentType(); } return Stores[typeof(TComponent)] as TypedComponentStore; } From 77272f10d765db10a68d7b314cc252ad41a7ef07 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Mon, 16 Mar 2020 19:00:42 -0700 Subject: [PATCH 04/12] message read checks + started AddComponent --- TODO | 2 - encompass-cs/Engine.cs | 71 ++++++++++++++++++++++++++++------ encompass-cs/EntityManager.cs | 2 +- encompass-cs/MessageManager.cs | 7 ++++ 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/TODO b/TODO index 2822a8e..8094915 100644 --- a/TODO +++ b/TODO @@ -5,7 +5,5 @@ - auto destroy entities that no longer have components -- fast lookup for messages that contain entity references instead of `Where` loop? - - look at test coverage - docs diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index e546e5c..b404022 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -53,6 +53,8 @@ namespace Encompass } } + private HashSet _newlyCreatedEntities = new HashSet(); + protected Engine() { ID = Guid.NewGuid(); @@ -158,6 +160,19 @@ namespace Encompass this.trackingManager = trackingManager; } + internal void CheckMessageRead() where TMessage : struct, IMessage + { + if (!receiveTypes.Contains(typeof(TMessage))) + { + throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name); + } + } + + private bool EntityCreatedThisFrame(int entityID) + { + return _newlyCreatedEntities.Contains(entityID); + } + /// /// Runs once per World update with the calculated delta-time. /// @@ -169,7 +184,9 @@ namespace Encompass /// protected Entity CreateEntity() { - return entityManager.CreateEntity(); + var entity = entityManager.CreateEntity(); + _newlyCreatedEntities.Add(entity.ID); + return entity; } /// @@ -448,6 +465,28 @@ namespace Encompass } } + /// + /// An alternative to SetComponent that can be used for new Entities and does not require setting write priority. + /// + /// + /// Thrown when the Engine does not declare that it Writes the given Component Type. + /// + protected void AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent + { + if (!_newlyCreatedEntities.Contains(entity.ID)) + { + throw new IllegalWriteException("AddComponent used on Entity that was not created in this context. Use SetComponent instead."); + } + + componentManager.AddComponent(entity.ID, component); + trackingManager.RegisterAddition(entity.ID, typeof(TComponent)); + + if (component is IDrawableComponent drawableComponent) + { + componentManager.RegisterDrawableComponent(entity.ID, component, drawableComponent.Layer); + } + } + /// /// Sends a Message. /// @@ -490,11 +529,7 @@ namespace Encompass /// protected IEnumerable ReadMessages() where TMessage : struct, IMessage { - if (!receiveTypes.Contains(typeof(TMessage))) - { - throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name); - } - + CheckMessageRead(); return messageManager.GetMessagesByType(); } @@ -506,6 +541,7 @@ namespace Encompass /// protected TMessage ReadMessage() where TMessage : struct, IMessage { + CheckMessageRead(); return messageManager.First(); } @@ -517,11 +553,7 @@ namespace Encompass /// protected bool SomeMessage() where TMessage : struct, IMessage { - if (!receiveTypes.Contains(typeof(TMessage))) - { - throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name); - } - + CheckMessageRead(); return messageManager.Any(); } @@ -648,18 +680,33 @@ namespace Encompass } /// - /// Efficiently reads Messages of a given type that all reference the same Entity. + /// Efficiently reads Messages of a given type that all reference the given Entity. /// /// The Message subtype. /// The entity that all messages in the IEnumerable refer to. /// protected IEnumerable ReadMessagesWithEntity(Entity entity) where TMessage : struct, IMessage, IHasEntity { + CheckMessageRead(); return messageManager.WithEntity(entity.ID); } + /// + /// Efficiently reads a single Message of a given type that references a given Entity. + /// It is recommended to use this method in conjunction with SomeMessageWithEntity to prevent errors. + /// + protected TMessage ReadMessageWithEntity(Entity entity) where TMessage : struct, IMessage, IHasEntity + { + CheckMessageRead(); + return messageManager.WithEntitySingular(entity.ID); + } + + /// + /// Efficiently checks if any Message of a given type referencing a given Entity exists. + /// protected bool SomeMessageWithEntity(Entity entity) where TMessage : struct, IMessage, IHasEntity { + CheckMessageRead(); return messageManager.SomeWithEntity(entity.ID); } diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index d9e9337..483b36b 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -8,7 +8,7 @@ namespace Encompass 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; diff --git a/encompass-cs/MessageManager.cs b/encompass-cs/MessageManager.cs index 3876f03..c9e53ae 100644 --- a/encompass-cs/MessageManager.cs +++ b/encompass-cs/MessageManager.cs @@ -57,6 +57,13 @@ namespace Encompass return messageStore.WithEntity(entityID); } + internal TMessage WithEntitySingular(int entityID) where TMessage : struct, IMessage, IHasEntity + { + var enumerator = messageStore.WithEntity(entityID).GetEnumerator(); + enumerator.MoveNext(); + return enumerator.Current; + } + internal bool SomeWithEntity(int entityID) where TMessage : struct, IMessage, IHasEntity { return messageStore.SomeWithEntity(entityID); From 511a05f18ec5b677117ebf66014b90eb4cb40306 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Tue, 17 Mar 2020 15:29:16 -0700 Subject: [PATCH 05/12] AddComponent for newly created Entities --- encompass-cs/ComponentManager.cs | 15 ++++- encompass-cs/Engine.cs | 16 +++++- encompass-cs/World.cs | 2 + test/EngineTest.cs | 99 +++++++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 4 deletions(-) diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index ded956e..8c45186 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -73,7 +73,14 @@ namespace Encompass return false; } - public bool UpdateComponent(int entityID, TComponent component, int priority) where TComponent : struct, IComponent + internal void AddImmediateComponent(int entityID, TComponent component) where TComponent : struct, IComponent + { + immediateComponentStore.Set(entityID, component); + replayStore.Set(entityID, component); + upToDateComponentStore.Set(entityID, component); + } + + internal bool UpdateComponent(int entityID, TComponent component, int priority) where TComponent : struct, IComponent { var result = upToDateComponentStore.Set(entityID, component, priority); if (result) @@ -83,6 +90,12 @@ namespace Encompass return result; } + internal void AddComponent(int entityID, TComponent component) where TComponent : struct, IComponent + { + upToDateComponentStore.Set(entityID, component); + replayStore.Set(entityID, component); + } + // existing or immediate reads internal IEnumerable<(TComponent, int)> ReadExistingAndImmediateComponentsByType() where TComponent : struct, IComponent diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index b404022..8be53dd 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -173,6 +173,11 @@ namespace Encompass return _newlyCreatedEntities.Contains(entityID); } + internal void ClearNewlyCreatedEntities() + { + _newlyCreatedEntities.Clear(); + } + /// /// Runs once per World update with the calculated delta-time. /// @@ -478,7 +483,16 @@ namespace Encompass throw new IllegalWriteException("AddComponent used on Entity that was not created in this context. Use SetComponent instead."); } - componentManager.AddComponent(entity.ID, component); + if (writeImmediateTypes.Contains(typeof(TComponent))) + { + componentManager.AddImmediateComponent(entity.ID, component); + trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent)); + } + else + { + componentManager.AddComponent(entity.ID, component); + } + trackingManager.RegisterAddition(entity.ID, typeof(TComponent)); if (component is IDrawableComponent drawableComponent) diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 677bf8f..fcc3384 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -54,6 +54,8 @@ namespace Encompass { engine.Update(dt); } + + engine.ClearNewlyCreatedEntities(); } messageManager.ClearMessages(); diff --git a/test/EngineTest.cs b/test/EngineTest.cs index de4d956..b415eb8 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -415,8 +415,8 @@ namespace Tests Assert.Throws(() => world.Update(0.01f)); } - struct EntityMessage : IMessage, IHasEntity - { + struct EntityMessage : IMessage, IHasEntity + { public EntityMessage(Entity entity, int myInt) { Entity = entity; @@ -1240,6 +1240,101 @@ namespace Tests undilatedDeltaTime.Should().Be(0.5); } + class AddComponentWithoutPriorityEngine : Engine + { + public override void Update(double dt) + { + var entity = CreateEntity(); + AddComponent(entity, new MockComponent()); + + var entityB = CreateEntity(); + AddComponent(entityB, new MockComponent()); + } + } + + [Test] + public void AddComponent() + { + var worldBuilder = new WorldBuilder(); + + worldBuilder.AddEngine(new AddComponentWithoutPriorityEngine()); + worldBuilder.AddEngine(new ReadComponentsTestEngine()); + + var world = worldBuilder.Build(); + + world.Update(0.01); + world.Update(0.01); + + resultComponents.Should().HaveCount(2); + + world.Update(0.01); + + resultComponents.Should().HaveCount(4); + } + + [Reads(typeof(MockComponent))] + class AddComponentToPreviouslyExistingEntityEngine : Engine + { + public override void Update(double dt) + { + var (component, entity) = ReadComponentIncludingEntity(); + + AddComponent(entity, new MockComponent()); + } + } + + [Test] + public void AddComponentToPreviouslyExistingEntityTest() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(new AddComponentToPreviouslyExistingEntityEngine()); + + var entity = worldBuilder.CreateEntity(); + worldBuilder.SetComponent(entity, new MockComponent()); + + var world = worldBuilder.Build(); + + Assert.Throws(() => world.Update(0.01)); + } + + [WritesImmediate(typeof(MockComponentB))] + class AddImmediateComponentEngine : Engine + { + public override void Update(double dt) + { + var entity = CreateEntity(); + AddComponent(entity, new MockComponentB(5)); + } + } + + [ReadsImmediate(typeof(MockComponentB))] + class ReadImmediateComponentEngine : Engine + { + public override void Update(double dt) + { + var (component, entity) = ReadComponentIncludingEntity(); + + getComponentResult = component; + } + } + + [Test] + public void AddImmediateComponentTest() + { + getComponentResult = default(MockComponentB); + + var worldBuilder = new WorldBuilder(); + + worldBuilder.AddEngine(new AddImmediateComponentEngine()); + worldBuilder.AddEngine(new ReadImmediateComponentEngine()); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + getComponentResult.Should().Be(new MockComponentB(5)); + } + public class QueryTests { struct MockComponentB : IComponent { } From 4d67b4ddf7a120993a341ee59c057f7bad561304 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Tue, 17 Mar 2020 16:04:27 -0700 Subject: [PATCH 06/12] add test for singular ReadMessageWithEntity --- test/EngineTest.cs | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/EngineTest.cs b/test/EngineTest.cs index b415eb8..29da00c 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -497,6 +497,58 @@ namespace Tests entityMessageResults.Should().BeEmpty(); } + [Sends(typeof(EntityMessage), typeof(MockMessage))] + class EntityMessageSingularEmitterEngine : Engine + { + private Entity _entity; + + public EntityMessageSingularEmitterEngine(Entity entity) + { + _entity = entity; + } + + public override void Update(double dt) + { + SendMessage(new EntityMessage(_entity, 2)); + SendMessage(new MockMessage()); + } + } + + static EntityMessage entityMessageResult; + + [Receives(typeof(EntityMessage))] + class SingularMessageWithEntityEngine : Engine + { + private Entity _entity; + + public SingularMessageWithEntityEngine(Entity entity) + { + _entity = entity; + } + + public override void Update(double dt) + { + entityMessageResult = ReadMessageWithEntity(_entity); + } + } + + [Test] + public void MessageWithEntity() + { + var worldBuilder = new WorldBuilder(); + + var entity = worldBuilder.CreateEntity(); + + worldBuilder.AddEngine(new EntityMessageSingularEmitterEngine(entity)); + worldBuilder.AddEngine(new SingularMessageWithEntityEngine(entity)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + entityMessageResult.Should().Be(new EntityMessage(entity, 2)); + } + class SomeComponentTestEngine : Engine { public override void Update(double dt) From f24ee21de01633fe2ea3c81256fd204483989674 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Tue, 17 Mar 2020 17:40:11 -0700 Subject: [PATCH 07/12] pruning empty entities --- encompass-cs/ComponentManager.cs | 6 ++++++ encompass-cs/Engine.cs | 8 ++++++++ encompass-cs/EntityManager.cs | 12 ++++++++++++ encompass-cs/World.cs | 1 + test/EngineTest.cs | 31 +++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+) diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 8c45186..046704e 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MoonTools.FastCollections; namespace Encompass { @@ -262,5 +263,10 @@ namespace Encompass drawLayerManager.UnRegisterComponentWithLayer(entityID); } } + + public bool UpToDateEntityIsEmpty(int entityID) + { + return upToDateComponentStore.EntityBitArray(entityID).AllFalse(); + } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 8be53dd..a7ba186 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -202,6 +202,14 @@ namespace Encompass return entityManager.EntityExists(entity.ID); } + /// + /// Returns true if an Entity with the specified ID exists. + /// + protected bool EntityExists(int entityID) + { + return entityManager.EntityExists(entityID); + } + /// /// Returns an Entity containing the specified Component type. /// diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index 483b36b..c800281 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -76,5 +76,17 @@ namespace Encompass entitiesMarkedForDestroy.Clear(); } + + // NOTE: this is very suboptimal + public void PruneEmptyEntities() + { + foreach (var id in EntityIDs) + { + if (componentManager.UpToDateEntityIsEmpty(id)) + { + MarkForDestroy(id); + } + } + } } } diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index fcc3384..b611d96 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -59,6 +59,7 @@ namespace Encompass } messageManager.ClearMessages(); + entityManager.PruneEmptyEntities(); entityManager.DestroyMarkedEntities(enginesInOrder); componentManager.RemoveMarkedComponents(); diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 29da00c..77ceeb1 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1387,6 +1387,37 @@ namespace Tests getComponentResult.Should().Be(new MockComponentB(5)); } + static bool entityExistsResult; + + class EntityExistsEngine : Engine + { + private int _id; + + public EntityExistsEngine(int id) + { + _id = id; + } + + public override void Update(double dt) + { + entityExistsResult = EntityExists(_id); + } + } + + [Test] + public void PruneEmptyEntities() + { + var worldBuilder = new WorldBuilder(); + var entity = worldBuilder.CreateEntity(); + var id = entity.ID; + + var world = worldBuilder.Build(); + + world.Update(0.01); + + entityExistsResult.Should().BeFalse(); + } + public class QueryTests { struct MockComponentB : IComponent { } From 4a962bac3cc190615f89289ad2335ebb08de0d73 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Tue, 17 Mar 2020 17:47:39 -0700 Subject: [PATCH 08/12] switch to DroneCI --- .circleci/config.yml | 46 -------------------------------------------- .drone.yml | 24 +++++++++++++++++++++++ TODO | 2 -- 3 files changed, 24 insertions(+), 48 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .drone.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c922620..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,46 +0,0 @@ -version: 2.1 - -defaults: &defaults - working_directory: ~/repo - docker: - - image: mcr.microsoft.com/dotnet/core/sdk:3.0 - environment: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - -jobs: - test: - <<: *defaults - steps: - - checkout - - run: dotnet restore - - run: dotnet build -c Release - - run: dotnet test -c Release - - persist_to_workspace: - root: . - paths: ./encompass-cs/bin - - deploy: - <<: *defaults - steps: - - checkout - - attach_workspace: - at: . - - run: dotnet nuget push ./encompass-cs/bin/Release/EncompassECS.Framework.*.nupkg -k $API_KEY -s $NUGET_SOURCE - -workflows: - version: 2 - test_and_deploy: - jobs: - - test: - filters: - tags: - only: /.*/ - - deploy: - requires: - - test - filters: - branches: - ignore: /.*/ - tags: - only: /^\d+\.\d+\.\d+(-preview\d+)?$/ diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..7d02a31 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,24 @@ + kind: pipeline + type: docker + name: default + + workspace: + path: /build + + steps: + - name: test + image: mcr.microsoft.com/dotnet/core/sdk:3.1 + commands: + - dotnet build -c Release + - dotnet test -c Release + + - name: deploy + image: mcr.microsoft.com/dotnet/core/sdk:3.1 + environment: + API_KEY: + from_secret: API_KEY + commands: + - dotnet nuget push /build/encompass-cs/bin/Release/EncompassECS.Framework.*.nupkg -s https://api.nuget.org/v3/index.json -k $API_KEY + when: + ref: + - refs/tags/*.*.* diff --git a/TODO b/TODO index 8094915..d96f6e9 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,5 @@ - method to remove all components of a type without destroying Entities - method to remove a component of a type without destroying entity -- auto destroy entities that no longer have components - - look at test coverage - docs From 1c3c6c6b69fc01fff18ababb300ad63c3341af64 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Wed, 18 Mar 2020 13:49:37 -0700 Subject: [PATCH 09/12] register and preload component types via reflection in worldbuilder --- encompass-cs/WorldBuilder.cs | 37 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 059d03f..3145626 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -36,7 +36,7 @@ namespace Encompass private readonly HashSet senders = new HashSet(); - private readonly HashSet componentTypesToRegister = new HashSet(); + private readonly HashSet componentTypesToPreload = new HashSet(); private readonly HashSet messageTypes = new HashSet(); @@ -87,8 +87,6 @@ namespace Encompass public void SetComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { RegisterComponentType(); - componentTypesToRegister.Add(typeof(TComponent)); - startingExistingComponentStore.Set(entity.ID, component); startingUpToDateComponentStore.Set(entity.ID, component); @@ -104,6 +102,7 @@ namespace Encompass if (!typeToIndex.ContainsKey(typeof(TComponent))) { typeToIndex.Add(typeof(TComponent), typeToIndex.Count); + componentTypesToPreload.Add(typeof(TComponent)); componentManager.RegisterComponentType(); startingExistingComponentStore.RegisterComponentType(); startingUpToDateComponentStore.RegisterComponentType(); @@ -115,11 +114,6 @@ namespace Encompass messageTypes.UnionWith(types); } - internal void AddComponentTypeToRegister(Type componentType) - { - componentTypesToRegister.Add(componentType); - } - /// /// Adds the specified Engine to the World. /// @@ -164,11 +158,6 @@ namespace Encompass } } - foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readImmediateTypes)) - { - AddComponentTypeToRegister(componentType); - } - foreach (var receiveType in engine.receiveTypes.Union(engine.readImmediateTypes)) { if (!typeToReaders.ContainsKey(receiveType)) @@ -197,7 +186,6 @@ namespace Encompass public OrderedRenderer AddOrderedRenderer(OrderedRenderer renderer) where TComponent : struct, IComponent, IDrawableComponent { RegisterComponentType(); - componentTypesToRegister.Add(typeof(TComponent)); renderer.AssignEntityManager(entityManager); renderer.AssignComponentManager(componentManager); renderManager.RegisterOrderedRenderer(renderer.InternalRender); @@ -366,16 +354,23 @@ namespace Encompass throw new EngineWriteConflictException(errorString); } - var engineOrder = new List(); - - foreach (var registeredComponentType in componentTypesToRegister) + // doing reflection to grab all component types, because not all writes need to be declared + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { - var method = typeof(WorldBuilder).GetMethod("RegisterComponentType", BindingFlags.NonPublic | BindingFlags.Instance); - var generic = method.MakeGenericMethod(registeredComponentType); - generic.Invoke(this, null); + foreach (var componentType in assembly.GetTypes()) + { + if (typeof(IComponent).IsAssignableFrom(componentType) && componentType.IsValueType && !componentType.IsEnum && !componentType.IsPrimitive) + { + var method = typeof(WorldBuilder).GetMethod("RegisterComponentType", BindingFlags.NonPublic | BindingFlags.Instance); + var generic = method.MakeGenericMethod(componentType); + generic.Invoke(this, null); + } + } } - PreloadJIT(componentTypesToRegister, messageTypes); + PreloadJIT(componentTypesToPreload, messageTypes); + + var engineOrder = new List(); foreach (var engine in engineGraph.TopologicalSort()) { From 9bd793e337151c39fc6aa5d0bb166df7261eff5f Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Wed, 18 Mar 2020 14:14:07 -0700 Subject: [PATCH 10/12] remove now unnecessary type lookup --- encompass-cs/Collections/ComponentStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index dbe1a95..aee17e8 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -33,7 +33,6 @@ namespace Encompass private TypedComponentStore Lookup() where TComponent : struct, IComponent { - if (!Stores.ContainsKey(typeof(TComponent))) { RegisterComponentType(); } return Stores[typeof(TComponent)] as TypedComponentStore; } From 130890b9cc64ec44b2b6c51189cf8784a1849888 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Wed, 18 Mar 2020 14:17:35 -0700 Subject: [PATCH 11/12] bump to 0.20.0 --- encompass-cs/encompass-cs.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/encompass-cs/encompass-cs.csproj b/encompass-cs/encompass-cs.csproj index f2c9458..9fda87b 100644 --- a/encompass-cs/encompass-cs.csproj +++ b/encompass-cs/encompass-cs.csproj @@ -3,7 +3,7 @@ netstandard2.0 Encompass EncompassECS.Framework - 0.19.0 + 0.20.0 Evan Hemsley true Moonside Games @@ -27,4 +27,4 @@ - \ No newline at end of file + From 7c1a798b01edccadbae6557f90c3ed9d0ad5ea95 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Wed, 18 Mar 2020 14:23:08 -0700 Subject: [PATCH 12/12] use clearly named method --- encompass-cs/Engine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index a7ba186..8a3cdf6 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -486,7 +486,7 @@ namespace Encompass /// protected void AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - if (!_newlyCreatedEntities.Contains(entity.ID)) + if (!EntityCreatedThisFrame(entity.ID)) { throw new IllegalWriteException("AddComponent used on Entity that was not created in this context. Use SetComponent instead."); }