message read checks + started AddComponent

pull/2/head
Evan Hemsley 2020-03-16 19:00:42 -07:00
parent c1e1f7f5ca
commit 77272f10d7
4 changed files with 67 additions and 15 deletions

2
TODO
View File

@ -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

View File

@ -53,6 +53,8 @@ namespace Encompass
}
}
private HashSet<int> _newlyCreatedEntities = new HashSet<int>();
protected Engine()
{
ID = Guid.NewGuid();
@ -158,6 +160,19 @@ namespace Encompass
this.trackingManager = trackingManager;
}
internal void CheckMessageRead<TMessage>() 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);
}
/// <summary>
/// Runs once per World update with the calculated delta-time.
/// </summary>
@ -169,7 +184,9 @@ namespace Encompass
/// </summary>
protected Entity CreateEntity()
{
return entityManager.CreateEntity();
var entity = entityManager.CreateEntity();
_newlyCreatedEntities.Add(entity.ID);
return entity;
}
/// <summary>
@ -448,6 +465,28 @@ namespace Encompass
}
}
/// <summary>
/// An alternative to SetComponent that can be used for new Entities and does not require setting write priority.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalWriteException">
/// Thrown when the Engine does not declare that it Writes the given Component Type.
/// </exception>
protected void AddComponent<TComponent>(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);
}
}
/// <summary>
/// Sends a Message.
/// </summary>
@ -490,11 +529,7 @@ namespace Encompass
/// </exception>
protected IEnumerable<TMessage> ReadMessages<TMessage>() 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<TMessage>();
return messageManager.GetMessagesByType<TMessage>();
}
@ -506,6 +541,7 @@ namespace Encompass
/// </exception>
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
{
CheckMessageRead<TMessage>();
return messageManager.First<TMessage>();
}
@ -517,11 +553,7 @@ namespace Encompass
/// </exception>
protected bool SomeMessage<TMessage>() 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<TMessage>();
return messageManager.Any<TMessage>();
}
@ -648,18 +680,33 @@ namespace Encompass
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TMessage">The Message subtype.</typeparam>
/// <param name="entity">The entity that all messages in the IEnumerable refer to.</param>
/// <returns></returns>
protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(Entity entity) where TMessage : struct, IMessage, IHasEntity
{
CheckMessageRead<TMessage>();
return messageManager.WithEntity<TMessage>(entity.ID);
}
/// <summary>
/// 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.
/// </summary>
protected TMessage ReadMessageWithEntity<TMessage>(Entity entity) where TMessage : struct, IMessage, IHasEntity
{
CheckMessageRead<TMessage>();
return messageManager.WithEntitySingular<TMessage>(entity.ID);
}
/// <summary>
/// Efficiently checks if any Message of a given type referencing a given Entity exists.
/// </summary>
protected bool SomeMessageWithEntity<TMessage>(Entity entity) where TMessage : struct, IMessage, IHasEntity
{
CheckMessageRead<TMessage>();
return messageManager.SomeWithEntity<TMessage>(entity.ID);
}

View File

@ -8,7 +8,7 @@ namespace Encompass
private readonly int entityCapacity;
private readonly IDManager idManager = new IDManager();
private readonly HashSet<int> IDs = new HashSet<int>();
private readonly HashSet<int> entitiesMarkedForDestroy = new HashSet<int>();
private readonly ComponentManager componentManager;

View File

@ -57,6 +57,13 @@ namespace Encompass
return messageStore.WithEntity<TMessage>(entityID);
}
internal TMessage WithEntitySingular<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
{
var enumerator = messageStore.WithEntity<TMessage>(entityID).GetEnumerator();
enumerator.MoveNext();
return enumerator.Current;
}
internal bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
{
return messageStore.SomeWithEntity<TMessage>(entityID);