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 ComponentManager componentManager; private MessageManager messageManager; 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; } 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 (!sendTypes.Contains(typeof(PendingComponentMessage))) { throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } var componentID = componentManager.AddComponent(entity.ID, component); PendingComponentMessage 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 (!sendTypes.Contains(typeof(PendingComponentMessage))) { throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } var componentID = componentManager.AddDrawComponent(entity.ID, component, layer); PendingComponentMessage newComponentMessage; newComponentMessage.entity = entity; newComponentMessage.componentID = componentID; newComponentMessage.component = component; SendMessage(newComponentMessage); return componentID; } protected void ActivateComponent(Guid componentID) where TComponent : struct, IComponent { if (!sendTypes.Contains(typeof(PendingComponentMessage))) { 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); PendingComponentMessage newComponentMessage; newComponentMessage.entity = entity; newComponentMessage.componentID = componentID; newComponentMessage.component = component; SendMessage(newComponentMessage); componentManager.Activate(componentID); // TODO: actually delay this to end of frame, make sure to update after activate } protected void DeactivateComponent(Guid componentID) { componentManager.MarkForDeactivation(componentID); } private IEnumerable> ExistingComponents() where TComponent : struct, IComponent { return ReadMessages>().Select((message) => (message.entity, message.componentID, message.component)); } private IEnumerable> PendingComponents() where TComponent : struct, IComponent { return ReadMessages>().Select((message) => (message.entity, message.componentID, message.component)); } private IEnumerable> ReadComponentMessages() where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (existingRead && pendingRead) { return ExistingComponents().Union(PendingComponents()); } else if (existingRead) { return ExistingComponents(); } else if (pendingRead) { return PendingComponents(); } else { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } } private IEnumerable> ExistingComponentsOnEntity(Entity entity) where TComponent : struct, IComponent { return ReadComponentMessages().Where((triple) => triple.Item1 == entity).Select((triple) => (triple.Item2, triple.Item3)); } private IEnumerable> PendingComponentsOnEntity(Entity entity) where TComponent : struct, IComponent { return ReadComponentMessages().Where((triple) => triple.Item1 == entity).Select((triple) => (triple.Item2, triple.Item3)); } protected IEnumerable> ReadComponents() where TComponent : struct, IComponent { return ReadComponentMessages().Select((triple) => (triple.Item2, triple.Item3)); } protected ValueTuple ReadComponent() where TComponent : struct, IComponent { return ReadComponents().Single(); } protected bool SomeComponent() where TComponent : struct, IComponent { return ReadComponentMessages().Any(); } protected IEnumerable> GetComponents(Entity entity) where TComponent : struct, IComponent { return ExistingComponentsOnEntity(entity); } protected ValueTuple GetPendingComponent(Entity entity) where TComponent : struct, IComponent { return PendingComponentsOnEntity(entity).First(); } protected ValueTuple GetComponent(Entity entity) where TComponent : struct, IComponent { return GetComponents(entity).First(); } protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { return GetComponents(entity).Any(); } 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); } 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); } } }