using System; using System.Reflection; using System.Collections.Generic; using System.Linq; using Encompass.Exceptions; namespace Encompass { public abstract class Engine { internal readonly HashSet writeTypes = new HashSet(); internal readonly HashSet readTypes = new HashSet(); private EntityManager entityManager; private ComponentManager componentManager; private MessageManager messageManager; protected Engine() { var writesAttribute = GetType().GetCustomAttribute(false); if (writesAttribute != null) { writeTypes = writesAttribute.writeTypes; } var readsAttribute = GetType().GetCustomAttribute(false); if (readsAttribute != null) { readTypes = readsAttribute.readTypes; } } 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 (!readTypes.Contains(typeof(TComponent))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } 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); } protected IEnumerable> ReadComponents() where TComponent : struct, IComponent { if (!readTypes.Contains(typeof(TComponent))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } return componentManager.GetActiveComponentsByType(); } protected ValueTuple ReadComponent() where TComponent : struct, IComponent { if (!readTypes.Contains(typeof(TComponent))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } return componentManager.GetActiveComponentByType(); } protected Guid AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { if (!writeTypes.Contains(typeof(TComponent))) { throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } return componentManager.AddComponent(entity.ID, component); } protected Guid AddDrawComponent(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent { if (!writeTypes.Contains(typeof(TComponent))) { throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } return componentManager.AddDrawComponent(entity.ID, component, layer); } protected void ActivateComponent(Guid componentID) { var type = componentManager.GetComponentTypeByID(componentID); if (!writeTypes.Contains(type)) { throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, type.Name); } componentManager.Activate(componentID); } protected void DeactivateComponent(Guid componentID) { var type = componentManager.GetComponentTypeByID(componentID); if (!writeTypes.Contains(type)) { throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, type.Name); } componentManager.Deactivate(componentID); } protected IEnumerable> GetComponents(Entity entity) where TComponent : struct, IComponent { if (!readTypes.Contains(typeof(TComponent))) { throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } return componentManager.GetComponentsByEntityAndType(entity.ID); } protected ValueTuple GetComponent(Entity entity) where TComponent : struct, IComponent { return GetComponents(entity).First(); } protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { return componentManager.EntityHasComponentOfType(entity.ID); } internal void UpdateComponentInWorld(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent { if (!writeTypes.Contains(typeof(TComponent))) { throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); } componentManager.UpdateComponent(componentID, newComponent); } protected void UpdateComponent(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent { UpdateComponentInWorld(componentID, newComponentValue); } protected void EmitMessage(TMessage message) where TMessage : struct, IMessage { if (!writeTypes.Contains(typeof(TMessage))) { throw new IllegalWriteException("Engine {0} tried to emit 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 bool Some() 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().Any(); } protected void Destroy(Guid entityID) { entityManager.MarkForDestroy(entityID); } protected void RemoveComponent(Guid componentID) { var type = componentManager.GetComponentTypeByID(componentID); if (!writeTypes.Contains(type)) { throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, type.Name); } componentManager.Remove(componentID); } } }