Merge pull request #4 from thatcosmonaut/gc_optimization

GC Optimization
pull/5/head
thatcosmonaut 2019-12-06 13:14:51 -08:00 committed by GitHub
commit 447a668e1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 856 additions and 1112 deletions

View File

@ -21,7 +21,7 @@ namespace Encompass
throw new IllegalReadTypeException("{0} must be a Component", readType.Name);
}
this.readTypes.Add(typeof(ComponentMessage<>).MakeGenericType(readType));
this.readTypes.Add(readType);
}
}
}

View File

@ -21,7 +21,7 @@ namespace Encompass
throw new IllegalReadTypeException("{0} must be a Component", readPendingType.Name);
}
this.readPendingTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(readPendingType));
this.readPendingTypes.Add(readPendingType);
}
}
}

View File

@ -21,7 +21,7 @@ namespace Encompass
throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
}
this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType));
this.writeTypes.Add(writeType);
}
}
@ -33,8 +33,8 @@ namespace Encompass
throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
}
this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType));
this.priorities.Add(writeType, priority);
writeTypes.Add(writeType);
priorities.Add(writeType, priority);
}
}
}

View File

@ -19,7 +19,7 @@ namespace Encompass
throw new IllegalWritePendingTypeException("{0} must be a Component", writePendingType.Name);
}
this.writePendingTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(writePendingType));
this.writePendingTypes.Add(writePendingType);
}
}
}

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
internal class ComponentStore
{
private Dictionary<Type, TypedComponentStore> Stores = new Dictionary<Type, TypedComponentStore>(512);
public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable()
{
foreach (var entry in Stores)
{
yield return (entry.Key, entry.Value);
}
}
private TypedComponentStore<TComponent> Lookup<TComponent>() where TComponent : struct, IComponent
{
if (!Stores.ContainsKey(typeof(TComponent)))
{
var store = new TypedComponentStore<TComponent>();
Stores.Add(typeof(TComponent), store);
}
return Stores[typeof(TComponent)] as TypedComponentStore<TComponent>;
}
public bool Has<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return Lookup<TComponent>().Has(entity);
}
public bool Has(Type type, Entity entity)
{
return Stores.ContainsKey(type) && Stores[type].Has(entity);
}
public TComponent Get<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return Lookup<TComponent>().Get(entity);
}
public void Set<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
Lookup<TComponent>().Set(entity, component);
}
public bool Set<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
return Lookup<TComponent>().Set(entity, component, priority);
}
public void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent
{
Lookup<TComponent>().Remove(entity);
}
public void Remove(Entity entity)
{
foreach (var entry in Stores.Values)
{
entry.Remove(entity);
}
}
public bool Any<TComponent>() where TComponent : struct, IComponent
{
return Lookup<TComponent>().Count > 0;
}
public IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped()
{
foreach (var store in Stores.Values)
{
foreach (var thing in store.AllInterfaceTyped())
{
yield return thing;
}
}
}
public IEnumerable<(Entity, TComponent)> All<TComponent>() where TComponent : struct, IComponent
{
return Lookup<TComponent>().All();
}
public void Clear<TComponent>() where TComponent : struct, IComponent
{
Lookup<TComponent>().Clear();
}
public void ClearAll()
{
foreach (var store in Stores.Values)
{
store.Clear();
}
}
public void SwapWith(ComponentStore other)
{
(Stores, other.Stores) = (other.Stores, Stores);
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
internal class MessageStore
{
private Dictionary<Type, TypedMessageStore> Stores = new Dictionary<Type, TypedMessageStore>(512);
private void RegisterMessageType<TMessage>() where TMessage : struct, IMessage
{
Stores.Add(typeof(TMessage), new TypedMessageStore<TMessage>());
}
private TypedMessageStore<TMessage> Lookup<TMessage>() where TMessage : struct, IMessage
{
if (!Stores.ContainsKey(typeof(TMessage))) { RegisterMessageType<TMessage>(); }
return Stores[typeof(TMessage)] as TypedMessageStore<TMessage>;
}
public void AddMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
{
Lookup<TMessage>().Add(message);
}
public void AddMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
Lookup<TMessage>().Add(message, time);
}
public void AddMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
Lookup<TMessage>().AddIgnoringTimeDilation(message, time);
}
public TMessage First<TMessage>() where TMessage : struct, IMessage
{
return Lookup<TMessage>().First();
}
public IEnumerable<TMessage> All<TMessage>() where TMessage : struct, IMessage
{
return Lookup<TMessage>().All();
}
public bool Any<TMessage>() where TMessage : struct, IMessage
{
return Lookup<TMessage>().Any();
}
public void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
{
foreach (var store in Stores.Values)
{
store.ProcessDelayedMessages(dilatedDelta, realtimeDelta);
}
}
public void ClearAll()
{
foreach (var store in Stores.Values)
{
store.Clear();
}
}
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal abstract class TypedComponentStore
{
public abstract int Count { get; }
public abstract IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped();
public abstract bool Has(Entity entity);
public abstract void Remove(Entity entity);
public abstract void Clear();
}
internal class TypedComponentStore<TComponent> : TypedComponentStore where TComponent : struct, IComponent
{
private readonly Dictionary<Entity, TComponent> store = new Dictionary<Entity, TComponent>(128);
private readonly Dictionary<Entity, int> priorities = new Dictionary<Entity, int>(128);
public override int Count { get => store.Count; }
public TComponent Get(Entity entity)
{
return store[entity];
}
public void Set(Entity entity, TComponent component)
{
store[entity] = component;
}
public bool Set(Entity entity, TComponent component, int priority)
{
if (!priorities.ContainsKey(entity) || priority < priorities[entity]) {
store[entity] = component;
priorities[entity] = priority;
return true;
}
return false;
}
public override bool Has(Entity entity)
{
return store.ContainsKey(entity);
}
public override void Clear()
{
store.Clear();
priorities.Clear();
}
public IEnumerable<(Entity, TComponent)> All()
{
return store.Select(kvp => (kvp.Key, kvp.Value));
}
public override IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped()
{
return store.Select(kvp => (kvp.Key, typeof(TComponent), (IComponent)kvp.Value));
}
// public override IEnumerable<T> All<T>()
// {
// return store.Values.Cast<T>();
// }
public override void Remove(Entity entity)
{
store.Remove(entity);
priorities.Remove(entity);
}
}
}

View File

@ -0,0 +1,89 @@
using System.Collections.Generic;
namespace Encompass
{
internal abstract class TypedMessageStore
{
public abstract void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta);
public abstract void Clear();
}
internal class TypedMessageStore<TMessage> : TypedMessageStore where TMessage : struct, IMessage
{
private readonly List<TMessage> store = new List<TMessage>(128);
private readonly List<(TMessage, double)> delayedStore = new List<(TMessage, double)>(128);
private readonly List<(TMessage, double)> delayedStoreIgnoringTimeDilation = new List<(TMessage, double)>(128);
public override void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
{
for (int i = delayedStore.Count - 1; i >= 0; i--)
{
var (message, time) = delayedStore[i];
var updatedTime = time - dilatedDelta;
if (updatedTime <= 0)
{
Add(message);
delayedStore.RemoveAt(i);
}
else
{
delayedStore[i] = (message, updatedTime);
}
}
for (int i = delayedStoreIgnoringTimeDilation.Count - 1; i >= 0; i--)
{
var (message, time) = delayedStoreIgnoringTimeDilation[i];
var updatedTime = time - realtimeDelta;
if (updatedTime <= 0)
{
Add(message);
delayedStoreIgnoringTimeDilation.RemoveAt(i);
}
else
{
delayedStoreIgnoringTimeDilation[i] = (message, updatedTime);
}
}
}
public void Add(TMessage message)
{
store.Add(message);
}
public void Add(TMessage message, double time)
{
delayedStore.Add((message, time));
}
public void AddIgnoringTimeDilation(TMessage message, double time)
{
delayedStoreIgnoringTimeDilation.Add((message, time));
}
public TMessage First()
{
return store[0];
}
public bool Any()
{
return store.Count > 0;
}
public IEnumerable<TMessage> All()
{
return store;
}
public override void Clear()
{
store.Clear();
}
}
}

View File

