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 readTypes = new HashSet(); internal readonly HashSet activateTypes = new HashSet(); internal readonly HashSet updateTypes = new HashSet(); private EntityManager entityManager; private ComponentManager componentManager; private MessageManager messageManager; protected Engine() { var writesAttribute = GetType().GetCustomAttribute(false); if (writesAttribute != null) { sendTypes = writesAttribute.sendTypes; } var readsAttribute = GetType().GetCustomAttribute(false); if (readsAttribute != null) { readTypes = readsAttribute.readTypes; } var activatesAttribute = GetType().GetCustomAttribute(false); if (activatesAttribute != null) { activateTypes = activatesAttribute.activateTypes; } var updatesAttribute = GetType().GetCustomAttribute(false); if (updatesAttribute != null) { updateTypes = updatesAttribute.updateTypes; if (sendTypes.Any()) { throw new IllegalEngineAttributesException("Engine {0} sends Message(s) and updates Component(s)", GetType().Name); } } } internal void AssignEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } internal void AssignComponentManager(ComponentManager componentManager) { this.componentManager = componentManager; } internal void AssignMessageManager(MessageManager messageManager) { this.messageManager = messageManager; } 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 componentManager.GetEntityIDByComponentID(componentID); } protected Entity GetEntityByComponentID(Guid componentID) { return GetEntity(GetEntityIDByComponentID(componentID)); } protected TComponent GetComponentByID(Guid componentID) where TComponent : struct, IComponent { if (componentManager.GetComponentTypeByID(componentID) != typeof(TComponent)) { throw new ComponentTypeMismatchException("Expected Component to be of type {0} but was actually of type {1}", typeof(TComponent).Name, componentManager.GetComponentTypeByID(componentID).Name); } return (TComponent)componentManager.GetComponentByID(componentID); } internal IEnumerable> ReadComponentsFromWorld() where TComponent : struct, IComponent { return componentManager.GetActiveComponentsByType().Select((triple) => (GetEntity(triple.Item1), triple.Item2, triple.Item3)); } protected Guid AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { if (!activateTypes.Contains(typeof(ComponentMessage))) { throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } var componentID = componentManager.AddComponent(entity.ID, component); ComponentMessage componentMessage; componentMessage.entity = entity; componentMessage.componentID = componentID; componentMessage.component = component; SendMessage(componentMessage); return componentID; } protected Guid AddDrawComponent(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent { if (!activateTypes.Contains(typeof(ComponentMessage))) { throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } var componentID = componentManager.AddDrawComponent(entity.ID, component, layer); ComponentMessage componentMessage; componentMessage.entity = entity; componentMessage.componentID = componentID; componentMessage.component = component; SendMessage(componentMessage); return componentID; } protected void ActivateComponent(Guid componentID) where TComponent : struct, IComponent { if (!activateTypes.Contains(typeof(ComponentMessage))) { throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } var entity = GetEntity(componentManager.GetEntityIDByComponentID(componentID)); var component = GetComponentByID(componentID); ComponentMessage componentMessage; componentMessage.entity = entity; componentMessage.componentID = componentID; componentMessage.component = component; SendMessage(componentMessage); componentManager.Activate(componentID); } protected void DeactivateComponent(Guid componentID) { componentManager.MarkForDeactivation(componentID); } protected IEnumerable> GetComponents(Entity entity) where TComponent : struct, IComponent { if (!readTypes.Contains(typeof(ComponentMessage))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } return ReadMessages>().Where((message) => message.entity == entity).Select((message) => (message.componentID, message.component)); } protected ValueTuple GetComponent(Entity entity) where TComponent : struct, IComponent { return GetComponents(entity).First(); } protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { if (!readTypes.Contains(typeof(ComponentMessage))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } return ReadMessages>().Where((message) => message.entity == entity).Any(); } internal void UpdateComponentInWorld(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent { if (!updateTypes.Contains(typeof(TComponent))) { throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } componentManager.AddUpdateComponentOperation(componentID, newComponent); } protected void UpdateComponent(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent { UpdateComponentInWorld(componentID, newComponentValue); } protected void SendMessage(TMessage message) where TMessage : struct, IMessage { if (!sendTypes.Contains(typeof(TMessage))) { throw new IllegalWriteException("Engine {0} tried to write undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name); } messageManager.AddMessage(message); } protected IEnumerable ReadMessages() where TMessage : struct, IMessage { if (!readTypes.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 IEnumerable<(Guid, TComponent)> ReadComponents() where TComponent : struct, IComponent { if (!readTypes.Contains(typeof(ComponentMessage))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } return ReadMessages>().Select((message) => (message.componentID, message.component)); } protected (Guid, TComponent) ReadComponent() where TComponent : struct, IComponent { return ReadComponents().Single(); } protected bool SomeMessage() where TMessage : struct, IMessage { if (!readTypes.Contains(typeof(TMessage))) { throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name); } return ReadMessages().Any(); } protected bool SomeComponent() where TComponent : struct, IComponent { if (!readTypes.Contains(typeof(ComponentMessage))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } return ReadMessages>().Any(); } protected void Destroy(Guid entityID) { entityManager.MarkForDestroy(entityID); } protected void RemoveComponent(Guid componentID) { componentManager.MarkForRemoval(componentID); } } }