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); 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); 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); 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); throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
} }
this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType)); writeTypes.Add(writeType);
this.priorities.Add(writeType, priority); priorities.Add(writeType, priority);
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Encompass
throw new IllegalWritePendingTypeException("{0} must be a Component", writePendingType.Name); 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.Collections.Generic;
using System.Linq; using System.Linq;
using Encompass.Exceptions;
using Collections.Pooled;
namespace Encompass namespace Encompass
{ {
internal class ComponentManager internal class ComponentManager
{ {
private readonly DrawLayerManager drawLayerManager; private readonly DrawLayerManager drawLayerManager;
private readonly ComponentUpdateManager componentUpdateManager;
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>(); private readonly ComponentStore componentStore = new ComponentStore();
private readonly Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>(); private readonly HashSet<Entity> entitiesMarkedForRemoval = new HashSet<Entity>();
private readonly Dictionary<Guid, PooledSet<Guid>> entityIDToComponentIDs = new Dictionary<Guid, PooledSet<Guid>>();
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
private readonly Dictionary<Guid, PooledDictionary<Type, Guid>> entityIDToComponentTypeToComponentID = new Dictionary<Guid, PooledDictionary<Type, Guid>>(); public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager)
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)
{ {
this.drawLayerManager = drawLayerManager; this.drawLayerManager = drawLayerManager;
this.componentUpdateManager = componentUpdateManager;
} }
internal void RegisterEntity(Guid entityID) internal void SetComponentStore(ComponentStore componentStore)
{ {
entityIDToComponentIDs.Add(entityID, new PooledSet<Guid>()); this.componentStore.SwapWith(componentStore);
entityIDToComponentTypeToComponentID.Add(entityID, new PooledDictionary<Type, Guid>());
} }
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; componentStore.Set(entity, component);
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;
} }
internal void WriteComponents() internal void WriteComponents()
{ {
foreach (var keyValuePair in componentWriteData) componentStore.SwapWith(componentUpdateManager.UpToDateComponentStore);
{
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();
} }
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 componentStore.All<TComponent>().Select(pair => (pair.Item2, pair.Item1));
{
return idSet;
}
return Enumerable.Empty<Guid>();
} }
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 componentStore.All<TComponent>().Select(pair => pair.Item2);
{
return idSet.Select(id => (id, (TComponent)IDToComponent[id]));
}
return Enumerable.Empty<(Guid, TComponent)>();
} }
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 componentStore.Get<TComponent>(entity);
{
return (id, (TComponent)IDToComponent[id]);
}
throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID);
} }
internal bool EntityHasComponentOfType<TComponent>(Entity entity) where TComponent : struct, IComponent 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 internal bool ComponentOfTypeExists<TComponent>() where TComponent : struct, IComponent
{ {
if (typeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet)) return componentStore.Any<TComponent>();
{
return idSet.Count > 0;
}
return false;
} }
internal IComponent GetComponentByID(Guid componentID) internal void MarkAllComponentsOnEntityForRemoval(Entity entity)
{ {
if (IDToComponent.ContainsKey(componentID)) entitiesMarkedForRemoval.Add(entity);
{
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);
} }
internal void RemoveMarkedComponents() internal void RemoveMarkedComponents()
{ {
foreach (var componentID in componentsMarkedForRemoval) foreach (var entity in entitiesMarkedForRemoval)
{ {
if (componentIDsMarkedForWrite.Contains(componentID)) componentStore.Remove(entity);
{ drawLayerManager.UnRegisterEntityWithLayer(entity);
componentIDsMarkedForWrite.Remove(componentID);
}
if (IDToComponent.ContainsKey(componentID))
{
Remove(componentID);
}
} }
componentsMarkedForRemoval.Clear(); entitiesMarkedForRemoval.Clear();
} }
private void Remove(Guid componentID) public void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
var type = componentIDToType[componentID]; componentUpdateManager.Remove<TComponent>(entity);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity);
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);
} }
} }
} }

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 SortedList<int, int> layerOrder = new SortedList<int, int>();
private readonly Dictionary<int, HashSet<Guid>> layerIndexToComponentIDs = new Dictionary<int, HashSet<Guid>>(); private readonly Dictionary<int, ComponentStore> layerIndexToComponentStore = new Dictionary<int, ComponentStore>(512);
private readonly Dictionary<int, HashSet<GeneralRenderer>> layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>(); 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; } } public IEnumerable<int> LayerOrder { get { return layerOrder.Values; } }
@ -49,23 +49,28 @@ namespace Encompass
RegisterGeneralRendererWithLayer(renderer, newLayer); 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 (!typeToEntityToLayer.ContainsKey(typeof(TComponent)))
if (layerIndexToComponentIDs.ContainsKey(layer))
{ {
var set = layerIndexToComponentIDs[layer]; typeToEntityToLayer.Add(typeof(TComponent), new Dictionary<Entity, int>());
set.Add(id); }
if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) { UnRegisterComponentWithLayer<TComponent>(entity); }
if (layerIndexToComponentStore.ContainsKey(layer))
{
var set = layerIndexToComponentStore[layer];
set.Set<TComponent>(entity, component);
} }
else else
{ {
var set = new HashSet<Guid>(); var set = new ComponentStore();
layerIndexToComponentIDs.Add(layer, set); layerIndexToComponentStore.Add(layer, set);
set.Add(id); set.Set<TComponent>(entity, component);
} }
componentIDToLayerIndex[id] = layer; typeToEntityToLayer[typeof(TComponent)].Add(entity, layer);
if (!layerOrder.ContainsKey(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]; var layer = typeToEntityToLayer[typeof(TComponent)][entity];
layerIndexToComponentIDs[layer].Remove(id); 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) ? foreach (var store in layerIndexToComponentStore.Values)
layerIndexToComponentIDs[layer] : {
Enumerable.Empty<Guid>(); store.Remove(entity);
}
} }
public IEnumerable<GeneralRenderer> GeneralRenderersByLayer(int layer) public IEnumerable<GeneralRenderer> GeneralRenderersByLayer(int layer)
@ -96,5 +104,12 @@ namespace Encompass
layerIndexToGeneralRenderers[layer] : layerIndexToGeneralRenderers[layer] :
Enumerable.Empty<GeneralRenderer>(); 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 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> sendTypes = new HashSet<Type>();
internal readonly HashSet<Type> receiveTypes = 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 Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
internal readonly int defaultWritePriority = 0;
/// <summary> /// <summary>
/// If false, the Engine will ignore time dilation. /// If false, the Engine will ignore time dilation.
/// </summary> /// </summary>
internal bool usesTimeDilation = true; internal bool usesTimeDilation = true;
public bool TimeDilationActive { get => usesTimeDilation && timeManager.TimeDilationActive; } public bool TimeDilationActive { get => usesTimeDilation && timeManager.TimeDilationActive; }
/// <summary>
/// Used when activating time dilation. Lower priority overrides higher priority.
/// </summary>
private EntityManager entityManager; private EntityManager entityManager;
private MessageManager messageManager; private MessageManager messageManager;
private ComponentManager componentManager; private ComponentManager componentManager;
private ComponentMessageManager componentMessageManager; private ComponentUpdateManager componentUpdateManager;
private TimeManager timeManager; private TimeManager timeManager;
protected Engine() protected Engine()
@ -47,13 +49,19 @@ namespace Encompass
var activatesAttribute = GetType().GetCustomAttribute<WritesPending>(false); var activatesAttribute = GetType().GetCustomAttribute<WritesPending>(false);
if (activatesAttribute != null) if (activatesAttribute != null)
{ {
sendTypes.UnionWith(activatesAttribute.writePendingTypes); writePendingTypes = activatesAttribute.writePendingTypes;
} }
var writesAttributes = GetType().GetCustomAttributes<Writes>(false); var defaultWritePriorityAttribute = GetType().GetCustomAttribute<DefaultWritePriority>(false);
foreach (var writesAttribute in writesAttributes)
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); 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); var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null) if (readsAttribute != null)
{ {
receiveTypes.UnionWith(readsAttribute.readTypes); readTypes = readsAttribute.readTypes;
} }
var readsPendingAttribute = GetType().GetCustomAttribute<ReadsPending>(false); var readsPendingAttribute = GetType().GetCustomAttribute<ReadsPending>(false);
if (readsPendingAttribute != null) if (readsPendingAttribute != null)
{ {
receiveTypes.UnionWith(readsPendingAttribute.readPendingTypes); readPendingTypes = readsPendingAttribute.readPendingTypes;
} }
} }
public override bool Equals(object obj) 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; return false;
@ -111,9 +119,9 @@ namespace Encompass
this.messageManager = messageManager; this.messageManager = messageManager;
} }
internal void AssignComponentMessageManager(ComponentMessageManager componentMessageManager) internal void AssignComponentUpdateManager(ComponentUpdateManager componentUpdateManager)
{ {
this.componentMessageManager = componentMessageManager; this.componentUpdateManager = componentUpdateManager;
} }
internal void AssignTimeManager(TimeManager timeManager) internal void AssignTimeManager(TimeManager timeManager)
@ -154,37 +162,12 @@ namespace Encompass
return entityManager.GetEntity(entityID); 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> /// <summary>
/// Returns an Entity containing the specified Component type. /// Returns an Entity containing the specified Component type.
/// </summary> /// </summary>
protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
{ {
var (id, component) = ReadComponentHelper<TComponent>(); return ReadComponentHelper<TComponent>().Item1;
return GetEntityByComponentID<TComponent>(id);
} }
/// <summary> /// <summary>
@ -192,60 +175,31 @@ namespace Encompass
/// </summary> /// </summary>
protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
{ {
foreach (var (id, _) in ReadComponentsHelper<TComponent>()) return ReadComponentsHelper<TComponent>().Select(pair => pair.Item1);
{
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);
} }
// these next two are for the ComponentMessageEmitter only // 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>(); return componentManager.GetComponentsByType<TComponent>();
} }
internal Entity ReadEntityFromWorld(Guid componentID) private IEnumerable<(Entity, TComponent)> ReadComponentsHelper<TComponent>() where TComponent : struct, IComponent
{ {
return GetEntity(componentManager.GetEntityIDByComponentID(componentID)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
} var existingRead = readTypes.Contains(typeof(TComponent));
private IEnumerable<(Guid, TComponent)> ReadComponentsHelper<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
if (existingRead && pendingRead) if (existingRead && pendingRead)
{ {
return componentMessageManager.ReadExistingAndPendingComponentsByType<TComponent>(); return componentUpdateManager.ReadExistingAndPendingComponentsByType<TComponent>();
} }
else if (existingRead) else if (existingRead)
{ {
return componentMessageManager.ReadExistingComponentsByType<TComponent>(); return componentUpdateManager.ReadExistingComponentsByType<TComponent>();
} }
else if (pendingRead) else if (pendingRead)
{ {
return componentMessageManager.ReadPendingComponentsByType<TComponent>(); return componentUpdateManager.ReadPendingComponentsByType<TComponent>();
} }
else else
{ {
@ -266,24 +220,29 @@ namespace Encompass
/// </summary> /// </summary>
protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity<TComponent>() where TComponent : struct, IComponent 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>)); return componentManager.GetComponentsIncludingEntity<TComponent>();
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<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) if (existingRead && pendingRead)
{ {
return componentMessageManager.ReadFirstExistingOrPendingComponentByType<TComponent>(); return componentUpdateManager.ReadFirstExistingOrPendingComponentByType<TComponent>();
} }
else if (existingRead) else if (existingRead)
{ {
return componentMessageManager.ReadFirstExistingComponentByType<TComponent>(); return componentUpdateManager.ReadFirstExistingComponentByType<TComponent>();
} }
else if (pendingRead) else if (pendingRead)
{ {
return componentMessageManager.ReadFirstPendingComponentByType<TComponent>(); return componentUpdateManager.ReadFirstPendingComponentByType<TComponent>();
} }
else else
{ {
@ -304,8 +263,8 @@ namespace Encompass
/// </summary> /// </summary>
protected (TComponent, Entity) ReadComponentIncludingEntity<TComponent>() where TComponent : struct, IComponent protected (TComponent, Entity) ReadComponentIncludingEntity<TComponent>() where TComponent : struct, IComponent
{ {
var (id, component) = ReadComponentHelper<TComponent>(); var (entity, component) = ReadComponentHelper<TComponent>();
return (component, GetEntityByComponentID<TComponent>(id)); return (component, entity);
} }
/// <summary> /// <summary>
@ -313,19 +272,19 @@ namespace Encompass
/// </summary> /// </summary>
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead) if (existingRead && pendingRead)
{ {
return componentMessageManager.SomeExistingOrPendingComponent<TComponent>(); return componentUpdateManager.SomeExistingOrPendingComponent<TComponent>();
} }
else if (existingRead) else if (existingRead)
{ {
return componentMessageManager.SomeExistingComponent<TComponent>(); return componentUpdateManager.SomeExistingComponent<TComponent>();
} }
else if (pendingRead) else if (pendingRead)
{ {
return componentMessageManager.SomePendingComponent<TComponent>(); return componentUpdateManager.SomePendingComponent<TComponent>();
} }
else 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 pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead) 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 else
{ {
@ -354,11 +313,11 @@ namespace Encompass
} }
else if (existingRead) else if (existingRead)
{ {
return componentMessageManager.ReadExistingComponentByEntityAndType<TComponent>(entity); return componentUpdateManager.ReadExistingComponentByEntityAndType<TComponent>(entity);
} }
else if (pendingRead) else if (pendingRead)
{ {
return componentMessageManager.ReadPendingComponentByEntityAndType<TComponent>(entity); return componentUpdateManager.ReadPendingComponentByEntityAndType<TComponent>(entity);
} }
else else
{ {
@ -377,58 +336,7 @@ namespace Encompass
/// </exception> /// </exception>
protected TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
return GetComponentHelper<TComponent>(entity).Item2; return GetComponentHelper<TComponent>(entity);
}
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;
} }
/// <summary> /// <summary>
@ -439,20 +347,20 @@ namespace Encompass
/// </exception> /// </exception>
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (pendingRead && existingRead) if (pendingRead && existingRead)
{ {
return componentMessageManager.HasExistingOrPendingComponent<TComponent>(entity); return componentUpdateManager.HasExistingOrPendingComponent<TComponent>(entity);
} }
else if (existingRead) else if (existingRead)
{ {
return componentMessageManager.HasExistingComponent<TComponent>(entity); return componentUpdateManager.HasExistingComponent<TComponent>(entity);
} }
else if (pendingRead) else if (pendingRead)
{ {
return componentMessageManager.HasPendingComponent<TComponent>(entity); return componentUpdateManager.HasPendingComponent<TComponent>(entity);
} }
else else
{ {
@ -468,23 +376,20 @@ namespace Encompass
/// </exception> /// </exception>
protected bool HasComponent(Entity entity, Type type) protected bool HasComponent(Entity entity, Type type)
{ {
var pending = typeof(PendingComponentMessage<>).MakeGenericType(type); var pendingRead = readPendingTypes.Contains(type);
var existing = typeof(ComponentMessage<>).MakeGenericType(type); var existingRead = readTypes.Contains(type);
var pendingRead = receiveTypes.Contains(pending);
var existingRead = receiveTypes.Contains(existing);
if (pendingRead && existingRead) if (pendingRead && existingRead)
{ {
return componentMessageManager.HasExistingOrPendingComponent(entity, type); return componentUpdateManager.HasExistingOrPendingComponent(entity, type);
} }
else if (existingRead) else if (existingRead)
{ {
return componentMessageManager.HasExistingComponent(entity, type); return componentUpdateManager.HasExistingComponent(entity, type);
} }
else if (pendingRead) else if (pendingRead)
{ {
return componentMessageManager.HasPendingComponent(entity, type); return componentUpdateManager.HasPendingComponent(entity, type);
} }
else else
{ {
@ -500,28 +405,26 @@ namespace Encompass
/// </exception> /// </exception>
protected void SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent 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 (!writeTypes.Contains(typeof(TComponent)))
if (!sendTypes.Contains(typeof(ComponentWriteMessage<TComponent>)))
{ {
throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name); 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; written = AddPendingComponent(entity, component, priority);
newComponentMessage.entity = entity; }
newComponentMessage.componentID = componentID; else
newComponentMessage.component = component; {
newComponentMessage.priority = priority; written = componentUpdateManager.UpdateComponent(entity, component, priority);
SendPendingComponentMessage(newComponentMessage);
} }
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> /// <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 protected void SendMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{ {
messageManager.AddMessageDelayed(message, time); messageManager.AddMessage(message, time);
} }
/// <summary> /// <summary>
@ -556,30 +459,17 @@ namespace Encompass
/// <param name="time">The time in seconds that will elapse before the message is sent.</param> /// <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 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 internal void AddExistingComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
protected void SendMessage(IMessage message)
{ {
var type = message.GetType(); componentUpdateManager.AddExistingComponent(entity, component);
if (!sendTypes.Contains(type) || !type.IsValueType)
{
throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, type.Name);
}
messageManager.AddMessage(message);
} }
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); return componentUpdateManager.AddPendingComponent(entity, component, priority);
}
internal void SendPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> message) where TComponent : struct, IComponent
{
componentMessageManager.AddPendingComponentMessage(message);
} }
/// <summary> /// <summary>
@ -606,7 +496,7 @@ namespace Encompass
/// </exception> /// </exception>
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
{ {
return ReadMessages<TMessage>().First(); return messageManager.First<TMessage>();
} }
/// <summary> /// <summary>
@ -622,16 +512,7 @@ namespace Encompass
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name); throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
} }
return ReadMessages<TMessage>().Any(); return messageManager.Any<TMessage>();
}
/// <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);
} }
/// <summary> /// <summary>
@ -640,7 +521,7 @@ namespace Encompass
/// </summary> /// </summary>
protected void Destroy(Entity entity) protected void Destroy(Entity entity)
{ {
entityManager.MarkForDestroy(entity.ID); entityManager.MarkForDestroy(entity);
} }
/// <summary> /// <summary>
@ -673,21 +554,10 @@ namespace Encompass
{ {
if (!HasComponent<TComponent>(entity)) { return false; } if (!HasComponent<TComponent>(entity)) { return false; }
var (componentID, component) = GetComponentHelper<TComponent>(entity); componentManager.Remove<TComponent>(entity);
RemoveComponent(componentID);
return true; return true;
} }
/// <summary>
/// Removes the Component with the specified ID from its Entity.
/// </summary>
private void RemoveComponent(Guid componentID)
{
componentManager.MarkForRemoval(componentID);
}
/// <summary> /// <summary>
/// Activates the Encompass time dilation system. /// Activates the Encompass time dilation system.
/// Engines that have the IgnoresTimeDilation property will ignore all time dilation. /// 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) internal Entity(Guid id)
{ {
this.ID = id; ID = id;
} }
public override bool Equals(object obj) 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; return false;