@ -1,265 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
using Collections.Pooled;
namespace Encompass
{
internal class ComponentManager
{
private readonly DrawLayerManager drawLayerManager;
private readonly ComponentUpdateManager componentUpdateManager;
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>();
private readonly Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>();
private readonly Dictionary<Guid, PooledSet<Guid>> entityIDToComponentIDs = new Dictionary<Guid, PooledSet<Guid>>();
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
private readonly ComponentStore componentStore = new ComponentStore();
private readonly HashSet<Entity> entitiesMarkedForRemoval = new HashSet<Entity>();
private readonly Dictionary<Guid, PooledDictionary<Type, Guid>> entityIDToComponentTypeToComponentID = new Dictionary<Guid, PooledDictionary<Type, Guid>>();
private readonly Dictionary<Type, HashSet<Guid>> typeToComponentIDs = new Dictionary<Type, HashSet<Guid>>();
private readonly Dictionary<(Entity, Type), (Guid, IComponent)> componentWriteData = new Dictionary<(Entity, Type), (Guid, IComponent)>();
private readonly Dictionary<(Entity, Type), int> componentWritePriorities = new Dictionary<(Entity, Type), int>();
private readonly HashSet<Guid> componentIDsMarkedForWrite = new HashSet<Guid>();
private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>();
public ComponentManager(DrawLayerManager drawLayerManager)
public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager)
{
this.drawLayerManager = drawLayerManager;
this.componentUpdateManager = componentUpdateManager;
}
internal void RegisterEntity(Guid entityID)
internal void SetComponentStore(ComponentStore componentStore)
{
entityIDToComponentIDs.Add(entityID, new PooledSet<Guid>());
entityIDToComponentTypeToComponentID.Add(entityID, new PooledDictionary<Type, Guid>());
this.componentStore.SwapWith(componentStore);
}
private Guid NextID()
internal void RegisterDrawableComponent<TComponent>(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent
{
return Guid.NewGuid();
drawLayerManager.RegisterComponentWithLayer(entity, component, layer);
}
internal Guid MarkComponentForWrite<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
internal void AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
Guid id;
if (EntityHasComponentOfType<TComponent>(entity))
{
id = GetComponentByEntityAndType<TComponent>(entity).Item1;
}
else
{
id = NextID();
}
if (componentWriteData.ContainsKey((entity, typeof(TComponent))))
{
var currentPriority = componentWritePriorities[(entity, typeof(TComponent))];
if (priority < currentPriority)
{
componentWriteData[(entity, typeof(TComponent))] = (id, component);
componentWritePriorities[(entity, typeof(TComponent))] = priority;
componentIDsMarkedForWrite.Add(id);
}
}
else
{
componentWriteData.Add((entity, typeof(TComponent)), (id, component));
componentWritePriorities[(entity, typeof(TComponent))] = priority;
componentIDsMarkedForWrite.Add(id);
}
return id;
}
internal void RegisterDrawableComponent<TComponent>(Guid componentID, TComponent component) where TComponent : IDrawableComponent
{
drawLayerManager.RegisterComponentWithLayer(componentID, component.Layer);
}
internal void AddComponent(Entity entity, Type type, Guid componentID, IComponent component)
{
IDToComponent[componentID] = component;
componentIDToEntityID[componentID] = entity.ID;
componentIDToType[componentID] = type;
entityIDToComponentTypeToComponentID[entity.ID][type] = componentID;
if (!typeToComponentIDs.ContainsKey(type))
{
typeToComponentIDs.Add(type, new HashSet<Guid>());
}
typeToComponentIDs[type].Add(componentID);
entityIDToComponentIDs[entity.ID].Add(componentID);
}
internal void UpdateComponent(Guid componentID, IComponent component)
{
IDToComponent[componentID] = component;
componentStore.Set(entity, component);
}
internal void WriteComponents()
{
foreach (var keyValuePair in componentWriteData)
{
var (entity, type) = keyValuePair.Key;
var (componentID, component) = keyValuePair.Value;
if (!componentIDsMarkedForWrite.Contains(componentID) || !entityIDToComponentTypeToComponentID.ContainsKey(entity.ID)) { continue; }
if (entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(type))
{
UpdateComponent(componentID, component);
}
else
{
AddComponent(entity, type, componentID, component);
}
}
componentWriteData.Clear();
componentIDsMarkedForWrite.Clear();
componentWritePriorities.Clear();
componentStore.SwapWith(componentUpdateManager.UpToDateComponentStore);
}
internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID)
internal IEnumerable<(TComponent, Entity)> GetComponentsIncludingEntity<TComponent>() where TComponent : struct, IComponent
{
if (entityIDToComponentIDs.TryGetValue(entityID, out PooledSet<Guid> idSet))
{
return idSet;
}
return Enumerable.Empty<Guid>();
return componentStore.All<TComponent>().Select(pair => (pair.Item2, pair.Item1));
}
internal IEnumerable<(Guid, TComponent)> GetComponentsByType<TComponent>() where TComponent : struct, IComponent
internal IEnumerable<TComponent> GetComponentsByType<TComponent>() where TComponent : struct, IComponent
{
if (typeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Select(id => (id, (TComponent)IDToComponent[id]));
}
return Enumerable.Empty<(Guid, TComponent)>();
return componentStore.All<TComponent>().Select(pair => pair.Item2);
}
internal (Guid, TComponent) GetComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
internal TComponent GetComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (entityIDToComponentTypeToComponentID.ContainsKey(entity.ID) && entityIDToComponentTypeToComponentID[entity.ID].TryGetValue(typeof(TComponent), out Guid id))
{
return (id, (TComponent)IDToComponent[id]);
}
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID);
return componentStore.Get<TComponent>(entity);
}
internal bool EntityHasComponentOfType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return (entityIDToComponentTypeToComponentID.ContainsKey(entity.ID) && entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(typeof(TComponent)));
return componentStore.Has<TComponent>(entity);
}
internal bool ComponentOfTypeExists<TComponent>() where TComponent : struct, IComponent
{
if (typeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Count > 0;
}
return false;
return componentStore.Any<TComponent>();
}
internal IComponent GetComponentByID(Guid componentID)
internal void MarkAllComponentsOnEntityForRemoval(Entity entity)
{
if (IDToComponent.ContainsKey(componentID))
{
return IDToComponent[componentID];
}
else
{
throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID);
}
}
internal Type GetComponentTypeByID(Guid componentID)
{
if (componentIDToType.ContainsKey(componentID))
{
return componentIDToType[componentID];
}
else
{
throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID);
}
}
internal Guid GetEntityIDByComponentID(Guid componentID)
{
if (componentIDToEntityID.ContainsKey(componentID))
{
return componentIDToEntityID[componentID];
}
else
{
throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID);
}
}
internal void MarkAllComponentsOnEntityForRemoval(Guid entityID)
{
foreach (var componentID in GetComponentIDsByEntityID(entityID))
{
MarkForRemoval(componentID);
}
}
internal void MarkForRemoval(Guid componentID)
{
componentsMarkedForRemoval.Add(componentID);
entitiesMarkedForRemoval.Add(entity);
}
internal void RemoveMarkedComponents()
{
foreach (var componentID in componentsMarkedForRemoval)
foreach (var entity in entitiesMarkedForRemoval)
{
if (componentIDsMarkedForWrite.Contains(componentID))
{
componentIDsMarkedForWrite.Remove(componentID);
}
if (IDToComponent.ContainsKey(componentID))
{
Remove(componentID);
}
componentStore.Remove(entity);
drawLayerManager.UnRegisterEntityWithLayer(entity);
}
componentsMarkedForRemoval.Clear();
entitiesMarkedForRemoval.Clear();
}
private void Remove(Guid componentID)
public void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent
{
var type = componentIDToType[componentID];
var entityID = componentIDToEntityID[componentID];
if (entityIDToComponentIDs.ContainsKey(entityID))
{
entityIDToComponentIDs[entityID].Remove(componentID);
}
if (entityIDToComponentTypeToComponentID.ContainsKey(entityID))
{
entityIDToComponentTypeToComponentID[entityID].Remove(type);
}
IDToComponent.Remove(componentID);
componentIDToType.Remove(componentID);
componentIDToEntityID.Remove(componentID);
typeToComponentIDs[type].Remove(componentID);
drawLayerManager.UnRegisterComponentWithLayer(componentID);
}
public void RegisterDestroyedEntity(Guid entityID)
{
entityIDToComponentIDs[entityID].Dispose();
entityIDToComponentIDs.Remove(entityID);
entityIDToComponentTypeToComponentID[entityID].Dispose();
entityIDToComponentTypeToComponentID.Remove(entityID);
componentUpdateManager.Remove<TComponent>(entity);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity);
}
}
}

View File

