diff --git a/encompass-cs/ComponentMessageManager.cs b/encompass-cs/ComponentMessageManager.cs index 60a9bf3..302ec14 100644 --- a/encompass-cs/ComponentMessageManager.cs +++ b/encompass-cs/ComponentMessageManager.cs @@ -1,13 +1,300 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; namespace Encompass { class ComponentMessageManager { - private readonly Dictionary> componentMessageTypeToComponentMessages = new Dictionary>(); + private readonly Dictionary componentIDToComponent = new Dictionary(); + private readonly Dictionary> componentMessageTypeToExistingComponentIDs = new Dictionary>(); + private readonly Dictionary> componentMessageTypeToPendingComponentIDs = new Dictionary>(); + private readonly Dictionary> componentMessageTypeToComponentIDs = new Dictionary>(); + private readonly Dictionary>> entityToTypeToExistingComponentIDs = new Dictionary>>(); + private readonly Dictionary>> entityToTypeToPendingComponentIDs = new Dictionary>>(); + private readonly Dictionary>> entityToTypeToComponentIDs = new Dictionary>>(); + + internal void ClearMessages() + { + componentIDToComponent.Clear(); + + foreach (var set in componentMessageTypeToExistingComponentIDs.Values) + { + set.Clear(); + } + + foreach (var set in componentMessageTypeToPendingComponentIDs.Values) + { + set.Clear(); + } + + foreach (var set in componentMessageTypeToComponentIDs.Values) + { + set.Clear(); + } + + foreach (var dictionary in entityToTypeToExistingComponentIDs.Values) + { + foreach (var set in dictionary.Values) + { + set.Clear(); + } + } + + foreach (var dictionary in entityToTypeToPendingComponentIDs.Values) + { + foreach (var set in dictionary.Values) + { + set.Clear(); + } + } + + foreach (var dictionary in entityToTypeToComponentIDs.Values) + { + foreach (var set in dictionary.Values) + { + set.Clear(); + } + } + } + + internal void RegisterDestroyedEntity(Entity entity) + { + entityToTypeToComponentIDs.Remove(entity); + entityToTypeToPendingComponentIDs.Remove(entity); + entityToTypeToExistingComponentIDs.Remove(entity); + } + + internal void AddExistingComponentMessage(ComponentMessage componentMessage) where TComponent : struct, IComponent + { + RegisterExistingOrPendingComponentMessage(componentMessage.componentID, componentMessage.component); + + if (!componentMessageTypeToExistingComponentIDs.ContainsKey(typeof(TComponent))) + { + componentMessageTypeToExistingComponentIDs.Add(typeof(TComponent), new HashSet()); + } + + componentMessageTypeToExistingComponentIDs[typeof(TComponent)].Add(componentMessage.componentID); + + if (!entityToTypeToExistingComponentIDs.ContainsKey(componentMessage.entity)) + { + entityToTypeToExistingComponentIDs.Add(componentMessage.entity, new Dictionary>()); + } + if (!entityToTypeToExistingComponentIDs[componentMessage.entity].ContainsKey(typeof(TComponent))) + { + entityToTypeToExistingComponentIDs[componentMessage.entity].Add(typeof(TComponent), new HashSet()); + } + + entityToTypeToExistingComponentIDs[componentMessage.entity][typeof(TComponent)].Add(componentMessage.componentID); + } + + internal void AddPendingComponentMessage(PendingComponentMessage pendingComponentMessage) where TComponent : struct, IComponent + { + RegisterExistingOrPendingComponentMessage(pendingComponentMessage.componentID, pendingComponentMessage.component); + + if (!componentMessageTypeToPendingComponentIDs.ContainsKey(typeof(TComponent))) + { + componentMessageTypeToPendingComponentIDs.Add(typeof(TComponent), new HashSet()); + } + + componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Add(pendingComponentMessage.componentID); + + if (!entityToTypeToPendingComponentIDs.ContainsKey(pendingComponentMessage.entity)) + { + entityToTypeToPendingComponentIDs.Add(pendingComponentMessage.entity, new Dictionary>()); + } + if (!entityToTypeToPendingComponentIDs[pendingComponentMessage.entity].ContainsKey(typeof(TComponent))) + { + entityToTypeToPendingComponentIDs[pendingComponentMessage.entity].Add(typeof(TComponent), new HashSet()); + } + + entityToTypeToPendingComponentIDs[pendingComponentMessage.entity][typeof(TComponent)].Add(pendingComponentMessage.componentID); + } + + private void RegisterExistingOrPendingComponentMessage(Guid componentID, TComponent component) where TComponent: struct, IComponent + { + componentIDToComponent[componentID] = component; + if (!componentMessageTypeToComponentIDs.ContainsKey(typeof(TComponent))) + { + componentMessageTypeToComponentIDs.Add(typeof(TComponent), new HashSet()); + } + componentMessageTypeToComponentIDs[typeof(TComponent)].Add(componentID); + } + + // general component reads by type + + internal IEnumerable<(Guid, TComponent)> ReadExistingAndPendingComponentsByType() where TComponent : struct, IComponent + { + HashSet idSet; + if (componentMessageTypeToComponentIDs.TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); + } + + return Enumerable.Empty<(Guid, TComponent)>(); + } + + internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByType() where TComponent: struct, IComponent + { + HashSet idSet; + if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); + } + + return Enumerable.Empty<(Guid, TComponent)>(); + } + + internal IEnumerable<(Guid, TComponent)> ReadPendingComponentsByType() where TComponent : struct, IComponent + { + HashSet idSet; + if (componentMessageTypeToPendingComponentIDs.TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); + } + + return Enumerable.Empty<(Guid, TComponent)>(); + } + + // singular component reads by type + + internal (Guid, TComponent) ReadFirstExistingOrPendingComponentByType() where TComponent : struct, IComponent + { + return ReadExistingAndPendingComponentsByType().First(); + } + + internal (Guid, TComponent) ReadFirstExistingComponentByType() where TComponent : struct, IComponent + { + return ReadExistingComponentsByType().First(); + } + + internal (Guid, TComponent) ReadFirstPendingComponentByType() where TComponent : struct, IComponent + { + return ReadPendingComponentsByType().First(); + } + + // check if some component of type exists in the world + + internal bool SomeExistingOrPendingComponent() where TComponent : struct, IComponent + { + HashSet idSet; + if (componentMessageTypeToComponentIDs.TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Count > 0; + } + + return false; + } + + internal bool SomeExistingComponent() where TComponent : struct, IComponent + { + HashSet idSet; + if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Count > 0; + } + + return false; + } + + internal bool SomePendingComponent() where TComponent : struct, IComponent + { + HashSet idSet; + if (componentMessageTypeToPendingComponentIDs.TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Count > 0; + } + + return false; + } + + // read components by entity and type + + internal IEnumerable<(Guid, TComponent)> ReadExistingAndPendingComponentsByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + HashSet idSet; + if (entityToTypeToComponentIDs.TryGetValue(entity, out _) && entityToTypeToComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); + } + + return Enumerable.Empty<(Guid, TComponent)>(); + } + + internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + HashSet idSet; + if (entityToTypeToExistingComponentIDs.TryGetValue(entity, out _) && entityToTypeToExistingComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); + } + + return Enumerable.Empty<(Guid, TComponent)>(); + } + + internal IEnumerable<(Guid, TComponent)> ReadPendingComponentsByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + HashSet idSet; + if (entityToTypeToPendingComponentIDs.TryGetValue(entity, out _) && entityToTypeToPendingComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); + } + + return Enumerable.Empty<(Guid, TComponent)>(); + } + + // singular read components by entity and type + + internal (Guid, TComponent) ReadFirstExistingOrPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return ReadExistingAndPendingComponentsByEntityAndType(entity).First(); + } + + internal (Guid, TComponent) ReadFirstExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return ReadExistingComponentsByEntityAndType(entity).First(); + } + + internal (Guid, TComponent) ReadFirstPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return ReadPendingComponentsByEntityAndType(entity).First(); + } + + // check if entity has component of type + + internal bool HasExistingOrPendingComponent(Entity entity) where TComponent : struct, IComponent + { + HashSet idSet; + if (entityToTypeToComponentIDs.TryGetValue(entity, out _) && entityToTypeToComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Count > 0; + } + + return false; + } + + internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent + { + HashSet idSet; + if (entityToTypeToExistingComponentIDs.TryGetValue(entity, out _) && entityToTypeToExistingComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Count > 0; + } + + return false; + } + + internal bool HasPendingComponent(Entity entity) where TComponent : struct, IComponent + { + HashSet idSet; + if (entityToTypeToPendingComponentIDs.TryGetValue(entity, out _) && entityToTypeToPendingComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet)) + { + return idSet.Count > 0; + } + + return false; + } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index ae798cf..507f974 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -14,6 +14,7 @@ namespace Encompass private EntityManager entityManager; private ComponentManager componentManager; private MessageManager messageManager; + private ComponentMessageManager componentMessageManager; protected Engine() { @@ -69,6 +70,11 @@ namespace Encompass this.messageManager = messageManager; } + internal void AssignComponentMessageManager(ComponentMessageManager componentMessageManager) + { + this.componentMessageManager = componentMessageManager; + } + public abstract void Update(double dt); protected Entity CreateEntity() @@ -119,11 +125,12 @@ namespace Encompass if (sendTypes.Contains(typeof(PendingComponentMessage))) { - PendingComponentMessage componentMessage; - componentMessage.entity = entity; - componentMessage.componentID = componentID; - componentMessage.component = component; - SendMessage(componentMessage); + PendingComponentMessage newComponentMessage; + newComponentMessage.entity = entity; + newComponentMessage.componentID = componentID; + newComponentMessage.component = component; + SendMessage(newComponentMessage); + SendPendingComponentMessage(newComponentMessage); } return componentID; @@ -142,36 +149,28 @@ namespace Encompass newComponentMessage.componentID = componentID; newComponentMessage.component = component; SendMessage(newComponentMessage); + SendPendingComponentMessage(newComponentMessage); + } return 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 + protected IEnumerable> ReadComponents() where TComponent : struct, IComponent { var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); if (existingRead && pendingRead) { - return ExistingComponents().Union(PendingComponents()); + return componentMessageManager.ReadExistingAndPendingComponentsByType(); } else if (existingRead) { - return ExistingComponents(); + return componentMessageManager.ReadExistingComponentsByType(); } else if (pendingRead) { - return PendingComponents(); + return componentMessageManager.ReadPendingComponentsByType(); } else { @@ -179,44 +178,115 @@ namespace Encompass } } - 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(); + 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 { - return ReadComponentMessages().Any(); + 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 IEnumerable> GetComponents(Entity entity) where TComponent : struct, IComponent { - return ExistingComponentsOnEntity(entity); + var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); + var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); + if (existingRead && pendingRead) + { + return componentMessageManager.ReadExistingAndPendingComponentsByEntityAndType(entity); + } + else if (existingRead) + { + return componentMessageManager.ReadExistingComponentsByEntityAndType(entity); + } + else if (pendingRead) + { + return componentMessageManager.ReadPendingComponentsByEntityAndType(entity); + } + 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 { - return GetComponents(entity).First(); + var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); + var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); + if (existingRead && pendingRead) + { + return componentMessageManager.ReadFirstExistingOrPendingComponentByEntityAndType(entity); + } + else if (existingRead) + { + return componentMessageManager.ReadFirstExistingComponentByEntityAndType(entity); + } + else if (pendingRead) + { + return componentMessageManager.ReadFirstPendingComponentByEntityAndType(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 { - return GetComponents(entity).Any(); + 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 @@ -260,6 +330,16 @@ namespace Encompass 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))) diff --git a/encompass-cs/Engines/ComponentMessageEmitter.cs b/encompass-cs/Engines/ComponentMessageEmitter.cs index 3ba6bb7..4aa9e0a 100644 --- a/encompass-cs/Engines/ComponentMessageEmitter.cs +++ b/encompass-cs/Engines/ComponentMessageEmitter.cs @@ -18,6 +18,7 @@ namespace Encompass.Engines componentMessage.componentID = componentID; componentMessage.component = component; SendMessage(componentMessage); + SendExistingComponentMessage(componentMessage); } } } diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index 0af09b4..87f4b77 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -10,10 +10,12 @@ namespace Encompass private readonly HashSet entitiesMarkedForDestroy = new HashSet(); private readonly ComponentManager componentManager; + private readonly ComponentMessageManager componentMessageManager; - public EntityManager(ComponentManager componentManager) + public EntityManager(ComponentManager componentManager, ComponentMessageManager componentMessageManager) { this.componentManager = componentManager; + this.componentMessageManager = componentMessageManager; } public Entity CreateEntity() @@ -44,6 +46,7 @@ namespace Encompass { foreach (var entityID in entitiesMarkedForDestroy) { + componentMessageManager.RegisterDestroyedEntity(GetEntity(entityID)); componentManager.MarkAllComponentsOnEntityForRemoval(entityID); IDToEntity.Remove(entityID); componentManager.RegisterDestroyedEntity(entityID); diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 481dd0d..c862cbf 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -8,6 +8,7 @@ namespace Encompass private readonly EntityManager entityManager; private readonly ComponentManager componentManager; private readonly MessageManager messageManager; + private readonly ComponentMessageManager componentMessageManager; private readonly RenderManager renderManager; internal World( @@ -15,6 +16,7 @@ namespace Encompass EntityManager entityManager, ComponentManager componentManager, MessageManager messageManager, + ComponentMessageManager componentMessageManager, RenderManager renderManager ) { @@ -22,6 +24,7 @@ namespace Encompass this.entityManager = entityManager; this.componentManager = componentManager; this.messageManager = messageManager; + this.componentMessageManager = componentMessageManager; this.renderManager = renderManager; } @@ -33,6 +36,7 @@ namespace Encompass } messageManager.ClearMessages(); + componentMessageManager.ClearMessages(); entityManager.DestroyMarkedEntities(); componentManager.PerformComponentUpdates(); diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 46bdf52..678ae0a 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -15,6 +15,7 @@ namespace Encompass private readonly ComponentManager componentManager; private readonly EntityManager entityManager; private readonly MessageManager messageManager; + private readonly ComponentMessageManager componentMessageManager; private readonly DrawLayerManager drawLayerManager; private readonly RenderManager renderManager; @@ -23,16 +24,14 @@ namespace Encompass private readonly HashSet senders = new HashSet(); private readonly HashSet registeredComponentTypes = new HashSet(); - private readonly HashSet registeredNewComponentTypes = new HashSet(); public WorldBuilder() { - var entitiesWithAddedComponents = new HashSet(); - var entitiesWithRemovedComponents = new HashSet(); drawLayerManager = new DrawLayerManager(); componentManager = new ComponentManager(drawLayerManager); - entityManager = new EntityManager(componentManager); messageManager = new MessageManager(); + componentMessageManager = new ComponentMessageManager(); + entityManager = new EntityManager(componentManager, componentMessageManager); renderManager = new RenderManager(entityManager, componentManager, drawLayerManager); } @@ -74,6 +73,7 @@ namespace Encompass engine.AssignEntityManager(entityManager); engine.AssignComponentManager(componentManager); engine.AssignMessageManager(messageManager); + engine.AssignComponentMessageManager(componentMessageManager); engines.Add(engine); engineGraph.AddVertex(engine); @@ -250,6 +250,7 @@ namespace Encompass entityManager, componentManager, messageManager, + componentMessageManager, renderManager ); diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 51444db..215ad7c 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -112,7 +112,9 @@ namespace Tests var world = worldBuilder.Build(); - Assert.Throws(() => world.Update(0.01f)); + world.Update(0.01); + + Assert.That(resultComponent, Is.EqualTo(mockComponent).Or.EqualTo(mockComponentB)); } [Reads(typeof(MockComponent))]