2019-06-15 07:39:08 +00:00
using System ;
using System.Reflection ;
2019-06-15 00:51:06 +00:00
using System.Collections.Generic ;
2019-06-17 01:11:35 +00:00
using System.Linq ;
2019-06-24 19:14:37 +00:00
using Encompass.Exceptions ;
2019-06-15 00:51:06 +00:00
2019-06-16 01:05:56 +00:00
namespace Encompass
{
2019-10-24 19:48:36 +00:00
/// <summary>
/// Engines are the Encompass notion of an ECS System.
/// They are responsible for reading the World state, reading messages, emitting messages, and creating or mutating Entities and Components.
/// Engines run once per World Update.
/// </summary>
2019-10-24 20:13:43 +00:00
public abstract class Engine : IEquatable < Engine >
2019-06-16 01:05:56 +00:00
{
2019-10-24 20:13:43 +00:00
public Guid ID ;
2019-07-19 01:20:38 +00:00
internal readonly HashSet < Type > sendTypes = new HashSet < Type > ( ) ;
2019-07-19 19:47:17 +00:00
internal readonly HashSet < Type > receiveTypes = new HashSet < Type > ( ) ;
2019-08-21 02:25:59 +00:00
internal readonly Dictionary < Type , int > writePriorities = new Dictionary < Type , int > ( ) ;
2019-06-15 07:39:08 +00:00
2019-06-15 00:51:06 +00:00
private EntityManager entityManager ;
2019-06-16 01:55:35 +00:00
private MessageManager messageManager ;
2019-08-20 00:30:31 +00:00
private ComponentManager componentManager ;
2019-08-01 23:24:57 +00:00
private ComponentMessageManager componentMessageManager ;
2019-06-15 00:51:06 +00:00
2019-06-20 17:46:15 +00:00
protected Engine ( )
2019-06-16 01:05:56 +00:00
{
2019-10-24 20:13:43 +00:00
ID = Guid . NewGuid ( ) ;
2019-07-19 19:47:17 +00:00
var sendsAttribute = GetType ( ) . GetCustomAttribute < Sends > ( false ) ;
if ( sendsAttribute ! = null )
2019-06-16 01:55:35 +00:00
{
2019-07-19 19:47:17 +00:00
sendTypes = sendsAttribute . sendTypes ;
}
2019-08-21 02:25:59 +00:00
var activatesAttribute = GetType ( ) . GetCustomAttribute < WritesPending > ( false ) ;
2019-07-19 19:47:17 +00:00
if ( activatesAttribute ! = null )
{
2019-08-21 02:25:59 +00:00
sendTypes . UnionWith ( activatesAttribute . writePendingTypes ) ;
2019-07-19 19:47:17 +00:00
}
2019-08-22 02:21:55 +00:00
var writesAttributes = GetType ( ) . GetCustomAttributes < Writes > ( false ) ;
foreach ( var writesAttribute in writesAttributes )
2019-07-20 00:50:13 +00:00
{
2019-08-21 02:25:59 +00:00
sendTypes . UnionWith ( writesAttribute . writeTypes ) ;
2019-08-22 02:21:55 +00:00
writePriorities = new Dictionary < Type , int > [ 2 ] { writePriorities , writesAttribute . priorities } . SelectMany ( dict = > dict ) . ToDictionary ( pair = > pair . Key , pair = > pair . Value ) ;
2019-07-20 00:50:13 +00:00
}
2019-07-19 19:47:17 +00:00
var receivesAttribute = GetType ( ) . GetCustomAttribute < Receives > ( false ) ;
if ( receivesAttribute ! = null )
{
receiveTypes = receivesAttribute . receiveTypes ;
2019-06-16 01:55:35 +00:00
}
2019-06-20 03:37:46 +00:00
var readsAttribute = GetType ( ) . GetCustomAttribute < Reads > ( false ) ;
2019-06-16 01:55:35 +00:00
if ( readsAttribute ! = null )
{
2019-07-19 19:47:17 +00:00
receiveTypes . UnionWith ( readsAttribute . readTypes ) ;
2019-06-16 01:55:35 +00:00
}
2019-07-23 17:17:53 +00:00
var readsPendingAttribute = GetType ( ) . GetCustomAttribute < ReadsPending > ( false ) ;
if ( readsPendingAttribute ! = null )
{
receiveTypes . UnionWith ( readsPendingAttribute . readPendingTypes ) ;
}
2019-06-15 07:39:08 +00:00
}
2019-10-24 20:13:43 +00:00
public override bool Equals ( object obj )
{
if ( obj is Engine )
{
return this . Equals ( ( Engine ) obj ) ;
}
return false ;
}
public bool Equals ( Engine other )
{
return other . ID = = ID ;
}
public override int GetHashCode ( )
{
return ID . GetHashCode ( ) ;
}
2019-06-16 01:05:56 +00:00
internal void AssignEntityManager ( EntityManager entityManager )
{
2019-06-15 00:51:06 +00:00
this . entityManager = entityManager ;
}
2019-06-16 01:05:56 +00:00
internal void AssignComponentManager ( ComponentManager componentManager )
{
2019-06-15 00:51:06 +00:00
this . componentManager = componentManager ;
}
2019-06-16 01:55:35 +00:00
internal void AssignMessageManager ( MessageManager messageManager )
{
this . messageManager = messageManager ;
}
2019-08-01 23:24:57 +00:00
internal void AssignComponentMessageManager ( ComponentMessageManager componentMessageManager )
{
this . componentMessageManager = componentMessageManager ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Runs once per World update with the calculated delta-time.
/// </summary>
/// <param name="dt">The time in seconds that has elapsed since the previous frame.</param>
2019-06-24 19:14:37 +00:00
public abstract void Update ( double dt ) ;
2019-06-15 00:51:06 +00:00
2019-10-24 19:48:36 +00:00
/// <summary>
/// Creates and returns a new empty Entity.
/// </summary>
2019-06-16 01:05:56 +00:00
protected Entity CreateEntity ( )
{
2019-06-20 03:37:46 +00:00
return entityManager . CreateEntity ( ) ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns true if an Entity with the specified ID exists.
/// </summary>
2019-07-13 00:37:31 +00:00
protected bool EntityExists ( Guid entityID )
{
return entityManager . EntityExists ( entityID ) ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns the Entity with the specified ID.
/// </summary>
/// <exception cref="Encompass.Exceptions.EntityNotFoundException">
/// Thrown when an Entity with the given ID does not exist.
/// </exception>
2019-06-20 03:37:46 +00:00
protected Entity GetEntity ( Guid entityID )
{
return entityManager . GetEntity ( entityID ) ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns the Entity ID associated with the specified Component Type and ID.
/// </summary>
2019-08-21 22:13:48 +00:00
protected Guid GetEntityIDByComponentID < TComponent > ( Guid componentID ) where TComponent : struct , IComponent
2019-06-20 03:37:46 +00:00
{
2019-08-21 22:13:48 +00:00
var pendingRead = receiveTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) ;
var existingRead = receiveTypes . Contains ( typeof ( ComponentMessage < TComponent > ) ) ;
if ( ! pendingRead & & ! existingRead )
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
2019-08-20 00:30:31 +00:00
return componentMessageManager . GetEntityIDByComponentID ( componentID ) ;
2019-06-15 00:51:06 +00:00
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns the Entity associated with the specified Component Type and ID.
/// </summary>
2019-08-21 22:13:48 +00:00
protected Entity GetEntityByComponentID < TComponent > ( Guid componentID ) where TComponent : struct , IComponent
2019-06-22 00:44:07 +00:00
{
2019-08-21 22:13:48 +00:00
return GetEntity ( GetEntityIDByComponentID < TComponent > ( componentID ) ) ;
2019-06-22 00:44:07 +00:00
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns an Entity containing the specified Component type.
/// </summary>
protected Entity ReadEntity < TComponent > ( ) where TComponent : struct , IComponent
{
var ( id , component ) = ReadComponent < TComponent > ( ) ;
return GetEntityByComponentID < TComponent > ( id ) ;
}
/// <summary>
/// Returns all Entities containing the specified Component type.
/// </summary>
protected IEnumerable < Entity > ReadEntities < TComponent > ( ) where TComponent : struct , IComponent
{
foreach ( var ( id , component ) in ReadComponents < TComponent > ( ) )
{
yield return GetEntityByComponentID < TComponent > ( id ) ;
}
}
/// <summary>
/// Returns the Component struct with the specified Component Type and ID.
/// </summary>
2019-06-22 00:44:07 +00:00
protected TComponent GetComponentByID < TComponent > ( Guid componentID ) where TComponent : struct , IComponent
{
2019-08-21 22:13:48 +00:00
var pendingRead = receiveTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) ;
var existingRead = receiveTypes . Contains ( typeof ( ComponentMessage < TComponent > ) ) ;
if ( ! pendingRead & & ! existingRead )
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
2019-08-20 00:30:31 +00:00
if ( componentMessageManager . GetComponentTypeByID ( componentID ) ! = typeof ( TComponent ) )
2019-06-22 00:44:07 +00:00
{
2019-08-20 00:30:31 +00:00
throw new ComponentTypeMismatchException ( "Expected Component to be of type {0} but was actually of type {1}" , typeof ( TComponent ) . Name , componentMessageManager . GetComponentTypeByID ( componentID ) . Name ) ;
2019-06-22 00:44:07 +00:00
}
2019-07-16 18:17:07 +00:00
2019-08-20 00:30:31 +00:00
return ( TComponent ) componentMessageManager . GetComponentByID ( componentID ) ;
2019-06-22 00:44:07 +00:00
}
2019-08-20 01:11:15 +00:00
// these next two are for the ComponentMessageEmitter only
2019-08-20 02:05:18 +00:00
2019-08-01 23:44:29 +00:00
internal IEnumerable < ( Guid , TComponent ) > ReadComponentsFromWorld < TComponent > ( ) where TComponent : struct , IComponent
2019-06-16 01:05:56 +00:00
{
2019-08-01 23:44:29 +00:00
return componentManager . GetComponentsByType < TComponent > ( ) ;
2019-06-15 00:51:06 +00:00
}
2019-06-15 07:39:08 +00:00
2019-08-20 00:30:31 +00:00
internal Entity ReadEntityFromWorld ( Guid componentID )
{
return GetEntity ( componentManager . GetEntityIDByComponentID ( componentID ) ) ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns all of the Components with the specified Component Type.
/// </summary>
protected IEnumerable < ( Guid , TComponent ) > ReadComponents < TComponent > ( ) where TComponent : struct , IComponent
2019-07-20 21:10:06 +00:00
{
var pendingRead = receiveTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) ;
var existingRead = receiveTypes . Contains ( typeof ( ComponentMessage < TComponent > ) ) ;
if ( existingRead & & pendingRead )
{
2019-08-01 23:24:57 +00:00
return componentMessageManager . ReadExistingAndPendingComponentsByType < TComponent > ( ) ;
2019-07-20 21:10:06 +00:00
}
2019-07-23 17:17:53 +00:00
else if ( existingRead )
{
2019-08-01 23:24:57 +00:00
return componentMessageManager . ReadExistingComponentsByType < TComponent > ( ) ;
2019-07-23 17:17:53 +00:00
}
else if ( pendingRead )
{
2019-08-01 23:24:57 +00:00
return componentMessageManager . ReadPendingComponentsByType < TComponent > ( ) ;
2019-07-23 17:17:53 +00:00
}
2019-07-20 21:10:06 +00:00
else
2019-07-18 21:02:57 +00:00
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
2019-07-20 21:10:06 +00:00
}
2019-07-18 21:02:57 +00:00
2019-10-24 19:48:36 +00:00
/// <summary>
2019-11-14 04:08:11 +00:00
/// Returns all of the components of the specified type including an Entity reference for each Component.
/// </summary>
protected IEnumerable < ( Guid , TComponent , Entity ) > ReadComponentsIncludingEntity < TComponent > ( ) where TComponent : struct , IComponent
{
return ReadComponents < TComponent > ( ) . Select ( ( tuple ) = > ( tuple . Item1 , tuple . Item2 , GetEntityByComponentID < TComponent > ( tuple . Item1 ) ) ) ;
}
/// <summary>
/// Returns a Component with the specified Component Type. If multiples exist, an arbitrary Component is returned.
2019-10-24 19:48:36 +00:00
/// </summary>
protected ( Guid , TComponent ) ReadComponent < TComponent > ( ) where TComponent : struct , IComponent
2019-07-20 21:10:06 +00:00
{
2019-08-01 23:24:57 +00:00
var pendingRead = receiveTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) ;
var existingRead = receiveTypes . Contains ( typeof ( ComponentMessage < TComponent > ) ) ;
if ( existingRead & & pendingRead )
{
return componentMessageManager . ReadFirstExistingOrPendingComponentByType < TComponent > ( ) ;
}
else if ( existingRead )
{
return componentMessageManager . ReadFirstExistingComponentByType < TComponent > ( ) ;
}
else if ( pendingRead )
{
return componentMessageManager . ReadFirstPendingComponentByType < TComponent > ( ) ;
}
else
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
2019-07-19 19:47:17 +00:00
}
2019-11-14 04:08:11 +00:00
/// <summary>
/// Returns a component of the specified type including its Entity reference. If multiples exist, an arbitrary Component is returned.
/// </summary>
protected ( Guid , TComponent , Entity ) ReadComponentIncludingEntity < TComponent > ( ) where TComponent : struct , IComponent
{
var ( id , component ) = ReadComponent < TComponent > ( ) ;
return ( id , component , GetEntityByComponentID < TComponent > ( id ) ) ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns true if any Component with the specified Component Type exists.
/// </summary>
2019-07-20 21:10:06 +00:00
protected bool SomeComponent < TComponent > ( ) where TComponent : struct , IComponent
2019-07-19 19:47:17 +00:00
{
2019-08-01 23:24:57 +00:00
var pendingRead = receiveTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) ;
var existingRead = receiveTypes . Contains ( typeof ( ComponentMessage < TComponent > ) ) ;
if ( existingRead & & pendingRead )
{
return componentMessageManager . SomeExistingOrPendingComponent < TComponent > ( ) ;
}
else if ( existingRead )
{
return componentMessageManager . SomeExistingComponent < TComponent > ( ) ;
}
else if ( pendingRead )
{
return componentMessageManager . SomePendingComponent < TComponent > ( ) ;
}
else
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
2019-07-19 19:47:17 +00:00
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns a Component with the specified Type that exists on the Entity.
/// </summary>
/// <exception cref="Encompass.Exceptions.NoComponentOfTypeOnEntityException">
/// Thrown when the Entity does not have a Component of the specified Type
/// </exception>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it reads the given Component Type.
/// </exception>
protected ( Guid , TComponent ) GetComponent < TComponent > ( Entity entity ) where TComponent : struct , IComponent
2019-07-17 18:24:21 +00:00
{
2019-08-01 23:24:57 +00:00
var pendingRead = receiveTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) ;
var existingRead = receiveTypes . Contains ( typeof ( ComponentMessage < TComponent > ) ) ;
if ( existingRead & & pendingRead )
{
2019-08-11 01:17:52 +00:00
if ( componentMessageManager . HasPendingComponent < TComponent > ( entity ) )
{
return componentMessageManager . ReadPendingComponentByEntityAndType < TComponent > ( entity ) ;
}
else if ( componentMessageManager . HasExistingComponent < TComponent > ( entity ) )
{
return componentMessageManager . ReadExistingComponentByEntityAndType < TComponent > ( entity ) ;
}
else
{
throw new NoComponentOfTypeOnEntityException ( "No Component of type {0} exists on Entity {1}" , typeof ( TComponent ) . Name , entity . ID ) ;
}
2019-08-01 23:24:57 +00:00
}
else if ( existingRead )
{
2019-08-11 00:34:00 +00:00
return componentMessageManager . ReadExistingComponentByEntityAndType < TComponent > ( entity ) ;
2019-08-01 23:24:57 +00:00
}
else if ( pendingRead )
{
2019-08-11 00:34:00 +00:00
return componentMessageManager . ReadPendingComponentByEntityAndType < TComponent > ( entity ) ;
2019-08-01 23:24:57 +00:00
}
else
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
2019-07-17 18:24:21 +00:00
}
2019-11-13 21:24:20 +00:00
/// <summary>
/// Returns a Component with the specified Type that exists on the Entity.
/// </summary>
/// <exception cref="Encompass.Exceptions.NoComponentOfTypeOnEntityException">
/// Thrown when the Entity does not have a Component of the specified Type
/// </exception>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it reads the given Component Type.
/// </exception>
protected ( Guid , IComponent ) GetComponent ( Entity entity , Type type )
{
var pending = typeof ( PendingComponentMessage < > ) . MakeGenericType ( type ) ;
var existing = typeof ( ComponentMessage < > ) . MakeGenericType ( type ) ;
var pendingRead = receiveTypes . Contains ( pending ) ;
var existingRead = receiveTypes . Contains ( existing ) ;
if ( existingRead & & pendingRead )
{
if ( componentMessageManager . HasPendingComponent ( entity , pending ) )
{
return componentMessageManager . ReadPendingComponentByEntityAndType ( entity , pending ) ;
}
else if ( componentMessageManager . HasExistingComponent ( entity , existing ) )
{
return componentMessageManager . ReadExistingComponentByEntityAndType ( entity , existing ) ;
}
else
{
throw new NoComponentOfTypeOnEntityException ( "No Component of type {0} exists on Entity {1}" , type . Name , entity . ID ) ;
}
}
else if ( existingRead )
{
return componentMessageManager . ReadExistingComponentByEntityAndType ( entity , existing ) ;
}
else if ( pendingRead )
{
return componentMessageManager . ReadPendingComponentByEntityAndType ( entity , pending ) ;
}
else
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , type . Name ) ;
}
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns true if the Entity has a Component of the given Type.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that is Reads the given Component Type.
/// </exception>
2019-07-17 18:24:21 +00:00
protected bool HasComponent < TComponent > ( Entity entity ) where TComponent : struct , IComponent
{
2019-08-01 23:24:57 +00:00
var pendingRead = receiveTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) ;
var existingRead = receiveTypes . Contains ( typeof ( ComponentMessage < TComponent > ) ) ;
if ( pendingRead & & existingRead )
{
return componentMessageManager . HasExistingOrPendingComponent < TComponent > ( entity ) ;
}
else if ( existingRead )
{
return componentMessageManager . HasExistingComponent < TComponent > ( entity ) ;
}
else if ( pendingRead )
{
return componentMessageManager . HasPendingComponent < TComponent > ( entity ) ;
}
else
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
2019-07-17 18:24:21 +00:00
}
2019-11-13 21:15:43 +00:00
/// <summary>
/// Returns true if the Entity has a Component of the given Type.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that is Reads the given Component Type.
/// </exception>
protected bool HasComponent ( Entity entity , Type type )
{
var pending = typeof ( PendingComponentMessage < > ) . MakeGenericType ( type ) ;
var existing = typeof ( ComponentMessage < > ) . MakeGenericType ( type ) ;
var pendingRead = receiveTypes . Contains ( pending ) ;
var existingRead = receiveTypes . Contains ( existing ) ;
if ( pendingRead & & existingRead )
{
return componentMessageManager . HasExistingOrPendingComponent ( entity , type ) ;
}
else if ( existingRead )
{
return componentMessageManager . HasExistingComponent ( entity , type ) ;
}
else if ( pendingRead )
{
return componentMessageManager . HasPendingComponent ( entity , type ) ;
}
else
{
throw new IllegalReadException ( "Engine {0} tried to read undeclared Component {1}" , GetType ( ) . Name , type . Name ) ;
}
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Sets Component data for the specified Component Type on the specified Entity. If Component data for this Type already existed on the Entity, the component data is overwritten.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalWriteException">
/// Thrown when the Engine does not declare that it Writes the given Component Type.
/// </exception>
2019-08-21 02:25:59 +00:00
protected Guid SetComponent < TComponent > ( Entity entity , TComponent component ) where TComponent : struct , IComponent
2019-06-16 01:05:56 +00:00
{
2019-08-21 02:25:59 +00:00
var priority = writePriorities . ContainsKey ( typeof ( TComponent ) ) ? writePriorities [ typeof ( TComponent ) ] : 0 ;
var componentID = componentManager . MarkComponentForWrite ( entity , component , priority ) ;
if ( ! sendTypes . Contains ( typeof ( ComponentWriteMessage < TComponent > ) ) )
2019-07-20 00:50:13 +00:00
{
2019-08-21 02:25:59 +00:00
throw new IllegalWriteException ( "Engine {0} tried to update undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
2019-07-20 00:50:13 +00:00
}
2019-08-21 02:25:59 +00:00
if ( sendTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) )
{
PendingComponentMessage < TComponent > newComponentMessage ;
newComponentMessage . entity = entity ;
newComponentMessage . componentID = componentID ;
newComponentMessage . component = component ;
newComponentMessage . priority = priority ;
SendPendingComponentMessage ( newComponentMessage ) ;
}
return componentID ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Overwrites Component struct data associated with the specified Component ID.
/// </summary>
2019-08-21 02:25:59 +00:00
protected Guid SetComponent < TComponent > ( Guid componentID , TComponent component ) where TComponent : struct , IComponent
{
2019-08-21 22:13:48 +00:00
return SetComponent ( GetEntityByComponentID < TComponent > ( componentID ) , component ) ;
2019-08-21 02:25:59 +00:00
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Sets Draw Component data for the specified Component Type on the specified Entity.
/// This method must be used for the Draw Component to be readable by an OrderedRenderer.
/// If Component data for this Type already existed on the Entity, the component data is overwritten.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalWriteException">
/// Thrown when the Engine does not declare that it Writes the given Component Type.
/// </exception>
2019-09-26 20:34:56 +00:00
protected Guid SetDrawComponent < TComponent > ( Entity entity , TComponent component , int layer = 0 ) where TComponent : struct , IComponent , IDrawComponent
2019-08-21 02:25:59 +00:00
{
var priority = writePriorities . ContainsKey ( typeof ( TComponent ) ) ? writePriorities [ typeof ( TComponent ) ] : 0 ;
var componentID = componentManager . MarkDrawComponentForWrite ( entity , component , priority , layer ) ;
if ( ! sendTypes . Contains ( typeof ( ComponentWriteMessage < TComponent > ) ) )
{
throw new IllegalWriteException ( "Engine {0} tried to write undeclared Component {1}" , GetType ( ) . Name , typeof ( TComponent ) . Name ) ;
}
if ( sendTypes . Contains ( typeof ( PendingComponentMessage < TComponent > ) ) )
{
PendingComponentMessage < TComponent > newComponentMessage ;
newComponentMessage . entity = entity ;
newComponentMessage . componentID = componentID ;
newComponentMessage . component = component ;
newComponentMessage . priority = priority ;
SendPendingComponentMessage ( newComponentMessage ) ;
}
return componentID ;
2019-06-15 07:39:08 +00:00
}
2019-06-16 01:55:35 +00:00
2019-10-24 19:48:36 +00:00
/// <summary>
/// Sends a Message.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalSendException">
/// Thrown when the Engine does not declare that it Sends the Message Type.
/// </exception>
2019-07-19 03:31:31 +00:00
protected void SendMessage < TMessage > ( TMessage message ) where TMessage : struct , IMessage
2019-06-16 01:55:35 +00:00
{
2019-07-19 01:20:38 +00:00
if ( ! sendTypes . Contains ( typeof ( TMessage ) ) )
2019-06-16 01:55:35 +00:00
{
2019-07-20 00:50:13 +00:00
throw new IllegalSendException ( "Engine {0} tried to send undeclared Message {1}" , GetType ( ) . Name , typeof ( TMessage ) . Name ) ;
2019-06-16 01:55:35 +00:00
}
2019-07-16 18:17:07 +00:00
messageManager . AddMessage ( message ) ;
2019-06-16 01:55:35 +00:00
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Sends a message after the specified number of seconds.
/// </summary>
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
2019-08-20 02:05:18 +00:00
protected void SendMessageDelayed < TMessage > ( TMessage message , double time ) where TMessage : struct , IMessage
{
messageManager . AddMessageDelayed ( message , time ) ;
}
2019-07-29 05:25:34 +00:00
// unparameterized version to enable dynamic dispatch
protected void SendMessage ( IMessage message )
{
var type = message . GetType ( ) ;
if ( ! sendTypes . Contains ( type ) | | ! type . IsValueType )
{
throw new IllegalSendException ( "Engine {0} tried to send undeclared Message {1}" , GetType ( ) . Name , type . Name ) ;
}
messageManager . AddMessage ( message ) ;
}
2019-08-01 23:24:57 +00:00
internal void SendExistingComponentMessage < TComponent > ( ComponentMessage < TComponent > message ) where TComponent : struct , IComponent
{
componentMessageManager . AddExistingComponentMessage ( message ) ;
}
internal void SendPendingComponentMessage < TComponent > ( PendingComponentMessage < TComponent > message ) where TComponent : struct , IComponent
{
componentMessageManager . AddPendingComponentMessage ( message ) ;
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Reads all messages of the specified Type.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
/// </exception>
2019-06-16 01:55:35 +00:00
protected IEnumerable < TMessage > ReadMessages < TMessage > ( ) where TMessage : struct , IMessage
{
2019-07-19 19:47:17 +00:00
if ( ! receiveTypes . Contains ( typeof ( TMessage ) ) )
2019-06-16 01:55:35 +00:00
{
2019-07-16 18:17:07 +00:00
throw new IllegalReadException ( "Engine {0} tried to read undeclared Message {1}" , this . GetType ( ) . Name , typeof ( TMessage ) . Name ) ;
2019-06-16 01:55:35 +00:00
}
2019-07-16 18:17:07 +00:00
return messageManager . GetMessagesByType < TMessage > ( ) ;
2019-06-16 01:55:35 +00:00
}
2019-06-17 01:11:35 +00:00
2019-10-24 19:48:36 +00:00
/// <summary>
/// Reads an arbitrary message of the specified Type.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
/// </exception>
2019-07-18 21:02:57 +00:00
protected TMessage ReadMessage < TMessage > ( ) where TMessage : struct , IMessage
{
2019-08-20 22:44:01 +00:00
return ReadMessages < TMessage > ( ) . First ( ) ;
2019-07-18 21:02:57 +00:00
}
2019-10-24 19:48:36 +00:00
/// <summary>
/// Returns true if a Message of the specified Type has been sent this frame.
/// </summary>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
/// </exception>
2019-07-17 18:46:54 +00:00
protected bool SomeMessage < TMessage > ( ) where TMessage : struct , IMessage
2019-06-17 01:11:35 +00:00
{
2019-07-19 19:47:17 +00:00
if ( ! receiveTypes . Contains ( typeof ( TMessage ) ) )
2019-06-17 01:11:35 +00:00
{
2019-07-18 21:02:57 +00:00
throw new IllegalReadException ( "Engine {0} tried to read undeclared Message {1}" , GetType ( ) . Name , typeof ( TMessage ) . Name ) ;
2019-06-17 01:11:35 +00:00
}
2019-07-16 18:17:07 +00:00
2019-07-19 00:50:38 +00:00
return ReadMessages < TMessage > ( ) . Any ( ) ;
2019-06-17 01:11:35 +00:00
}
2019-06-20 03:37:46 +00:00
2019-10-24 19:48:36 +00:00
/// <summary>
/// Destroys the Entity with the specified ID. This also removes all of the Components associated with the Entity.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
2019-06-20 03:37:46 +00:00
protected void Destroy ( Guid entityID )
{
entityManager . MarkForDestroy ( entityID ) ;
}
2019-07-17 18:24:21 +00:00
2019-10-24 19:48:36 +00:00
/// <summary>
/// Destroys the specified Entity. This also removes all of the Components associated with the Entity.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
protected void Destroy ( Entity entity )
{
entityManager . MarkForDestroy ( entity . ID ) ;
}
/// <summary>
/// Destroys an arbitrary Entity containing a Component of the specified Type.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
protected void DestroyWith < TComponent > ( ) where TComponent : struct , IComponent
{
Destroy ( ReadEntity < TComponent > ( ) ) ;
}
/// <summary>
/// Destroys all Entities containing a Component of the specified Type.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
protected void DestroyAllWith < TComponent > ( ) where TComponent : struct , IComponent
{
foreach ( var entity in ReadEntities < TComponent > ( ) )
{
Destroy ( entity ) ;
}
}
/// <summary>
/// Removes the Component with the specified ID from its Entity.
/// </summary>
2019-07-17 18:24:21 +00:00
protected void RemoveComponent ( Guid componentID )
{
2019-07-18 01:12:29 +00:00
componentManager . MarkForRemoval ( componentID ) ;
2019-07-17 18:24:21 +00:00
}
2019-06-15 00:51:06 +00:00
}
}