@ -1,356 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Collections.Pooled;
using Encompass.Exceptions;
namespace Encompass
{
class ComponentMessageManager
{
private readonly Dictionary<Guid, IComponent> componentIDToComponent = new Dictionary<Guid, IComponent>();
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>();
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
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, PooledDictionary<Type, Guid>> entityToTypeToExistingComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>();
private readonly Dictionary<Entity, PooledDictionary<Type, Guid>> entityToTypeToPendingComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>();
private readonly Dictionary<Entity, PooledDictionary<Type, Guid>> entityToTypeToComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>();
private readonly Dictionary<Entity, PooledDictionary<Type, int>> entityToTypeToPendingComponentPriority = new Dictionary<Entity, PooledDictionary<Type, int>>();
internal void RegisterEntity(Entity entity)
{
entityToTypeToComponentID[entity] = new PooledDictionary<Type, Guid>();
entityToTypeToPendingComponentID[entity] = new PooledDictionary<Type, Guid>();
entityToTypeToPendingComponentPriority[entity] = new PooledDictionary<Type, int>();
entityToTypeToExistingComponentID[entity] = new PooledDictionary<Type, Guid>();
}
internal void RegisterDestroyedEntity(Entity entity)
{
entityToTypeToComponentID[entity].Dispose();
entityToTypeToComponentID.Remove(entity);
entityToTypeToPendingComponentID[entity].Dispose();
entityToTypeToPendingComponentID.Remove(entity);
entityToTypeToPendingComponentPriority[entity].Dispose();
entityToTypeToPendingComponentPriority.Remove(entity);
entityToTypeToExistingComponentID[entity].Dispose();
entityToTypeToExistingComponentID.Remove(entity);
}
internal void ClearMessages()
{
componentIDToComponent.Clear();
componentIDToType.Clear();
componentIDToEntityID.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 entityToTypeToExistingComponentID.Values)
{
dictionary.Clear();
}
foreach (var dictionary in entityToTypeToPendingComponentID.Values)
{
dictionary.Clear();
}
foreach (var dictionary in entityToTypeToComponentID.Values)
{
dictionary.Clear();
}
foreach (var dictionary in entityToTypeToPendingComponentPriority.Values)
{
dictionary.Clear();
}
}
internal void AddExistingComponentMessage<TComponent>(ComponentMessage<TComponent> componentMessage) where TComponent : struct, IComponent
{
RegisterExistingOrPendingComponentMessage(componentMessage.entity, componentMessage.componentID, componentMessage.component);
if (!componentMessageTypeToExistingComponentIDs.ContainsKey(typeof(TComponent)))
{
componentMessageTypeToExistingComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
}
componentMessageTypeToExistingComponentIDs[typeof(TComponent)].Add(componentMessage.componentID);
if (!entityToTypeToExistingComponentID[componentMessage.entity].ContainsKey(typeof(TComponent)))
{
entityToTypeToExistingComponentID[componentMessage.entity].Add(typeof(TComponent), componentMessage.componentID);
}
else
{
throw new MultipleComponentOfSameTypeException("Entity {0} cannot have multiple components of type {1}", componentMessage.entity.ID, typeof(TComponent).Name);
}
}
internal void AddPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> pendingComponentMessage) where TComponent : struct, IComponent
{
RegisterExistingOrPendingComponentMessage(pendingComponentMessage.entity, pendingComponentMessage.componentID, pendingComponentMessage.component);
if (!componentMessageTypeToPendingComponentIDs.ContainsKey(typeof(TComponent)))
{
componentMessageTypeToPendingComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
}
if (!entityToTypeToPendingComponentID[pendingComponentMessage.entity].ContainsKey(typeof(TComponent)))
{
entityToTypeToPendingComponentID[pendingComponentMessage.entity].Add(typeof(TComponent), pendingComponentMessage.componentID);
entityToTypeToPendingComponentPriority[pendingComponentMessage.entity].Add(typeof(TComponent), pendingComponentMessage.priority);
componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Add(pendingComponentMessage.componentID);
}
else
{
if (pendingComponentMessage.priority < entityToTypeToPendingComponentPriority[pendingComponentMessage.entity][typeof(TComponent)])
{
componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Remove(entityToTypeToPendingComponentID[pendingComponentMessage.entity][typeof(TComponent)]);
entityToTypeToPendingComponentID[pendingComponentMessage.entity][typeof(TComponent)] = pendingComponentMessage.componentID;
entityToTypeToPendingComponentPriority[pendingComponentMessage.entity][typeof(TComponent)] = pendingComponentMessage.priority;
componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Add(pendingComponentMessage.componentID);
}
}
}
private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, Guid componentID, TComponent component) where TComponent : struct, IComponent
{
componentIDToComponent[componentID] = component;
componentIDToEntityID[componentID] = entity.ID;
componentIDToType[componentID] = typeof(TComponent);
if (!componentMessageTypeToComponentIDs.ContainsKey(typeof(TComponent)))
{
componentMessageTypeToComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
}
componentMessageTypeToComponentIDs[typeof(TComponent)].Add(componentID);
entityToTypeToComponentID[entity][typeof(TComponent)] = componentID;
}
// general component reads by type
internal IEnumerable<(Guid, TComponent)> ReadExistingAndPendingComponentsByType<TComponent>() where TComponent : struct, IComponent
{
if (componentMessageTypeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Select(id => (id, (TComponent)componentIDToComponent[id]));
}
return Enumerable.Empty<(Guid, TComponent)>();
}
internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByType<TComponent>() where TComponent : struct, IComponent
{
if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Select(id => (id, (TComponent)componentIDToComponent[id]));
}
return Enumerable.Empty<(Guid, TComponent)>();
}
internal IEnumerable<(Guid, TComponent)> ReadPendingComponentsByType<TComponent>() where TComponent : struct, IComponent
{
if (componentMessageTypeToPendingComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> 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
{
if (!SomeExistingOrPendingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
return ReadExistingAndPendingComponentsByType<TComponent>().First();
}
internal (Guid, TComponent) ReadFirstExistingComponentByType<TComponent>() where TComponent : struct, IComponent
{
if (!SomeExistingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
return ReadExistingComponentsByType<TComponent>().First();
}
internal (Guid, TComponent) ReadFirstPendingComponentByType<TComponent>() where TComponent : struct, IComponent
{
if (!SomeExistingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
return ReadPendingComponentsByType<TComponent>().First();
}
// check if some component of type exists in the world
internal bool SomeExistingOrPendingComponent<TComponent>() where TComponent : struct, IComponent
{
if (componentMessageTypeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Count > 0;
}
return false;
}
internal bool SomeExistingComponent<TComponent>() where TComponent : struct, IComponent
{
if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Count > 0;
}
return false;
}
internal bool SomePendingComponent<TComponent>() where TComponent : struct, IComponent
{
if (componentMessageTypeToPendingComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
{
return idSet.Count > 0;
}
return false;
}
// read components by entity and type
internal (Guid, TComponent) ReadExistingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (entityToTypeToExistingComponentID.ContainsKey(entity) && entityToTypeToExistingComponentID[entity].TryGetValue(typeof(TComponent), out Guid id))
{
return (id, (TComponent)componentIDToComponent[id]);
}
else
{
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID);
}
}
internal (Guid, IComponent) ReadExistingComponentByEntityAndType(Entity entity, Type type)
{
if (entityToTypeToExistingComponentID.ContainsKey(entity) && entityToTypeToExistingComponentID[entity].TryGetValue(type, out Guid id))
{
return (id, componentIDToComponent[id]);
}
else
{
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", type.Name, entity.ID);
}
}
internal (Guid, TComponent) ReadPendingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (entityToTypeToPendingComponentID.ContainsKey(entity) && entityToTypeToPendingComponentID[entity].TryGetValue(typeof(TComponent), out Guid id))
{
return (id, (TComponent)componentIDToComponent[id]);
}
else
{
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID);
}
}
internal (Guid, IComponent) ReadPendingComponentByEntityAndType(Entity entity, Type type)
{
if (entityToTypeToPendingComponentID.ContainsKey(entity) && entityToTypeToPendingComponentID[entity].TryGetValue(type, out Guid id))
{
return (id, componentIDToComponent[id]);
}
else
{
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", type.Name, entity.ID);
}
}
// check if entity has component of type
internal bool HasExistingOrPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return entityToTypeToComponentID.ContainsKey(entity) && entityToTypeToComponentID[entity].ContainsKey(typeof(TComponent));
}
internal bool HasExistingOrPendingComponent(Entity entity, Type type)
{
return entityToTypeToComponentID.ContainsKey(entity) && entityToTypeToComponentID[entity].ContainsKey(type);
}
internal bool HasExistingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return entityToTypeToExistingComponentID.ContainsKey(entity) && entityToTypeToExistingComponentID[entity].ContainsKey(typeof(TComponent));
}
internal bool HasExistingComponent(Entity entity, Type type)
{
return entityToTypeToExistingComponentID.ContainsKey(entity) && entityToTypeToExistingComponentID[entity].ContainsKey(type);
}
internal bool HasPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return entityToTypeToPendingComponentID.ContainsKey(entity) && entityToTypeToPendingComponentID[entity].ContainsKey(typeof(TComponent));
}
internal bool HasPendingComponent(Entity entity, Type type)
{
return entityToTypeToPendingComponentID.ContainsKey(entity) && entityToTypeToPendingComponentID[entity].ContainsKey(type);
}
internal IComponent GetComponentByID(Guid componentID)
{
if (componentIDToComponent.ContainsKey(componentID))
{
return componentIDToComponent[componentID];
}
else
{
throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID);
}
}
internal Type GetComponentTypeByID(Guid componentID)
{
if (componentIDToType.ContainsKey(componentID))
{
return componentIDToType[componentID];
}
else
{
throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID);
}
}
internal Guid GetEntityIDByComponentID(Guid componentID)
{
if (componentIDToEntityID.ContainsKey(componentID))
{
return componentIDToEntityID[componentID];
}
else
{
throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID);
}
}
}
}

View File

@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal class ComponentUpdateManager
{
private readonly ComponentStore existingAndPendingComponentStore = new ComponentStore();
private readonly ComponentStore existingComponentStore = new ComponentStore();
private readonly ComponentStore pendingComponentStore = new ComponentStore();
private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToPendingComponentPriority = new Dictionary<Type, Dictionary<Entity, int>>(128);
public ComponentStore UpToDateComponentStore { get; private set; } = new ComponentStore();
internal void Clear()
{
existingAndPendingComponentStore.ClearAll();
existingComponentStore.ClearAll();
pendingComponentStore.ClearAll();
UpToDateComponentStore.ClearAll();
foreach (var dictionary in typeToEntityToPendingComponentPriority.Values)
{
dictionary.Clear();
}
}
internal void SetStartingComponentStore(ComponentStore componentStore)
{
UpToDateComponentStore = componentStore;
}
internal void AddExistingComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
RegisterExistingOrPendingComponentMessage(entity, component);
existingComponentStore.Set(entity, component);
}
internal bool AddPendingComponent<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
if (pendingComponentStore.Set(entity, component, priority))
{
RegisterExistingOrPendingComponentMessage(entity, component);
return true;
}
return false;
}
private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
existingAndPendingComponentStore.Set(entity, component);
UpToDateComponentStore.Set(entity, component);
}
public bool UpdateComponent<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
return UpToDateComponentStore.Set<TComponent>(entity, component, priority);
}
// general component reads by type
internal IEnumerable<(Entity, TComponent)> ReadExistingAndPendingComponentsByType<TComponent>() where TComponent : struct, IComponent
{
return existingAndPendingComponentStore.All<TComponent>();
}
internal IEnumerable<(Entity, TComponent)> ReadExistingComponentsByType<TComponent>() where TComponent : struct, IComponent
{
return existingComponentStore.All<TComponent>();
}
internal IEnumerable<(Entity, TComponent)> ReadPendingComponentsByType<TComponent>() where TComponent : struct, IComponent
{
return pendingComponentStore.All<TComponent>();
}
// singular component reads by type
internal (Entity, TComponent) ReadFirstExistingOrPendingComponentByType<TComponent>() where TComponent : struct, IComponent
{
if (!SomeExistingOrPendingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
return ReadExistingAndPendingComponentsByType<TComponent>().First();
}
internal (Entity, TComponent) ReadFirstExistingComponentByType<TComponent>() where TComponent : struct, IComponent
{
if (!SomeExistingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
return ReadExistingComponentsByType<TComponent>().First();
}
internal (Entity, TComponent) ReadFirstPendingComponentByType<TComponent>() where TComponent : struct, IComponent
{
if (!SomePendingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
return ReadPendingComponentsByType<TComponent>().First();
}
// check if some component of type exists in the world
internal bool SomeExistingOrPendingComponent<TComponent>() where TComponent : struct, IComponent
{
return existingAndPendingComponentStore.Any<TComponent>();
}
internal bool SomeExistingComponent<TComponent>() where TComponent : struct, IComponent
{
return existingComponentStore.Any<TComponent>();
}
internal bool SomePendingComponent<TComponent>() where TComponent : struct, IComponent
{
return pendingComponentStore.Any<TComponent>();
}
// read components by entity and type
internal TComponent ReadExistingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return existingComponentStore.Get<TComponent>(entity);
}
internal TComponent ReadPendingComponentByEntityAndType<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return pendingComponentStore.Get<TComponent>(entity);
}
// check if entity has component of type
internal bool HasExistingOrPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return existingAndPendingComponentStore.Has<TComponent>(entity);
}
internal bool HasExistingOrPendingComponent(Entity entity, Type type)
{
return existingAndPendingComponentStore.Has(type, entity);
}
internal bool HasExistingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return existingComponentStore.Has<TComponent>(entity);
}
internal bool HasExistingComponent(Entity entity, Type type)
{
return existingComponentStore.Has(type, entity);
}
internal bool HasPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return pendingComponentStore.Has<TComponent>(entity);
}
internal bool HasPendingComponent(Entity entity, Type type)
{
return pendingComponentStore.Has(type, entity);
}
internal void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent
{
UpToDateComponentStore.Remove<TComponent>(entity);
}
}
}

