From 77272f10d765db10a68d7b314cc252ad41a7ef07 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Mon, 16 Mar 2020 19:00:42 -0700 Subject: [PATCH] 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);