View File

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

View File

@ -1,102 +1,55 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Encompass namespace Encompass
{ {
internal class MessageManager internal class MessageManager
{ {
private TimeManager timeManager; private readonly TimeManager timeManager;
private readonly MessageStore messageStore = new MessageStore();
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)>();
public MessageManager(TimeManager timeManager) public MessageManager(TimeManager timeManager)
{ {
this.timeManager = timeManager; this.timeManager = timeManager;
} }
internal void RegisterMessageType(Type messageType) internal void AddMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
{ {
if (!messageTypeToMessages.ContainsKey(messageType)) messageStore.AddMessage<TMessage>(message);
{
messageTypeToMessages.Add(messageType, new List<IMessage>());
}
} }
internal void AddMessage(IMessage message) internal void AddMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{ {
var type = message.GetType(); messageStore.AddMessage(message, time);
messageTypeToMessages[type].Add(message);
} }
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>()); } messageStore.AddMessageIgnoringTimeDilation(message, time);
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));
} }
internal void ClearMessages() internal void ClearMessages()
{ {
foreach (var entry in messageTypeToMessages) messageStore.ClearAll();
{
entry.Value.Clear();
}
} }
internal void ProcessDelayedMessages(double dt) internal void ProcessDelayedMessages(double dt)
{ {
for (int i = delayedMessages.Count - 1; i >= 0; i--) messageStore.ProcessDelayedMessages(dt * timeManager.TimeDilationFactor, dt);
{
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);
}
}
} }
internal IEnumerable<TMessage> GetMessagesByType<TMessage>() where TMessage : struct, IMessage internal IEnumerable<TMessage> GetMessagesByType<TMessage>() where TMessage : struct, IMessage
{ {
return messageTypeToMessages.ContainsKey(typeof(TMessage)) ? return messageStore.All<TMessage>();
messageTypeToMessages[typeof(TMessage)].Cast<TMessage>() : }
Enumerable.Empty<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;
using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
namespace Encompass namespace Encompass
{ {
internal class RenderManager internal class RenderManager
{ {
private readonly ComponentManager componentManager;
private readonly DrawLayerManager drawLayerManager; 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( public RenderManager(DrawLayerManager drawLayerManager)
ComponentManager componentManager,
DrawLayerManager drawLayerManager,
EntityManager entityManager
)
{ {
this.componentManager = componentManager;
this.drawLayerManager = drawLayerManager; this.drawLayerManager = drawLayerManager;
this.entityManager = entityManager;
} }
public void RegisterOrderedRenderer<TComponent>(Action<Entity, IComponent> renderAction) where TComponent : struct, IComponent public void RegisterOrderedRenderer<TComponent>(Action<Entity, IComponent> renderAction) where TComponent : struct, IComponent
@ -37,16 +28,10 @@ namespace Encompass
{ {
foreach (var layer in drawLayerManager.LayerOrder) foreach (var layer in drawLayerManager.LayerOrder)
{ {
var componentIDSet = drawLayerManager.ComponentIDsByLayer(layer);
var generalRendererSet = drawLayerManager.GeneralRenderersByLayer(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)) if (drawComponentTypeToOrderedRenderer.ContainsKey(componentType))
{ {
var internalRenderAction = drawComponentTypeToOrderedRenderer[componentType]; var internalRenderAction = drawComponentTypeToOrderedRenderer[componentType];

View File

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

View File

@ -14,23 +14,21 @@ namespace Encompass
{ {
get get
{ {
double calculatedFactor = 1;
if (elapsedTime < easeInTime) if (elapsedTime < easeInTime)
{ {
calculatedFactor = easeInFunction(elapsedTime, 1, factor - 1, easeInTime); return easeInFunction(elapsedTime, 1, factor - 1, easeInTime);
} }
else if (elapsedTime < easeInTime + activeTime) else if (elapsedTime < easeInTime + activeTime)
{ {
calculatedFactor = factor; return factor;
} }
else if (elapsedTime < easeInTime + activeTime + easeOutTime) else if (elapsedTime < easeInTime + activeTime + easeOutTime)
{ {
var elapsedOutTime = elapsedTime - easeInTime - activeTime; 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 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) private double Linear(double t, double b, double c, double d)
{ {
return c * t / d + b; return (c * t / d) + b;
} }
public double TimeDilationFactor public double TimeDilationFactor

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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