View File

@ -8,10 +8,10 @@ namespace Encompass
{
private readonly SortedList<int, int> layerOrder = new SortedList<int, int>();
private readonly Dictionary<int, HashSet<Guid>> layerIndexToComponentIDs = new Dictionary<int, HashSet<Guid>>();
private readonly Dictionary<int, HashSet<GeneralRenderer>> layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>();
private readonly Dictionary<int, ComponentStore> layerIndexToComponentStore = new Dictionary<int, ComponentStore>(512);
private readonly Dictionary<int, HashSet<GeneralRenderer>> layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>(512);
private readonly Dictionary<Guid, int> componentIDToLayerIndex = new Dictionary<Guid, int>();
private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToLayer = new Dictionary<Type, Dictionary<Entity, int>>(512);
public IEnumerable<int> LayerOrder { get { return layerOrder.Values; } }
@ -49,23 +49,28 @@ namespace Encompass
RegisterGeneralRendererWithLayer(renderer, newLayer);
}
public void RegisterComponentWithLayer(Guid id, int layer)
public void RegisterComponentWithLayer<TComponent>(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent
{
if (componentIDToLayerIndex.ContainsKey(id)) { UnRegisterComponentWithLayer(id); }
if (layerIndexToComponentIDs.ContainsKey(layer))
if (!typeToEntityToLayer.ContainsKey(typeof(TComponent)))
{
var set = layerIndexToComponentIDs[layer];
set.Add(id);
typeToEntityToLayer.Add(typeof(TComponent), new Dictionary<Entity, int>());
}
if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) { UnRegisterComponentWithLayer<TComponent>(entity); }
if (layerIndexToComponentStore.ContainsKey(layer))
{
var set = layerIndexToComponentStore[layer];
set.Set<TComponent>(entity, component);
}
else
{
var set = new HashSet<Guid>();
layerIndexToComponentIDs.Add(layer, set);
set.Add(id);
var set = new ComponentStore();
layerIndexToComponentStore.Add(layer, set);
set.Set<TComponent>(entity, component);
}
componentIDToLayerIndex[id] = layer;
typeToEntityToLayer[typeof(TComponent)].Add(entity, layer);
if (!layerOrder.ContainsKey(layer))
{
@ -73,21 +78,24 @@ namespace Encompass
}
}
public void UnRegisterComponentWithLayer(Guid id)
public void UnRegisterComponentWithLayer<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (componentIDToLayerIndex.ContainsKey(id))
if (!typeToEntityToLayer.ContainsKey(typeof(TComponent))) { return; }
if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity))
{
var layer = componentIDToLayerIndex[id];
layerIndexToComponentIDs[layer].Remove(id);
var layer = typeToEntityToLayer[typeof(TComponent)][entity];
layerIndexToComponentStore[layer].Remove<TComponent>(entity);
}
componentIDToLayerIndex.Remove(id);
typeToEntityToLayer[typeof(TComponent)].Remove(entity);
}
public IEnumerable<Guid> ComponentIDsByLayer(int layer)
public void UnRegisterEntityWithLayer(Entity entity)
{
return layerIndexToComponentIDs.ContainsKey(layer) ?
layerIndexToComponentIDs[layer] :
Enumerable.Empty<Guid>();
foreach (var store in layerIndexToComponentStore.Values)
{
store.Remove(entity);
}
}
public IEnumerable<GeneralRenderer> GeneralRenderersByLayer(int layer)
@ -96,5 +104,12 @@ namespace Encompass
layerIndexToGeneralRenderers[layer] :
Enumerable.Empty<GeneralRenderer>();
}
public IEnumerable<(Entity, Type, IComponent)> AllInLayer(int layer)
{
return layerIndexToComponentStore.ContainsKey(layer) ?
layerIndexToComponentStore[layer].AllInterfaceTyped() :
Enumerable.Empty<(Entity, Type, IComponent)>();
}
}
}

View File

