optimize component message system

pull/5/head
Evan Hemsley 2019-08-01 16:24:57 -07:00
parent ec3b0df585
commit d86c002305
7 changed files with 425 additions and 47 deletions

View File

@ -1,13 +1,300 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Encompass
{
class ComponentMessageManager
{
private readonly Dictionary<Type, HashSet<IMessage>> componentMessageTypeToComponentMessages = new Dictionary<Type, HashSet<IMessage>>();
private readonly Dictionary<Guid, IComponent> componentIDToComponent = new Dictionary<Guid, IComponent>();
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToExistingComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToPendingComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly Dictionary<Entity, Dictionary<Type, HashSet<Guid>>> entityToTypeToExistingComponentIDs = new Dictionary<Entity, Dictionary<Type, HashSet<Guid>>>();
private readonly Dictionary<Entity, Dictionary<Type, HashSet<Guid>>> entityToTypeToPendingComponentIDs = new Dictionary<Entity, Dictionary<Type, HashSet<Guid>>>();
private readonly Dictionary<Entity, Dictionary<Type, HashSet<Guid>>> entityToTypeToComponentIDs = new Dictionary<Entity, Dictionary<Type, HashSet<Guid>>>();
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<TComponent>(ComponentMessage<TComponent> componentMessage) where TComponent : struct, IComponent
{
RegisterExistingOrPendingComponentMessage(componentMessage.componentID, componentMessage.component);
if (!componentMessageTypeToExistingComponentIDs.ContainsKey(typeof(TComponent)))
{
componentMessageTypeToExistingComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
}
componentMessageTypeToExistingComponentIDs[typeof(TComponent)].Add(componentMessage.componentID);
if (!entityToTypeToExistingComponentIDs.ContainsKey(componentMessage.entity))
{
entityToTypeToExistingComponentIDs.Add(componentMessage.entity, new Dictionary<Type, HashSet<Guid>>());
}
if (!entityToTypeToExistingComponentIDs[componentMessage.entity].ContainsKey(typeof(TComponent)))
{
entityToTypeToExistingComponentIDs[componentMessage.entity].Add(typeof(TComponent), new HashSet<Guid>());
}
entityToTypeToExistingComponentIDs[componentMessage.entity][typeof(TComponent)].Add(componentMessage.componentID);
}
internal void AddPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> pendingComponentMessage) where TComponent : struct, IComponent
{
RegisterExistingOrPendingComponentMessage(pendingComponentMessage.componentID, pendingComponentMessage.component);
if (!componentMessageTypeToPendingComponentIDs.ContainsKey(typeof(TComponent)))
{
componentMessageTypeToPendingComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
}
componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Add(pendingComponentMessage.componentID);
if (!entityToTypeToPendingComponentIDs.ContainsKey(pendingComponentMessage.entity))
{
entityToTypeToPendingComponentIDs.Add(pendingComponentMessage.entity, new Dictionary<Type, HashSet<Guid>>());
}
if (!entityToTypeToPendingComponentIDs[pendingComponentMessage.entity].ContainsKey(typeof(TComponent)))
{
entityToTypeToPendingComponentIDs[pendingComponentMessage.entity].Add(typeof(TComponent), new HashSet<Guid>());
}
entityToTypeToPendingComponentIDs[pendingComponentMessage.entity][typeof(TComponent)].Add(pendingComponentMessage.componentID);
}
private void RegisterExistingOrPendingComponentMessage<TComponent>(Guid componentID, TComponent component) where TComponent: struct, IComponent
{
componentIDToComponent[componentID] = component;
if (!componentMessageTypeToComponentIDs.ContainsKey(typeof(TComponent)))
{
componentMessageTypeToComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
}
componentMessageTypeToComponentIDs[typeof(TComponent)].Add(componentID);
}
// general component reads by type
internal IEnumerable<(Guid, TComponent)> ReadExistingAndPendingComponentsByType<TComponent>() where TComponent : struct, IComponent
{
HashSet<Guid> 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<TComponent>() where TComponent: struct, IComponent
{
HashSet<Guid> 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<TComponent>() where TComponent : struct, IComponent
{
HashSet<Guid> 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<TComponent>() where TComponent : struct, IComponent
{
return ReadExistingAndPendingComponentsByType<TComponent>().First();
}
internal (Guid, TComponent) ReadFirstExistingComponentByType<TComponent>() where TComponent : struct, IComponent
{
return ReadExistingComponentsByType<TComponent>().First();
}
internal (Guid, TComponent) ReadFirstPendingComponentByType<TComponent>() where TComponent : struct, IComponent
{
return ReadPendingComponentsByType<TComponent>().First();
}
// check if some component of type exists in the world
internal bool SomeExistingOrPendingComponent<TComponent>() where TComponent : struct, IComponent
{
HashSet<Guid> idSet;
if (componentMessageTypeToComponentIDs.TryGetValue(typeof(TComponent), out idSet))
{
return idSet.Count > 0;
}
return false;
}
internal bool SomeExistingComponent<TComponent>() where TComponent : struct, IComponent
{
HashSet<Guid> idSet;
if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out idSet))
{
return idSet.Count > 0;
}
return false;
}
internal bool SomePendingComponent<TComponent>() where TComponent : struct, IComponent
{
HashSet<Guid> 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<TComponent>(Entity entity) where TComponent : struct, IComponent
{
HashSet<Guid> 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<TComponent>(Entity entity) where TComponent : struct, IComponent
{
HashSet<Guid> 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<TComponent>(Entity entity) where TComponent : struct, IComponent
{
HashSet<Guid> 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<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadExistingAndPendingComponentsByEntityAndType<TComponent>(entity).First();
}
internal (Guid, TComponent) ReadFirstExistingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadExistingComponentsByEntityAndType<TComponent>(entity).First();
}
internal (Guid, TComponent) ReadFirstPendingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadPendingComponentsByEntityAndType<TComponent>(entity).First();
}
// check if entity has component of type
internal bool HasExistingOrPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
HashSet<Guid> idSet;
if (entityToTypeToComponentIDs.TryGetValue(entity, out _) && entityToTypeToComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet))
{
return idSet.Count > 0;
}
return false;
}
internal bool HasExistingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
HashSet<Guid> idSet;
if (entityToTypeToExistingComponentIDs.TryGetValue(entity, out _) && entityToTypeToExistingComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet))
{
return idSet.Count > 0;
}
return false;
}
internal bool HasPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
HashSet<Guid> idSet;
if (entityToTypeToPendingComponentIDs.TryGetValue(entity, out _) && entityToTypeToPendingComponentIDs[entity].TryGetValue(typeof(TComponent), out idSet))
{
return idSet.Count > 0;
}
return false;
}
}
}

