using System; using System.Reflection; using System.Collections.Generic; using System.Linq; using Encompass.Exceptions; namespace Encompass { public abstract class Engine { internal readonly HashSet sendTypes = new HashSet(); internal readonly HashSet receiveTypes = new HashSet(); private EntityManager entityManager; private MessageManager messageManager; private ComponentManager componentManager; private ComponentMessageManager componentMessageManager; protected Engine() { var sendsAttribute = GetType().GetCustomAttribute(false); if (sendsAttribute != null) { sendTypes = sendsAttribute.sendTypes; } var activatesAttribute = GetType().GetCustomAttribute(false); if (activatesAttribute != null) { sendTypes.UnionWith(activatesAttribute.activateTypes); } var updatesAttribute = GetType().GetCustomAttribute(false); if (updatesAttribute != null) { sendTypes.UnionWith(updatesAttribute.updateTypes); } var receivesAttribute = GetType().GetCustomAttribute(false); if (receivesAttribute != null) { receiveTypes = receivesAttribute.receiveTypes; } var readsAttribute = GetType().GetCustomAttribute(false); if (readsAttribute != null) { receiveTypes.UnionWith(readsAttribute.readTypes); } var readsPendingAttribute = GetType().GetCustomAttribute(false); if (readsPendingAttribute != null) { receiveTypes.UnionWith(readsPendingAttribute.readPendingTypes); } } internal void AssignEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } internal void AssignComponentManager(ComponentManager componentManager) { this.componentManager = componentManager; } internal void AssignMessageManager(MessageManager messageManager) { this.messageManager = messageManager; } internal void AssignComponentMessageManager(ComponentMessageManager componentMessageManager) { this.componentMessageManager = componentMessageManager; } public abstract void Update(double dt); protected Entity CreateEntity() { return entityManager.CreateEntity(); } protected bool EntityExists(Guid entityID) { return entityManager.EntityExists(entityID); } protected Entity GetEntity(Guid entityID) { return entityManager.GetEntity(entityID); } protected Guid GetEntityIDByComponentID(Guid componentID) { return componentMessageManager.GetEntityIDByComponentID(componentID); } protected Entity GetEntityByComponentID(Guid componentID) { return GetEntity(GetEntityIDByComponentID(componentID)); } protected TComponent GetComponentByID(Guid componentID) where TComponent : struct, IComponent { if (componentMessageManager.GetComponentTypeByID(componentID) != typeof(TComponent)) { throw new ComponentTypeMismatchException("Expected Component to be of type {0} but was actually of type {1}", typeof(TComponent).Name, componentMessageManager.GetComponentTypeByID(componentID).Name); } return (TComponent)componentMessageManager.GetComponentByID(componentID); } // these next two are for the ComponentMessageEmitter only internal IEnumerable<(Guid, TComponent)> ReadComponentsFromWorld() where TComponent : struct, IComponent { return componentManager.GetComponentsByType(); } internal Entity ReadEntityFromWorld(Guid componentID) { return GetEntity(componentManager.GetEntityIDByComponentID(componentID)); } protected Guid AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { var componentID = componentManager.MarkComponentForAdd(entity, component); if (sendTypes.Contains(typeof(PendingComponentMessage))) { PendingComponentMessage newComponentMessage; newComponentMessage.entity = entity; newComponentMessage.componentID = componentID; newComponentMessage.component = component; SendMessage(newComponentMessage); SendPendingComponentMessage(newComponentMessage); } return componentID; } protected Guid AddDrawComponent(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent { var componentID = componentManager.MarkDrawComponentForAdd(entity, component, layer); if (sendTypes.Contains(typeof(PendingComponentMessage))) { PendingComponentMessage newComponentMessage; newComponentMessage.entity = entity; newComponentMessage.componentID = componentID; newComponentMessage.component = component; SendMessage(newComponentMessage); SendPendingComponentMessage(newComponentMessage); } return componentID; } protected IEnumerable> ReadComponents() where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (existingRead && pendingRead) { return componentMessageManager.ReadExistingAndPendingComponentsByType(); } else if (existingRead) { return componentMessageManager.ReadExistingComponentsByType(); } else if (pendingRead) { return componentMessageManager.ReadPendingComponentsByType(); } else { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } } protected ValueTuple ReadComponent() where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (existingRead && pendingRead) { return componentMessageManager.ReadFirstExistingOrPendingComponentByType(); } else if (existingRead) { return componentMessageManager.ReadFirstExistingComponentByType(); } else if (pendingRead) { return componentMessageManager.ReadFirstPendingComponentByType(); } else { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } } protected bool SomeComponent() where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (existingRead && pendingRead) { return componentMessageManager.SomeExistingOrPendingComponent(); } else if (existingRead) { return componentMessageManager.SomeExistingComponent(); } else if (pendingRead) { return componentMessageManager.SomePendingComponent(); } else { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } } protected ValueTuple GetComponent(Entity entity) where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (existingRead && pendingRead) { if (componentMessageManager.HasPendingComponent(entity)) { return componentMessageManager.ReadPendingComponentByEntityAndType(entity); } else if (componentMessageManager.HasExistingComponent(entity)) { return componentMessageManager.ReadExistingComponentByEntityAndType(entity); } else { throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID); } } else if (existingRead) { return componentMessageManager.ReadExistingComponentByEntityAndType(entity); } else if (pendingRead) { return componentMessageManager.ReadPendingComponentByEntityAndType(entity); } else { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } } protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (pendingRead && existingRead) { return componentMessageManager.HasExistingOrPendingComponent(entity); } else if (existingRead) { return componentMessageManager.HasExistingComponent(entity); } else if (pendingRead) { return componentMessageManager.HasPendingComponent(entity); } else { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } } internal void UpdateComponentInWorld(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent { componentManager.AddUpdateComponentOperation(componentID, newComponent); } protected void UpdateComponent(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent { if (!sendTypes.Contains(typeof(ComponentUpdateMessage))) { throw new IllegalUpdateException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } ComponentUpdateMessage componentUpdateMessage; componentUpdateMessage.componentID = componentID; componentUpdateMessage.component = newComponentValue; SendMessage(componentUpdateMessage); } protected void SendMessage(TMessage message) where TMessage : struct, IMessage { if (!sendTypes.Contains(typeof(TMessage))) { throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, typeof(TMessage).Name); } messageManager.AddMessage(message); } // 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); } internal void SendExistingComponentMessage(ComponentMessage message) where TComponent : struct, IComponent { componentMessageManager.AddExistingComponentMessage(message); } internal void SendPendingComponentMessage(PendingComponentMessage message) where TComponent : struct, IComponent { componentMessageManager.AddPendingComponentMessage(message); } 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); } return messageManager.GetMessagesByType(); } protected TMessage ReadMessage() where TMessage : struct, IMessage { return ReadMessages().Single(); } 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); } return ReadMessages().Any(); } protected void Destroy(Guid entityID) { entityManager.MarkForDestroy(entityID); } protected void RemoveComponent(Guid componentID) { componentManager.MarkForRemoval(componentID); } } }