@ -15,23 +15,25 @@ namespace Encompass
{
internal Guid ID;
internal readonly HashSet<Type> readTypes = new HashSet<Type>();
internal readonly HashSet<Type> readPendingTypes = new HashSet<Type>();
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
internal readonly HashSet<Type> writeTypes = new HashSet<Type>();
internal readonly HashSet<Type> writePendingTypes = new HashSet<Type>();
internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
internal readonly int defaultWritePriority = 0;
/// <summary>
/// If false, the Engine will ignore time dilation.
/// If false, the Engine will ignore time dilation.
/// </summary>
internal bool usesTimeDilation = true;
public bool TimeDilationActive { get => usesTimeDilation && timeManager.TimeDilationActive; }
/// <summary>
/// Used when activating time dilation. Lower priority overrides higher priority.
/// </summary>
private EntityManager entityManager;
private MessageManager messageManager;
private ComponentManager componentManager;
private ComponentMessageManager componentMessageManager;
private ComponentUpdateManager componentUpdateManager;
private TimeManager timeManager;
protected Engine()
@ -47,13 +49,19 @@ namespace Encompass
var activatesAttribute = GetType().GetCustomAttribute<WritesPending>(false);
if (activatesAttribute != null)
{
sendTypes.UnionWith(activatesAttribute.writePendingTypes);
writePendingTypes = activatesAttribute.writePendingTypes;
}
var writesAttributes = GetType().GetCustomAttributes<Writes>(false);
foreach (var writesAttribute in writesAttributes)
var defaultWritePriorityAttribute = GetType().GetCustomAttribute<DefaultWritePriority>(false);
if (defaultWritePriorityAttribute != null)
{
sendTypes.UnionWith(writesAttribute.writeTypes);
defaultWritePriority = defaultWritePriorityAttribute.writePriority;
}
foreach (var writesAttribute in GetType().GetCustomAttributes<Writes>(false))
{
writeTypes.UnionWith(writesAttribute.writeTypes);
writePriorities = new Dictionary<Type, int>[2] { writePriorities, writesAttribute.priorities }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
}
@ -66,21 +74,21 @@ namespace Encompass
var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null)
{
receiveTypes.UnionWith(readsAttribute.readTypes);
readTypes = readsAttribute.readTypes;
}
var readsPendingAttribute = GetType().GetCustomAttribute<ReadsPending>(false);
if (readsPendingAttribute != null)
{
receiveTypes.UnionWith(readsPendingAttribute.readPendingTypes);
readPendingTypes = readsPendingAttribute.readPendingTypes;
}
}
public override bool Equals(object obj)
{
if (obj is Engine)
if (obj is Engine engine)
{
return this.Equals((Engine)obj);
return Equals(engine);
}
return false;
@ -111,9 +119,9 @@ namespace Encompass
this.messageManager = messageManager;
}
internal void AssignComponentMessageManager(ComponentMessageManager componentMessageManager)
internal void AssignComponentUpdateManager(ComponentUpdateManager componentUpdateManager)
{
this.componentMessageManager = componentMessageManager;
this.componentUpdateManager = componentUpdateManager;
}
internal void AssignTimeManager(TimeManager timeManager)
@ -154,37 +162,12 @@ namespace Encompass
return entityManager.GetEntity(entityID);
}
/// <summary>
/// Returns the Entity ID associated with the specified Component Type and ID.
/// </summary>
private Guid GetEntityIDByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (!pendingRead && !existingRead)
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
return componentMessageManager.GetEntityIDByComponentID(componentID);
}
/// <summary>
/// Returns the Entity associated with the specified Component Type and ID.
/// </summary>
private Entity GetEntityByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent
{
return GetEntity(GetEntityIDByComponentID<TComponent>(componentID));
}
/// <summary>
/// Returns an Entity containing the specified Component type.
/// </summary>
protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
{
var (id, component) = ReadComponentHelper<TComponent>();
return GetEntityByComponentID<TComponent>(id);
return ReadComponentHelper<TComponent>().Item1;
}
/// <summary>
@ -192,60 +175,31 @@ namespace Encompass
/// </summary>
protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
{
foreach (var (id, _) in ReadComponentsHelper<TComponent>())
{
yield return GetEntityByComponentID<TComponent>(id);
}
}
/// <summary>
/// Returns the Component struct with the specified Component Type and ID.
/// </summary>
internal TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (!pendingRead && !existingRead)
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
if (componentMessageManager.GetComponentTypeByID(componentID) != typeof(TComponent))
{
throw new ComponentTypeMismatchException("Expected Component to be of type {0} but was actually of type {1}", typeof(TComponent).Name, componentMessageManager.GetComponentTypeByID(componentID).Name);
}
return (TComponent)componentMessageManager.GetComponentByID(componentID);
return ReadComponentsHelper<TComponent>().Select(pair => pair.Item1);
}
// these next two are for the ComponentMessageEmitter only
internal IEnumerable<(Guid, TComponent)> ReadComponentsFromWorld<TComponent>() where TComponent : struct, IComponent
internal IEnumerable<TComponent> ReadComponentsFromWorld<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetComponentsByType<TComponent>();
}
internal Entity ReadEntityFromWorld(Guid componentID)
private IEnumerable<(Entity, TComponent)> ReadComponentsHelper<TComponent>() where TComponent : struct, IComponent
{
return GetEntity(componentManager.GetEntityIDByComponentID(componentID));
}
private IEnumerable<(Guid, TComponent)> ReadComponentsHelper<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadExistingAndPendingComponentsByType<TComponent>();
return componentUpdateManager.ReadExistingAndPendingComponentsByType<TComponent>();
}
else if (existingRead)
{
return componentMessageManager.ReadExistingComponentsByType<TComponent>();
return componentUpdateManager.ReadExistingComponentsByType<TComponent>();
}
else if (pendingRead)
{
return componentMessageManager.ReadPendingComponentsByType<TComponent>();
return componentUpdateManager.ReadPendingComponentsByType<TComponent>();
}
else
{
@ -266,24 +220,29 @@ namespace Encompass
/// </summary>
protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity<TComponent>() where TComponent : struct, IComponent
{
return ReadComponentsHelper<TComponent>().Select((tuple) => (tuple.Item2, GetEntityByComponentID<TComponent>(tuple.Item1)));
return ReadComponentsHelper<TComponent>().Select((tuple) => (tuple.Item2, tuple.Item1));
}
private (Guid, TComponent) ReadComponentHelper<TComponent>() where TComponent : struct, IComponent
internal IEnumerable<(TComponent, Entity)> InternalRead<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
return componentManager.GetComponentsIncludingEntity<TComponent>();
}
private (Entity, TComponent) ReadComponentHelper<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadFirstExistingOrPendingComponentByType<TComponent>();
return componentUpdateManager.ReadFirstExistingOrPendingComponentByType<TComponent>();
}
else if (existingRead)
{
return componentMessageManager.ReadFirstExistingComponentByType<TComponent>();
return componentUpdateManager.ReadFirstExistingComponentByType<TComponent>();
}
else if (pendingRead)
{
return componentMessageManager.ReadFirstPendingComponentByType<TComponent>();
return componentUpdateManager.ReadFirstPendingComponentByType<TComponent>();
}
else
{
@ -304,8 +263,8 @@ namespace Encompass
/// </summary>
protected (TComponent, Entity) ReadComponentIncludingEntity<TComponent>() where TComponent : struct, IComponent
{
var (id, component) = ReadComponentHelper<TComponent>();
return (component, GetEntityByComponentID<TComponent>(id));
var (entity, component) = ReadComponentHelper<TComponent>();
return (component, entity);
}
/// <summary>
@ -313,19 +272,19 @@ namespace Encompass
/// </summary>
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
return componentMessageManager.SomeExistingOrPendingComponent<TComponent>();
return componentUpdateManager.SomeExistingOrPendingComponent<TComponent>();
}
else if (existingRead)
{
return componentMessageManager.SomeExistingComponent<TComponent>();
return componentUpdateManager.SomeExistingComponent<TComponent>();
}
else if (pendingRead)
{
return componentMessageManager.SomePendingComponent<TComponent>();
return componentUpdateManager.SomePendingComponent<TComponent>();
}
else
{
@ -333,19 +292,19 @@ namespace Encompass
}
}
private (Guid, TComponent) GetComponentHelper<TComponent>(Entity entity) where TComponent : struct, IComponent
private TComponent GetComponentHelper<TComponent>(Entity entity) where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
if (componentMessageManager.HasPendingComponent<TComponent>(entity))
if (componentUpdateManager.HasPendingComponent<TComponent>(entity))
{
return componentMessageManager.ReadPendingComponentByEntityAndType<TComponent>(entity);
return componentUpdateManager.ReadPendingComponentByEntityAndType<TComponent>(entity);
}
else if (componentMessageManager.HasExistingComponent<TComponent>(entity))
else if (componentUpdateManager.HasExistingComponent<TComponent>(entity))
{
return componentMessageManager.ReadExistingComponentByEntityAndType<TComponent>(entity);
return componentUpdateManager.ReadExistingComponentByEntityAndType<TComponent>(entity);
}
else
{
@ -354,11 +313,11 @@ namespace Encompass
}
else if (existingRead)
{
return componentMessageManager.ReadExistingComponentByEntityAndType<TComponent>(entity);
return componentUpdateManager.ReadExistingComponentByEntityAndType<TComponent>(entity);
}
else if (pendingRead)
{
return componentMessageManager.ReadPendingComponentByEntityAndType<TComponent>(entity);
return componentUpdateManager.ReadPendingComponentByEntityAndType<TComponent>(entity);
}
else
{
@ -377,58 +336,7 @@ namespace Encompass
/// </exception>
protected TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return GetComponentHelper<TComponent>(entity).Item2;
}
private (Guid, IComponent) GetComponentHelper(Entity entity, Type type)
{
var pending = typeof(PendingComponentMessage<>).MakeGenericType(type);
var existing = typeof(ComponentMessage<>).MakeGenericType(type);
var pendingRead = receiveTypes.Contains(pending);
var existingRead = receiveTypes.Contains(existing);
if (existingRead && pendingRead)
{
if (componentMessageManager.HasPendingComponent(entity, pending))
{
return componentMessageManager.ReadPendingComponentByEntityAndType(entity, pending);
}
else if (componentMessageManager.HasExistingComponent(entity, existing))
{
return componentMessageManager.ReadExistingComponentByEntityAndType(entity, existing);
}
else
{
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", type.Name, entity.ID);
}
}
else if (existingRead)
{
return componentMessageManager.ReadExistingComponentByEntityAndType(entity, existing);
}
else if (pendingRead)
{
return componentMessageManager.ReadPendingComponentByEntityAndType(entity, pending);
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, type.Name);
}
}
/// <summary>
/// Returns a Component with the specified Type that exists on the Entity.
/// </summary>
/// <exception cref="Encompass.Exceptions.NoComponentOfTypeOnEntityException">
/// Thrown when the Entity does not have a Component of the specified Type
/// </exception>
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it reads the given Component Type.
/// </exception>
protected IComponent GetComponent(Entity entity, Type type)
{
return GetComponentHelper(entity, type).Item2;
return GetComponentHelper<TComponent>(entity);
}
/// <summary>
@ -439,20 +347,20 @@ namespace Encompass
/// </exception>
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (pendingRead && existingRead)
{
return componentMessageManager.HasExistingOrPendingComponent<TComponent>(entity);
return componentUpdateManager.HasExistingOrPendingComponent<TComponent>(entity);
}
else if (existingRead)
{
return componentMessageManager.HasExistingComponent<TComponent>(entity);
return componentUpdateManager.HasExistingComponent<TComponent>(entity);
}
else if (pendingRead)
{
return componentMessageManager.HasPendingComponent<TComponent>(entity);
return componentUpdateManager.HasPendingComponent<TComponent>(entity);
}
else
{
@ -468,23 +376,20 @@ namespace Encompass
/// </exception>
protected bool HasComponent(Entity entity, Type type)
{
var pending = typeof(PendingComponentMessage<>).MakeGenericType(type);
var existing = typeof(ComponentMessage<>).MakeGenericType(type);
var pendingRead = receiveTypes.Contains(pending);
var existingRead = receiveTypes.Contains(existing);
var pendingRead = readPendingTypes.Contains(type);
var existingRead = readTypes.Contains(type);
if (pendingRead && existingRead)
{
return componentMessageManager.HasExistingOrPendingComponent(entity, type);
return componentUpdateManager.HasExistingOrPendingComponent(entity, type);
}
else if (existingRead)
{
return componentMessageManager.HasExistingComponent(entity, type);
return componentUpdateManager.HasExistingComponent(entity, type);
}
else if (pendingRead)
{
return componentMessageManager.HasPendingComponent(entity, type);
return componentUpdateManager.HasPendingComponent(entity, type);
}
else
{
@ -500,28 +405,26 @@ namespace Encompass
/// </exception>
protected void SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0;
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : defaultWritePriority;
var componentID = componentManager.MarkComponentForWrite(entity, component, priority);
if (!sendTypes.Contains(typeof(ComponentWriteMessage<TComponent>)))
if (!writeTypes.Contains(typeof(TComponent)))
{
throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>)))
bool written;
if (writePendingTypes.Contains(typeof(TComponent)))
{
PendingComponentMessage<TComponent> newComponentMessage;
newComponentMessage.entity = entity;
newComponentMessage.componentID = componentID;
newComponentMessage.component = component;
newComponentMessage.priority = priority;
SendPendingComponentMessage(newComponentMessage);
written = AddPendingComponent(entity, component, priority);
}
else
{
written = componentUpdateManager.UpdateComponent(entity, component, priority);
}
if (component is IDrawableComponent drawableComponent)
if (written && component is IDrawableComponent drawableComponent)
{
componentManager.RegisterDrawableComponent(componentID, drawableComponent);
componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer);
}
}
@ -547,7 +450,7 @@ namespace Encompass
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
protected void SendMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
messageManager.AddMessageDelayed(message, time);
messageManager.AddMessage(message, time);
}
/// <summary>
@ -556,30 +459,17 @@ namespace Encompass
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
protected void SendMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
messageManager.AddMessageDelayedIgnoringTimeDilation(message, time);
messageManager.AddMessageIgnoringTimeDilation(message, time);
}
// unparameterized version to enable dynamic dispatch
protected void SendMessage(IMessage message)
internal void AddExistingComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
var type = message.GetType();
if (!sendTypes.Contains(type) || !type.IsValueType)
{
throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, type.Name);
}
messageManager.AddMessage(message);
componentUpdateManager.AddExistingComponent(entity, component);
}
internal void SendExistingComponentMessage<TComponent>(ComponentMessage<TComponent> message) where TComponent : struct, IComponent
internal bool AddPendingComponent<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
componentMessageManager.AddExistingComponentMessage(message);
}
internal void SendPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> message) where TComponent : struct, IComponent
{
componentMessageManager.AddPendingComponentMessage(message);
return componentUpdateManager.AddPendingComponent(entity, component, priority);
}
/// <summary>
@ -606,7 +496,7 @@ namespace Encompass
/// </exception>
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
{
return ReadMessages<TMessage>().First();
return messageManager.First<TMessage>();
}
/// <summary>
@ -622,16 +512,7 @@ namespace Encompass
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
}
return ReadMessages<TMessage>().Any();
}
/// <summary>
/// Destroys the Entity with the specified ID. This also removes all of the Components associated with the Entity.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
internal void Destroy(Guid entityID)
{
entityManager.MarkForDestroy(entityID);
return messageManager.Any<TMessage>();
}
/// <summary>
@ -640,7 +521,7 @@ namespace Encompass
/// </summary>
protected void Destroy(Entity entity)
{
entityManager.MarkForDestroy(entity.ID);
entityManager.MarkForDestroy(entity);
}
/// <summary>
@ -673,21 +554,10 @@ namespace Encompass
{
if (!HasComponent<TComponent>(entity)) { return false; }
var (componentID, component) = GetComponentHelper<TComponent>(entity);
RemoveComponent(componentID);
componentManager.Remove<TComponent>(entity);
return true;
}
/// <summary>
/// Removes the Component with the specified ID from its Entity.
/// </summary>
private void RemoveComponent(Guid componentID)
{
componentManager.MarkForRemoval(componentID);
}
/// <summary>
/// Activates the Encompass time dilation system.
/// Engines that have the IgnoresTimeDilation property will ignore all time dilation.