View File

@ -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<TComponent>)))
{
PendingComponentMessage<TComponent> componentMessage;
componentMessage.entity = entity;
componentMessage.componentID = componentID;
componentMessage.component = component;
SendMessage(componentMessage);
PendingComponentMessage<TComponent> 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<ValueTuple<Entity, Guid, TComponent>> ExistingComponents<TComponent>() where TComponent : struct, IComponent
{
return ReadMessages<ComponentMessage<TComponent>>().Select((message) => (message.entity, message.componentID, message.component));
}
private IEnumerable<ValueTuple<Entity, Guid, TComponent>> PendingComponents<TComponent>() where TComponent : struct, IComponent
{
return ReadMessages<PendingComponentMessage<TComponent>>().Select((message) => (message.entity, message.componentID, message.component));
}
private IEnumerable<ValueTuple<Entity, Guid, TComponent>> ReadComponentMessages<TComponent>() where TComponent : struct, IComponent
protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead)
{
return ExistingComponents<TComponent>().Union(PendingComponents<TComponent>());
return componentMessageManager.ReadExistingAndPendingComponentsByType<TComponent>();
}
else if (existingRead)
{
return ExistingComponents<TComponent>();
return componentMessageManager.ReadExistingComponentsByType<TComponent>();
}
else if (pendingRead)
{
return PendingComponents<TComponent>();
return componentMessageManager.ReadPendingComponentsByType<TComponent>();
}
else
{
@ -179,44 +178,115 @@ namespace Encompass
}
}
private IEnumerable<ValueTuple<Guid, TComponent>> ExistingComponentsOnEntity<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadComponentMessages<TComponent>().Where((triple) => triple.Item1 == entity).Select((triple) => (triple.Item2, triple.Item3));
}
private IEnumerable<ValueTuple<Guid, TComponent>> PendingComponentsOnEntity<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ReadComponentMessages<TComponent>().Where((triple) => triple.Item1 == entity).Select((triple) => (triple.Item2, triple.Item3));
}
protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
{
return ReadComponentMessages<TComponent>().Select((triple) => (triple.Item2, triple.Item3));
}
protected ValueTuple<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
{
return ReadComponents<TComponent>().Single();
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadFirstExistingOrPendingComponentByType<TComponent>();
}
else if (existingRead)
{
return componentMessageManager.ReadFirstExistingComponentByType<TComponent>();
}
else if (pendingRead)
{
return componentMessageManager.ReadFirstPendingComponentByType<TComponent>();
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{
return ReadComponentMessages<TComponent>().Any();
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead)
{
return componentMessageManager.SomeExistingOrPendingComponent<TComponent>();
}
else if (existingRead)
{
return componentMessageManager.SomeExistingComponent<TComponent>();
}
else if (pendingRead)
{
return componentMessageManager.SomePendingComponent<TComponent>();
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return ExistingComponentsOnEntity<TComponent>(entity);
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadExistingAndPendingComponentsByEntityAndType<TComponent>(entity);
}
else if (existingRead)
{
return componentMessageManager.ReadExistingComponentsByEntityAndType<TComponent>(entity);
}
else if (pendingRead)
{
return componentMessageManager.ReadPendingComponentsByEntityAndType<TComponent>(entity);
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return GetComponents<TComponent>(entity).First();
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadFirstExistingOrPendingComponentByEntityAndType<TComponent>(entity);
}
else if (existingRead)
{
return componentMessageManager.ReadFirstExistingComponentByEntityAndType<TComponent>(entity);
}
else if (pendingRead)
{
return componentMessageManager.ReadFirstPendingComponentByEntityAndType<TComponent>(entity);
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return GetComponents<TComponent>(entity).Any();
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (pendingRead && existingRead)
{
return componentMessageManager.HasExistingOrPendingComponent<TComponent>(entity);
}
else if (existingRead)
{
return componentMessageManager.HasExistingComponent<TComponent>(entity);
}
else if (pendingRead)
{
return componentMessageManager.HasPendingComponent<TComponent>(entity);
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent
@ -260,6 +330,16 @@ namespace Encompass
messageManager.AddMessage(message);
}
internal void SendExistingComponentMessage<TComponent>(ComponentMessage<TComponent> message) where TComponent : struct, IComponent
{
componentMessageManager.AddExistingComponentMessage(message);
}
internal void SendPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> message) where TComponent : struct, IComponent
{
componentMessageManager.AddPendingComponentMessage(message);
}
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
{
if (!receiveTypes.Contains(typeof(TMessage)))

View File

@ -18,6 +18,7 @@ namespace Encompass.Engines
componentMessage.componentID = componentID;
componentMessage.component = component;
SendMessage(componentMessage);
SendExistingComponentMessage(componentMessage);
}
}
}

View File

@ -10,10 +10,12 @@ namespace Encompass
private readonly HashSet<Guid> entitiesMarkedForDestroy = new HashSet<Guid>();
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);

View File

@ -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();

View File

@ -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<Engine> senders = new HashSet<Engine>();
private readonly HashSet<Type> registeredComponentTypes = new HashSet<Type>();
private readonly HashSet<Type> registeredNewComponentTypes = new HashSet<Type>();
public WorldBuilder()
{
var entitiesWithAddedComponents = new HashSet<Guid>();
var entitiesWithRemovedComponents = new HashSet<Guid>();
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
);

View File

@ -112,7 +112,9 @@ namespace Tests
var world = worldBuilder.Build();
Assert.Throws<InvalidOperationException>(() => world.Update(0.01f));
world.Update(0.01);
Assert.That(resultComponent, Is.EqualTo(mockComponent).Or.EqualTo(mockComponentB));
}
[Reads(typeof(MockComponent))]