View File

@ -0,0 +1,18 @@
namespace Encompass
{
internal class ComponentEmitter<TComponent> : Engine where TComponent : struct, IComponent
{
public ComponentEmitter()
{
sendTypes.Add(typeof(TComponent));
}
public override void Update(double dt)
{
foreach (var (component, entity) in InternalRead<TComponent>())
{
AddExistingComponent(entity, component);
}
}
}
}

View File

@ -1,23 +0,0 @@
namespace Encompass
{
internal class ComponentMessageEmitter<TComponent> : Engine where TComponent : struct, IComponent
{
public ComponentMessageEmitter() : base()
{
sendTypes.Add(typeof(ComponentMessage<TComponent>));
}
public override void Update(double dt)
{
foreach (var (componentID, component) in ReadComponentsFromWorld<TComponent>())
{
ComponentMessage<TComponent> componentMessage;
componentMessage.entity = ReadEntityFromWorld(componentID);
componentMessage.componentID = componentID;
componentMessage.component = component;
SendMessage(componentMessage);
SendExistingComponentMessage(componentMessage);
}
}
}
}

View File

@ -12,14 +12,14 @@ namespace Encompass
internal Entity(Guid id)
{
this.ID = id;
ID = id;
}
public override bool Equals(object obj)
{
if (obj is Entity)
if (obj is Entity entity)
{
return this.Equals((Entity)obj);
return Equals(entity);
}
return false;

View File

@ -7,17 +7,15 @@ namespace Encompass
{
internal class EntityManager
{
private readonly Dictionary<Guid, Entity> IDToEntity = new Dictionary<Guid, Entity>();
private readonly Dictionary<Guid, Entity> IDToEntity = new Dictionary<Guid, Entity>(1024);
private readonly HashSet<Guid> entitiesMarkedForDestroy = new HashSet<Guid>();
private readonly HashSet<Entity> entitiesMarkedForDestroy = new HashSet<Entity>();
private readonly ComponentManager componentManager;
private readonly ComponentMessageManager componentMessageManager;
public EntityManager(ComponentManager componentManager, ComponentMessageManager componentMessageManager)
public EntityManager(ComponentManager componentManager)
{
this.componentManager = componentManager;
this.componentMessageManager = componentMessageManager;
}
public Entity CreateEntity()
@ -25,8 +23,6 @@ namespace Encompass
var id = NextID();
var entity = new Entity(id);
IDToEntity[id] = entity;
componentManager.RegisterEntity(id);
componentMessageManager.RegisterEntity(entity);
return entity;
}
@ -47,19 +43,17 @@ namespace Encompass
}
}
public void MarkForDestroy(Guid entityID)
public void MarkForDestroy(Entity entity)
{
entitiesMarkedForDestroy.Add(entityID);
entitiesMarkedForDestroy.Add(entity);
}
public void DestroyMarkedEntities()
{
foreach (var entityID in entitiesMarkedForDestroy)
foreach (var entity in entitiesMarkedForDestroy)
{
componentMessageManager.RegisterDestroyedEntity(GetEntity(entityID));
componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
IDToEntity.Remove(entityID);
componentManager.RegisterDestroyedEntity(entityID);
componentManager.MarkAllComponentsOnEntityForRemoval(entity);
IDToEntity.Remove(entity.ID);
}
entitiesMarkedForDestroy.Clear();

View File

@ -1,102 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal class MessageManager
{
private TimeManager timeManager;
private readonly Dictionary<Type, List<IMessage>> messageTypeToMessages = new Dictionary<Type, List<IMessage>>();
private readonly List<(IMessage, double)> delayedMessages = new List<(IMessage, double)>();
private readonly List<(IMessage, double)> delayedMessagesIgnoringTimeDilation = new List<(IMessage, double)>();
private readonly TimeManager timeManager;
private readonly MessageStore messageStore = new MessageStore();
public MessageManager(TimeManager timeManager)
{
this.timeManager = timeManager;
}
internal void RegisterMessageType(Type messageType)
internal void AddMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
{
if (!messageTypeToMessages.ContainsKey(messageType))
{
messageTypeToMessages.Add(messageType, new List<IMessage>());
}
messageStore.AddMessage<TMessage>(message);
}
internal void AddMessage(IMessage message)
internal void AddMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
var type = message.GetType();
messageTypeToMessages[type].Add(message);
messageStore.AddMessage(message, time);
}
internal void AddMessageDelayed(IMessage message, double time)
internal void AddMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
if (!messageTypeToMessages.ContainsKey(message.GetType())) { messageTypeToMessages.Add(message.GetType(), new List<IMessage>()); }
delayedMessages.Add((message, time));
}
internal void AddMessageDelayedIgnoringTimeDilation(IMessage message, double time)
{
if (!messageTypeToMessages.ContainsKey(message.GetType())) { messageTypeToMessages.Add(message.GetType(), new List<IMessage>()); }
delayedMessagesIgnoringTimeDilation.Add((message, time));
messageStore.AddMessageIgnoringTimeDilation(message, time);
}
internal void ClearMessages()
{
foreach (var entry in messageTypeToMessages)
{
entry.Value.Clear();
}
messageStore.ClearAll();
}
internal void ProcessDelayedMessages(double dt)
{
for (int i = delayedMessages.Count - 1; i >= 0; i--)
{
var (message, time) = delayedMessages[i];
var updatedTime = time - (dt * timeManager.TimeDilationFactor);
if (updatedTime <= 0)
{
AddMessage(message);
delayedMessages.RemoveAt(i);
}
else
{
delayedMessages[i] = (message, updatedTime);
}
}
for (int i = delayedMessagesIgnoringTimeDilation.Count - 1; i >= 0; i--)
{
var (message, time) = delayedMessagesIgnoringTimeDilation[i];
var updatedTime = time - dt;
if (updatedTime <= 0)
{
AddMessage(message);
delayedMessagesIgnoringTimeDilation.RemoveAt(i);
}
else
{
delayedMessagesIgnoringTimeDilation[i] = (message, updatedTime);
}
}
messageStore.ProcessDelayedMessages(dt * timeManager.TimeDilationFactor, dt);
}
internal IEnumerable<TMessage> GetMessagesByType<TMessage>() where TMessage : struct, IMessage
{
return messageTypeToMessages.ContainsKey(typeof(TMessage)) ?
messageTypeToMessages[typeof(TMessage)].Cast<TMessage>() :
Enumerable.Empty<TMessage>();
return messageStore.All<TMessage>();
}
internal bool Any<TMessage>() where TMessage : struct, IMessage
{
return messageStore.Any<TMessage>();
}
internal TMessage First<TMessage>() where TMessage : struct, IMessage
{
return messageStore.First<TMessage>();
}
}
}

View File

@ -1,11 +0,0 @@
using System;
namespace Encompass
{
internal struct ComponentMessage<TComponent> : IMessage where TComponent : struct, IComponent
{
public Entity entity;
public Guid componentID;
public TComponent component;
}
}

View File

@ -1,10 +0,0 @@
using System;
namespace Encompass
{
internal struct ComponentWriteMessage<TComponent> : IMessage where TComponent : struct, IComponent
{
public Guid componentID;
public TComponent component;
}
}

View File

@ -1,12 +0,0 @@
using System;
namespace Encompass
{
internal struct PendingComponentMessage<TComponent> : IMessage where TComponent : struct, IComponent
{
public Entity entity;
public Guid componentID;
public TComponent component;
public int priority;
}
}

View File

@ -1,26 +1,17 @@
using System;
using System.Reflection;
using System.Collections.Generic;
namespace Encompass
{
internal class RenderManager
{
private readonly ComponentManager componentManager;
private readonly DrawLayerManager drawLayerManager;
private readonly EntityManager entityManager;
private readonly Dictionary<Type, Action<Entity, IComponent>> drawComponentTypeToOrderedRenderer = new Dictionary<Type, Action<Entity, IComponent>>();
private readonly Dictionary<Type, Action<Entity, IComponent>> drawComponentTypeToOrderedRenderer = new Dictionary<Type, Action<Entity, IComponent>>(256);
public RenderManager(
ComponentManager componentManager,
DrawLayerManager drawLayerManager,
EntityManager entityManager
)
public RenderManager(DrawLayerManager drawLayerManager)
{
this.componentManager = componentManager;
this.drawLayerManager = drawLayerManager;
this.entityManager = entityManager;
}
public void RegisterOrderedRenderer<TComponent>(Action<Entity, IComponent> renderAction) where TComponent : struct, IComponent
@ -37,16 +28,10 @@ namespace Encompass
{
foreach (var layer in drawLayerManager.LayerOrder)
{
var componentIDSet = drawLayerManager.ComponentIDsByLayer(layer);
var generalRendererSet = drawLayerManager.GeneralRenderersByLayer(layer);
foreach (var componentID in componentIDSet)
foreach (var (entity, componentType, component) in drawLayerManager.AllInLayer(layer))
{
var component = componentManager.GetComponentByID(componentID);
var componentType = componentManager.GetComponentTypeByID(componentID);
var entityID = componentManager.GetEntityIDByComponentID(componentID);
var entity = entityManager.GetEntity(entityID);
if (drawComponentTypeToOrderedRenderer.ContainsKey(componentType))
{
var internalRenderAction = drawComponentTypeToOrderedRenderer[componentType];

View File

@ -19,11 +19,6 @@ namespace Encompass
this.componentManager = componentManager;
}
internal Guid GetEntityIDByComponentID(Guid componentID)
{
return componentManager.GetEntityIDByComponentID(componentID);
}
internal Entity GetEntity(Guid entityID)
{
return entityManager.GetEntity(entityID);
@ -41,12 +36,12 @@ namespace Encompass
protected IEnumerable<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetComponentsByType<TComponent>().Select(tuple => tuple.Item2);
return componentManager.GetComponentsByType<TComponent>();
}
protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetComponentsByType<TComponent>().Select(tuple => (tuple.Item2, GetEntity(GetEntityIDByComponentID(tuple.Item1))));
return componentManager.GetComponentsIncludingEntity<TComponent>();
}
protected TComponent ReadComponent<TComponent>() where TComponent : struct, IComponent
@ -61,7 +56,7 @@ namespace Encompass
protected TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return componentManager.GetComponentByEntityAndType<TComponent>(entity).Item2;
return componentManager.GetComponentByEntityAndType<TComponent>(entity);
}
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent

View File

@ -14,23 +14,21 @@ namespace Encompass
{
get
{
double calculatedFactor = 1;
if (elapsedTime < easeInTime)
{
calculatedFactor = easeInFunction(elapsedTime, 1, factor - 1, easeInTime);
return easeInFunction(elapsedTime, 1, factor - 1, easeInTime);
}
else if (elapsedTime < easeInTime + activeTime)
{
calculatedFactor = factor;
return factor;
}
else if (elapsedTime < easeInTime + activeTime + easeOutTime)
{
var elapsedOutTime = elapsedTime - easeInTime - activeTime;
calculatedFactor = easeOutFunction(elapsedOutTime, factor, 1 - factor, easeOutTime);
return easeOutFunction(elapsedOutTime, factor, 1 - factor, easeOutTime);
}
return calculatedFactor;
return 1;
}
}
}

View File

@ -5,11 +5,11 @@ namespace Encompass
{
internal class TimeManager
{
private List<TimeDilationData> timeDilationDatas = new List<TimeDilationData>();
private readonly List<TimeDilationData> timeDilationDatas = new List<TimeDilationData>(32);
private double Linear(double t, double b, double c, double d)
{
return c * t / d + b;
return (c * t / d) + b;
}
public double TimeDilationFactor

View File

@ -11,7 +11,7 @@ namespace Encompass
private readonly EntityManager entityManager;
private readonly ComponentManager componentManager;
private readonly MessageManager messageManager;
private readonly ComponentMessageManager componentMessageManager;
private readonly ComponentUpdateManager componentUpdateManager;
private readonly TimeManager timeManager;
private readonly RenderManager renderManager;
@ -20,7 +20,7 @@ namespace Encompass
EntityManager entityManager,
ComponentManager componentManager,
MessageManager messageManager,
ComponentMessageManager componentMessageManager,
ComponentUpdateManager componentUpdateManager,
TimeManager timeManager,
RenderManager renderManager
)
@ -29,7 +29,7 @@ namespace Encompass
this.entityManager = entityManager;
this.componentManager = componentManager;
this.messageManager = messageManager;
this.componentMessageManager = componentMessageManager;
this.componentUpdateManager = componentUpdateManager;
this.timeManager = timeManager;
this.renderManager = renderManager;
}
@ -56,11 +56,12 @@ namespace Encompass
}
messageManager.ClearMessages();
componentMessageManager.ClearMessages();
entityManager.DestroyMarkedEntities();
componentManager.RemoveMarkedComponents();
componentManager.WriteComponents();
componentManager.RemoveMarkedComponents();
componentUpdateManager.Clear();
}
/// <summary>

View File

@ -20,11 +20,13 @@ namespace Encompass
{
private readonly List<Engine> engines = new List<Engine>();
private readonly DirectedGraph<Engine, Unit> engineGraph = GraphBuilder.DirectedGraph<Engine>();
private readonly ComponentStore startingComponentStoreForComponentManager = new ComponentStore();
private readonly ComponentStore startingComponentStoreForComponentUpdateManager = new ComponentStore();
private readonly ComponentManager componentManager;
private readonly EntityManager entityManager;
private readonly MessageManager messageManager;
private readonly ComponentMessageManager componentMessageManager;
private readonly ComponentUpdateManager componentUpdateManager;
private readonly TimeManager timeManager;
private readonly DrawLayerManager drawLayerManager;
private readonly RenderManager renderManager;
@ -39,11 +41,11 @@ namespace Encompass
{
drawLayerManager = new DrawLayerManager();
timeManager = new TimeManager();
componentManager = new ComponentManager(drawLayerManager);
componentUpdateManager = new ComponentUpdateManager();
componentManager = new ComponentManager(drawLayerManager, componentUpdateManager);
messageManager = new MessageManager(timeManager);
componentMessageManager = new ComponentMessageManager();
entityManager = new EntityManager(componentManager, componentMessageManager);
renderManager = new RenderManager(componentManager, drawLayerManager, entityManager);
entityManager = new EntityManager(componentManager);
renderManager = new RenderManager(drawLayerManager);
}
/// <summary>
@ -67,25 +69,28 @@ namespace Encompass
/// </summary>
public void SendMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
messageManager.AddMessageDelayed(message, time);
messageManager.AddMessage<TMessage>(message, time);
}
/// <summary>
/// Sets Component data for the specified Component Type on the specified Entity.
/// </summary>
public void SetComponent<TComponent>(Entity entity, TComponent component, int priority = 0) where TComponent : struct, IComponent
public void SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
var componentID = componentManager.MarkComponentForWrite(entity, component, priority);
startingComponentStoreForComponentManager.Set(entity, component);
startingComponentStoreForComponentUpdateManager.Set(entity, component);
RegisterComponent(typeof(TComponent));
if (component is IDrawableComponent drawableComponent)
{
componentManager.RegisterDrawableComponent(componentID, drawableComponent);
componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer);
}
}
internal void RegisterComponent(Type componentType)
{
registeredComponentTypes.Add(componentType);
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
}
/// <summary>
@ -97,7 +102,7 @@ namespace Encompass
engine.AssignEntityManager(entityManager);
engine.AssignComponentManager(componentManager);
engine.AssignMessageManager(messageManager);
engine.AssignComponentMessageManager(componentMessageManager);
engine.AssignComponentUpdateManager(componentUpdateManager);
engine.AssignTimeManager(timeManager);
engines.Add(engine);
@ -106,42 +111,28 @@ namespace Encompass
var messageReceiveTypes = engine.receiveTypes;
var messageSendTypes = engine.sendTypes;
foreach (var messageType in messageReceiveTypes.Union(messageSendTypes))
foreach (var writePendingType in engine.writePendingTypes.Intersect(engine.readPendingTypes))
{
messageManager.RegisterMessageType(messageType);
throw new EngineSelfCycleException("Engine {0} both writes and reads pending Component {1}", engine.GetType().Name, writePendingType.Name);
}
foreach (var messageType in messageReceiveTypes.Intersect(messageSendTypes))
{
if ((messageType.IsGenericType && messageType.GetGenericTypeDefinition() == typeof(PendingComponentMessage<>)))
{
var componentType = messageType.GetGenericArguments().Single();
throw new EngineSelfCycleException("Engine {0} both activates and reads pending Component {1}", engine.GetType().Name, componentType.Name);
}
throw new EngineSelfCycleException("Engine {0} both receives and sends Message {1}", engine.GetType().Name, messageType.Name);
}
if (messageSendTypes.Any())
if (messageSendTypes.Count > 0 || engine.writePendingTypes.Count > 0)
{
senders.Add(engine);
}
foreach (var receiveType in engine.receiveTypes)
foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readPendingTypes))
{
if (receiveType.IsGenericType)
{
var genericTypeDefinition = receiveType.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(PendingComponentMessage<>))
{
var componentType = receiveType.GetGenericArguments().Single();
if (!registeredComponentTypes.Contains(componentType))
{
RegisterComponent(componentType);
}
}
}
RegisterComponent(componentType);
}
foreach (var receiveType in engine.receiveTypes.Union(engine.readPendingTypes))
{
if (!typeToReaders.ContainsKey(receiveType))
{
typeToReaders.Add(receiveType, new HashSet<Engine>());
@ -184,7 +175,7 @@ namespace Encompass
{
foreach (var senderEngine in senders)
{
foreach (var messageType in senderEngine.sendTypes)
foreach (var messageType in senderEngine.sendTypes.Union(senderEngine.writePendingTypes))
{
if (typeToReaders.ContainsKey(messageType))
{
@ -243,16 +234,12 @@ namespace Encompass
var defaultWritePriorityAttribute = engine.GetType().GetCustomAttribute<DefaultWritePriority>(false);
var writeTypes = engine.sendTypes.Where((type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ComponentWriteMessage<>));
foreach (var writeType in writeTypes)
foreach (var writeType in engine.writeTypes)
{
var componentType = writeType.GetGenericArguments()[0];
int? priority = null;
if (engine.writePriorities.ContainsKey(componentType))
if (engine.writePriorities.ContainsKey(writeType))
{
priority = engine.writePriorities[componentType];
priority = engine.writePriorities[writeType];
}
else if (defaultWritePriorityAttribute != null)
{
@ -261,44 +248,44 @@ namespace Encompass
if (priority.HasValue)
{
writtenComponentTypesWithPriority.Add(componentType);
writtenComponentTypesWithPriority.Add(writeType);
if (!writePriorities.ContainsKey(componentType))
if (!writePriorities.ContainsKey(writeType))
{
writePriorities[componentType] = new HashSet<int>();
writePriorities[writeType] = new HashSet<int>();
}
if (writePriorities[componentType].Contains(priority.Value))
if (writePriorities[writeType].Contains(priority.Value))
{
duplicateWritesWithSamePriority.Add(componentType);
duplicateWritesWithSamePriority.Add(writeType);
}
else if (writtenComponentTypesWithoutPriority.Contains(componentType))
else if (writtenComponentTypesWithoutPriority.Contains(writeType))
{
duplicateWritesWithoutPriority.Add(componentType);
duplicateWritesWithoutPriority.Add(writeType);
}
else
{
writePriorities[componentType].Add(priority.Value);
writePriorities[writeType].Add(priority.Value);
}
}
else
{
if (writtenComponentTypesWithoutPriority.Contains(componentType) || writtenComponentTypesWithPriority.Contains(componentType))
if (writtenComponentTypesWithoutPriority.Contains(writeType) || writtenComponentTypesWithPriority.Contains(writeType))
{
duplicateWritesWithoutPriority.Add(componentType);
duplicateWritesWithoutPriority.Add(writeType);
}
else
{
writtenComponentTypesWithoutPriority.Add(componentType);
writtenComponentTypesWithoutPriority.Add(writeType);
}
}
if (!writeMessageToEngines.ContainsKey(componentType))
if (!writeMessageToEngines.ContainsKey(writeType))
{
writeMessageToEngines[componentType] = new List<Engine>();
writeMessageToEngines[writeType] = new List<Engine>();
}
writeMessageToEngines[componentType].Add(engine);
writeMessageToEngines[writeType].Add(engine);
}
}
@ -331,6 +318,14 @@ namespace Encompass
}
var engineOrder = new List<Engine>();
foreach (var registeredComponentType in registeredComponentTypes)
{
var emitterEngine = (Engine)Activator.CreateInstance(typeof(ComponentEmitter<>).MakeGenericType(registeredComponentType));
AddEngine(emitterEngine);
engineOrder.Add(emitterEngine);
}
foreach (var engine in engineGraph.TopologicalSort())
{
engineOrder.Add(engine);
@ -341,13 +336,13 @@ namespace Encompass
entityManager,
componentManager,
messageManager,
componentMessageManager,
componentUpdateManager,
timeManager,
renderManager
);
componentManager.RemoveMarkedComponents();
componentManager.WriteComponents();
componentUpdateManager.SetStartingComponentStore(startingComponentStoreForComponentUpdateManager);
componentManager.SetComponentStore(startingComponentStoreForComponentManager);
return world;
}

View File

@ -3,13 +3,13 @@
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Encompass</RootNamespace>
<PackageId>EncompassECS.Framework</PackageId>
<Version>0.17.0</Version>
<Version>0.18.0-preview8</Version>
<Authors>Evan Hemsley</Authors>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Company>Moonside Games</Company>
<Product>Encompass ECS</Product>
<PackageProjectUrl>https://github.com/encompass-ecs</PackageProjectUrl>
<PackageLicenseUrl/>
<PackageLicenseUrl />
<Copyright>Evan Hemsley 2019</Copyright>
<Description>Encompass is an engine-agnostic Hyper ECS framework to help you code games, or other kinds of simulations.</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@ -19,11 +19,10 @@
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath/>
<PackagePath />
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Collections.Pooled" Version="1.0.82"/>
<PackageReference Include="MoonTools.Core.Graph" Version="1.0.0"/>
<PackageReference Include="MoonTools.Core.Graph" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,12 +1,7 @@
using System.ComponentModel;
using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System.Collections.Generic;
using System;
using System.Linq;
using Encompass.Exceptions;
namespace Tests
{
@ -89,16 +84,16 @@ namespace Tests
worldBuilder.AddEngine(new ReadMockComponentEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { myInt = 20, myString = "what" }, 2);
worldBuilder.SetComponent(entity, new MockComponent { myInt = 50, myString = "hi" }, 0);
worldBuilder.SetComponent(entity, new MockComponent { myInt = 40, myString = "wassup" }, 1);
worldBuilder.SetComponent(entity, new MockComponent { myInt = 20, myString = "what" });
worldBuilder.SetComponent(entity, new MockComponent { myInt = 50, myString = "hi" });
worldBuilder.SetComponent(entity, new MockComponent { myInt = 40, myString = "wassup" });
var world = worldBuilder.Build();
world.Update(0.01);
Assert.That(gottenMockComponent.myInt, Is.EqualTo(50));
Assert.That(gottenMockComponent.myString, Is.EqualTo("hi"));
Assert.That(gottenMockComponent.myInt, Is.EqualTo(40));
Assert.That(gottenMockComponent.myString, Is.EqualTo("wassup"));
}
[Reads(typeof(MockComponent))]
@ -267,45 +262,6 @@ namespace Tests
Assert.AreEqual(mockComponent, gottenMockComponent);
}
[Receives(typeof(EntityMessage))]
[Reads(typeof(MockComponent))]
class GetMockComponentByRuntimeType : Engine
{
public override void Update(double dt)
{
foreach (var entityMessage in ReadMessages<EntityMessage>())
{
gottenMockComponent = (MockComponent)GetComponent(entityMessage.entity, typeof(MockComponent));
}
}
}
[Test]
public void GetComponentByRuntimeType()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new GetMockComponentEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.SetComponent<MockComponent>(entity, mockComponent);
EntityMessage entityMessage;
entityMessage.entity = entity;
worldBuilder.SendMessage(entityMessage);
var world = worldBuilder.Build();
world.Update(0.01);
Assert.AreEqual(mockComponent, gottenMockComponent);
}
struct HasComponentTestMessage : IMessage
{
public Entity entity;
@ -347,6 +303,8 @@ namespace Tests
world.Update(0.01);
}
static bool hasComponentRuntimeTypeResult;
[Receives(typeof(HasComponentTestMessage))]
[Reads(typeof(MockComponent))]
class HasComponentWithRuntimeTypeEngine : Engine
@ -355,7 +313,7 @@ namespace Tests
{
foreach (var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
{
Assert.IsTrue(HasComponent(hasComponentTestEngine.entity, typeof(MockComponent)));
hasComponentRuntimeTypeResult = HasComponent(hasComponentTestEngine.entity, typeof(MockComponent));
}
}
}
@ -381,6 +339,27 @@ namespace Tests
var world = worldBuilder.Build();
world.Update(0.01);
Assert.IsTrue(hasComponentRuntimeTypeResult);
}
[Test]
public void HasComponentWithRuntimeTypeFalseWhenNoneHaveBeenCreated()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new HasComponentWithRuntimeTypeEngine());
var entity = worldBuilder.CreateEntity();
HasComponentTestMessage hasComponentTestMessage;
hasComponentTestMessage.entity = entity;
worldBuilder.SendMessage(hasComponentTestMessage);
var world = worldBuilder.Build();
world.Update(0.01);
Assert.IsFalse(hasComponentRuntimeTypeResult);
}
struct RemoveComponentTestMessage : IMessage
@ -431,6 +410,8 @@ namespace Tests
}
}
static bool hasComponentResult;
[Receives(typeof(CheckHasMockComponentMessage))]
[Reads(typeof(MockComponent))]
class CheckHasMockComponentEngine : Engine
@ -439,13 +420,13 @@ namespace Tests
{
foreach (var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
{
Assert.IsTrue(HasComponent<MockComponent>(checkHasMockComponentMessage.entity));
hasComponentResult = HasComponent<MockComponent>(checkHasMockComponentMessage.entity);
}
}
}
[Test]
public void RemoveComponent()
public void RemovedComponentShouldStillExistOnSameFrame()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
@ -467,6 +448,12 @@ namespace Tests
var world = worldBuilder.Build();
world.Update(0.01f);
hasComponentResult.Should().BeTrue();
world.Update(0.01f);
hasComponentResult.Should().BeFalse();
}
struct CheckHasMockComponentMessage : IMessage

View File

@ -557,7 +557,7 @@ namespace Tests
world.Update(0.01);
Assert.That(results, Does.Not.Contain((mockComponent, entity)));
Assert.That(results, Does.Not.Contain((mockComponent, entity)));
Assert.That(results, Does.Not.Contain((mockComponent, entityB)));
Assert.That(results, Does.Contain((mockComponent, entityC)));
}

View File

@ -35,6 +35,9 @@ namespace Tests
world.Update(0.01f);
world.Draw();
world.Update(0.01);
world.Draw();
Assert.That(result, Is.EqualTo((aComponent, entity)));
}
@ -55,7 +58,9 @@ namespace Tests
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
world.Update(0.01f);
world.Draw();
Assert.That(result, Is.EqualTo((aComponent, entity)).Or.EqualTo((aComponentTwo, entityB)));

View File

@ -223,14 +223,14 @@ namespace Tests
[Receives(typeof(SetMessage))]
[Writes(typeof(AComponent))]
[WritesPending(typeof(AComponent))]
[Encompass.DefaultWritePriority(1)]
[Encompass.DefaultWritePriority(4)]
class AEngine : Engine
{
public override void Update(double dt)
{
foreach (var setMessage in ReadMessages<SetMessage>())
{
SetComponent(setMessage.entity, new AComponent { myInt = 0 });
SetComponent(setMessage.entity, new AComponent { myInt = 5 });
}
}
}
@ -250,6 +250,20 @@ namespace Tests
}
}
[Receives(typeof(SetMessage))]
[Writes(typeof(AComponent), 2)]
[WritesPending(typeof(AComponent))]
class CEngine : Engine
{
public override void Update(double dt)
{
foreach (var setMessage in ReadMessages<SetMessage>())
{
SetComponent(setMessage.entity, new AComponent { myInt = 3 });
}
}
}
static AComponent resultComponent;
[ReadsPending(typeof(AComponent))]
@ -267,6 +281,8 @@ namespace Tests
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AEngine());
worldBuilder.AddEngine(new BEngine());
worldBuilder.AddEngine(new CEngine());
worldBuilder.AddEngine(new ReadComponentEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SendMessage(new SetMessage { entity = entity });
@ -275,7 +291,7 @@ namespace Tests
world.Update(0.01);
Assert.That(resultComponent.myInt, Is.EqualTo(0));
Assert.That(resultComponent.myInt, Is.EqualTo(3));
}
}