diff --git a/encompass-cs/Attributes/Reads.cs b/encompass-cs/Attributes/Reads.cs index a8892dc..b35e81f 100644 --- a/encompass-cs/Attributes/Reads.cs +++ b/encompass-cs/Attributes/Reads.cs @@ -21,7 +21,7 @@ namespace Encompass throw new IllegalReadTypeException("{0} must be a Component", readType.Name); } - this.readTypes.Add(typeof(ComponentMessage<>).MakeGenericType(readType)); + this.readTypes.Add(readType); } } } diff --git a/encompass-cs/Attributes/ReadsPending.cs b/encompass-cs/Attributes/ReadsPending.cs index f1798f8..e7e8653 100644 --- a/encompass-cs/Attributes/ReadsPending.cs +++ b/encompass-cs/Attributes/ReadsPending.cs @@ -21,7 +21,7 @@ namespace Encompass throw new IllegalReadTypeException("{0} must be a Component", readPendingType.Name); } - this.readPendingTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(readPendingType)); + this.readPendingTypes.Add(readPendingType); } } } diff --git a/encompass-cs/Attributes/Writes.cs b/encompass-cs/Attributes/Writes.cs index b6edfe1..ee73ded 100644 --- a/encompass-cs/Attributes/Writes.cs +++ b/encompass-cs/Attributes/Writes.cs @@ -21,7 +21,7 @@ namespace Encompass throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name); } - this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType)); + this.writeTypes.Add(writeType); } } @@ -33,8 +33,8 @@ namespace Encompass throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name); } - this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType)); - this.priorities.Add(writeType, priority); + writeTypes.Add(writeType); + priorities.Add(writeType, priority); } } } diff --git a/encompass-cs/Attributes/WritesPending.cs b/encompass-cs/Attributes/WritesPending.cs index 0983aab..ea5a370 100644 --- a/encompass-cs/Attributes/WritesPending.cs +++ b/encompass-cs/Attributes/WritesPending.cs @@ -19,7 +19,7 @@ namespace Encompass throw new IllegalWritePendingTypeException("{0} must be a Component", writePendingType.Name); } - this.writePendingTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(writePendingType)); + this.writePendingTypes.Add(writePendingType); } } } diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs new file mode 100644 index 0000000..ffb4eda --- /dev/null +++ b/encompass-cs/Collections/ComponentStore.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; + +namespace Encompass +{ + internal class ComponentStore + { + private Dictionary Stores = new Dictionary(512); + + public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable() + { + foreach (var entry in Stores) + { + yield return (entry.Key, entry.Value); + } + } + + private TypedComponentStore Lookup() where TComponent : struct, IComponent + { + if (!Stores.ContainsKey(typeof(TComponent))) + { + var store = new TypedComponentStore(); + Stores.Add(typeof(TComponent), store); + } + return Stores[typeof(TComponent)] as TypedComponentStore; + } + + public bool Has(Entity entity) where TComponent : struct, IComponent + { + return Lookup().Has(entity); + } + + public bool Has(Type type, Entity entity) + { + return Stores.ContainsKey(type) && Stores[type].Has(entity); + } + + public TComponent Get(Entity entity) where TComponent : struct, IComponent + { + return Lookup().Get(entity); + } + + public void Set(Entity entity, TComponent component) where TComponent : struct, IComponent + { + Lookup().Set(entity, component); + } + + public bool Set(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + { + return Lookup().Set(entity, component, priority); + } + + public void Remove(Entity entity) where TComponent : struct, IComponent + { + Lookup().Remove(entity); + } + + public void Remove(Entity entity) + { + foreach (var entry in Stores.Values) + { + entry.Remove(entity); + } + } + + public bool Any() where TComponent : struct, IComponent + { + return Lookup().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() where TComponent : struct, IComponent + { + return Lookup().All(); + } + + public void Clear() where TComponent : struct, IComponent + { + Lookup().Clear(); + } + + public void ClearAll() + { + foreach (var store in Stores.Values) + { + store.Clear(); + } + } + + public void SwapWith(ComponentStore other) + { + (Stores, other.Stores) = (other.Stores, Stores); + } + } +} diff --git a/encompass-cs/Collections/MessageStore.cs b/encompass-cs/Collections/MessageStore.cs new file mode 100644 index 0000000..12aef7b --- /dev/null +++ b/encompass-cs/Collections/MessageStore.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace Encompass +{ + internal class MessageStore + { + private Dictionary Stores = new Dictionary(512); + + private void RegisterMessageType() where TMessage : struct, IMessage + { + Stores.Add(typeof(TMessage), new TypedMessageStore()); + } + + private TypedMessageStore Lookup() where TMessage : struct, IMessage + { + if (!Stores.ContainsKey(typeof(TMessage))) { RegisterMessageType(); } + return Stores[typeof(TMessage)] as TypedMessageStore; + } + + public void AddMessage(TMessage message) where TMessage : struct, IMessage + { + Lookup().Add(message); + } + + public void AddMessage(TMessage message, double time) where TMessage : struct, IMessage + { + Lookup().Add(message, time); + } + + public void AddMessageIgnoringTimeDilation(TMessage message, double time) where TMessage : struct, IMessage + { + Lookup().AddIgnoringTimeDilation(message, time); + } + + public TMessage First() where TMessage : struct, IMessage + { + return Lookup().First(); + } + + public IEnumerable All() where TMessage : struct, IMessage + { + return Lookup().All(); + } + + public bool Any() where TMessage : struct, IMessage + { + return Lookup().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(); + } + } + } +} diff --git a/encompass-cs/Collections/TypedComponentStore.cs b/encompass-cs/Collections/TypedComponentStore.cs new file mode 100644 index 0000000..2b47f63 --- /dev/null +++ b/encompass-cs/Collections/TypedComponentStore.cs @@ -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 : TypedComponentStore where TComponent : struct, IComponent + { + private readonly Dictionary store = new Dictionary(128); + private readonly Dictionary priorities = new Dictionary(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 All() + // { + // return store.Values.Cast(); + // } + + public override void Remove(Entity entity) + { + store.Remove(entity); + priorities.Remove(entity); + } + } +} diff --git a/encompass-cs/Collections/TypedMessageStore.cs b/encompass-cs/Collections/TypedMessageStore.cs new file mode 100644 index 0000000..06fc648 --- /dev/null +++ b/encompass-cs/Collections/TypedMessageStore.cs @@ -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 : TypedMessageStore where TMessage : struct, IMessage + { + private readonly List store = new List(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 All() + { + return store; + } + + public override void Clear() + { + store.Clear(); + } + } +} diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index f76d76e..c0d53e3 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -1,265 +1,87 @@ -using System; using System.Collections.Generic; using System.Linq; -using Encompass.Exceptions; -using Collections.Pooled; namespace Encompass { internal class ComponentManager { private readonly DrawLayerManager drawLayerManager; + private readonly ComponentUpdateManager componentUpdateManager; - private readonly Dictionary componentIDToType = new Dictionary(); - private readonly Dictionary IDToComponent = new Dictionary(); - private readonly Dictionary> entityIDToComponentIDs = new Dictionary>(); - private readonly Dictionary componentIDToEntityID = new Dictionary(); + private readonly ComponentStore componentStore = new ComponentStore(); + private readonly HashSet entitiesMarkedForRemoval = new HashSet(); - private readonly Dictionary> entityIDToComponentTypeToComponentID = new Dictionary>(); - - private readonly Dictionary> typeToComponentIDs = new Dictionary>(); - - 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 componentIDsMarkedForWrite = new HashSet(); - private readonly HashSet componentsMarkedForRemoval = new HashSet(); - - public ComponentManager(DrawLayerManager drawLayerManager) + public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager) { this.drawLayerManager = drawLayerManager; + this.componentUpdateManager = componentUpdateManager; } - internal void RegisterEntity(Guid entityID) + internal void SetComponentStore(ComponentStore componentStore) { - entityIDToComponentIDs.Add(entityID, new PooledSet()); - entityIDToComponentTypeToComponentID.Add(entityID, new PooledDictionary()); + this.componentStore.SwapWith(componentStore); } - private Guid NextID() + internal void RegisterDrawableComponent(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent { - return Guid.NewGuid(); + drawLayerManager.RegisterComponentWithLayer(entity, component, layer); } - internal Guid MarkComponentForWrite(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + internal void AddComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - Guid id; - if (EntityHasComponentOfType(entity)) - { - id = GetComponentByEntityAndType(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(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()); - } - typeToComponentIDs[type].Add(componentID); - entityIDToComponentIDs[entity.ID].Add(componentID); - } - - internal void UpdateComponent(Guid componentID, IComponent component) - { - IDToComponent[componentID] = component; + componentStore.Set(entity, component); } internal void WriteComponents() { - foreach (var keyValuePair in componentWriteData) - { - var (entity, type) = keyValuePair.Key; - var (componentID, component) = keyValuePair.Value; - - if (!componentIDsMarkedForWrite.Contains(componentID) || !entityIDToComponentTypeToComponentID.ContainsKey(entity.ID)) { continue; } - - if (entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(type)) - { - UpdateComponent(componentID, component); - } - else - { - AddComponent(entity, type, componentID, component); - } - } - - componentWriteData.Clear(); - componentIDsMarkedForWrite.Clear(); - componentWritePriorities.Clear(); + componentStore.SwapWith(componentUpdateManager.UpToDateComponentStore); } - internal IEnumerable GetComponentIDsByEntityID(Guid entityID) + internal IEnumerable<(TComponent, Entity)> GetComponentsIncludingEntity() where TComponent : struct, IComponent { - if (entityIDToComponentIDs.TryGetValue(entityID, out PooledSet idSet)) - { - return idSet; - } - return Enumerable.Empty(); + return componentStore.All().Select(pair => (pair.Item2, pair.Item1)); } - internal IEnumerable<(Guid, TComponent)> GetComponentsByType() where TComponent : struct, IComponent + internal IEnumerable GetComponentsByType() where TComponent : struct, IComponent { - if (typeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Select(id => (id, (TComponent)IDToComponent[id])); - } - return Enumerable.Empty<(Guid, TComponent)>(); + return componentStore.All().Select(pair => pair.Item2); } - internal (Guid, TComponent) GetComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + internal TComponent GetComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent { - if (entityIDToComponentTypeToComponentID.ContainsKey(entity.ID) && entityIDToComponentTypeToComponentID[entity.ID].TryGetValue(typeof(TComponent), out Guid id)) - { - return (id, (TComponent)IDToComponent[id]); - } - - throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", typeof(TComponent).Name, entity.ID); + return componentStore.Get(entity); } internal bool EntityHasComponentOfType(Entity entity) where TComponent : struct, IComponent { - return (entityIDToComponentTypeToComponentID.ContainsKey(entity.ID) && entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(typeof(TComponent))); + return componentStore.Has(entity); } internal bool ComponentOfTypeExists() where TComponent : struct, IComponent { - if (typeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; + return componentStore.Any(); } - internal IComponent GetComponentByID(Guid componentID) + internal void MarkAllComponentsOnEntityForRemoval(Entity entity) { - if (IDToComponent.ContainsKey(componentID)) - { - return IDToComponent[componentID]; - } - else - { - throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID); - } - } - - internal Type GetComponentTypeByID(Guid componentID) - { - if (componentIDToType.ContainsKey(componentID)) - { - return componentIDToType[componentID]; - } - else - { - throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID); - } - } - - internal Guid GetEntityIDByComponentID(Guid componentID) - { - if (componentIDToEntityID.ContainsKey(componentID)) - { - return componentIDToEntityID[componentID]; - } - else - { - throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID); - } - } - - internal void MarkAllComponentsOnEntityForRemoval(Guid entityID) - { - foreach (var componentID in GetComponentIDsByEntityID(entityID)) - { - MarkForRemoval(componentID); - } - } - - internal void MarkForRemoval(Guid componentID) - { - componentsMarkedForRemoval.Add(componentID); + entitiesMarkedForRemoval.Add(entity); } internal void RemoveMarkedComponents() { - foreach (var componentID in componentsMarkedForRemoval) + foreach (var entity in entitiesMarkedForRemoval) { - if (componentIDsMarkedForWrite.Contains(componentID)) - { - componentIDsMarkedForWrite.Remove(componentID); - } - - if (IDToComponent.ContainsKey(componentID)) - { - Remove(componentID); - } + componentStore.Remove(entity); + drawLayerManager.UnRegisterEntityWithLayer(entity); } - componentsMarkedForRemoval.Clear(); + entitiesMarkedForRemoval.Clear(); } - private void Remove(Guid componentID) + public void Remove(Entity entity) where TComponent : struct, IComponent { - var type = componentIDToType[componentID]; - - var entityID = componentIDToEntityID[componentID]; - if (entityIDToComponentIDs.ContainsKey(entityID)) - { - entityIDToComponentIDs[entityID].Remove(componentID); - } - - if (entityIDToComponentTypeToComponentID.ContainsKey(entityID)) - { - entityIDToComponentTypeToComponentID[entityID].Remove(type); - } - - IDToComponent.Remove(componentID); - componentIDToType.Remove(componentID); - componentIDToEntityID.Remove(componentID); - typeToComponentIDs[type].Remove(componentID); - - drawLayerManager.UnRegisterComponentWithLayer(componentID); - } - - public void RegisterDestroyedEntity(Guid entityID) - { - entityIDToComponentIDs[entityID].Dispose(); - entityIDToComponentIDs.Remove(entityID); - - entityIDToComponentTypeToComponentID[entityID].Dispose(); - entityIDToComponentTypeToComponentID.Remove(entityID); + componentUpdateManager.Remove(entity); + drawLayerManager.UnRegisterComponentWithLayer(entity); } } } diff --git a/encompass-cs/ComponentMessageManager.cs b/encompass-cs/ComponentMessageManager.cs deleted file mode 100644 index 8541880..0000000 --- a/encompass-cs/ComponentMessageManager.cs +++ /dev/null @@ -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 componentIDToComponent = new Dictionary(); - private readonly Dictionary componentIDToType = new Dictionary(); - - private readonly Dictionary componentIDToEntityID = new Dictionary(); - - private readonly Dictionary> componentMessageTypeToExistingComponentIDs = new Dictionary>(); - private readonly Dictionary> componentMessageTypeToPendingComponentIDs = new Dictionary>(); - private readonly Dictionary> componentMessageTypeToComponentIDs = new Dictionary>(); - - private readonly Dictionary> entityToTypeToExistingComponentID = new Dictionary>(); - private readonly Dictionary> entityToTypeToPendingComponentID = new Dictionary>(); - private readonly Dictionary> entityToTypeToComponentID = new Dictionary>(); - - private readonly Dictionary> entityToTypeToPendingComponentPriority = new Dictionary>(); - - internal void RegisterEntity(Entity entity) - { - entityToTypeToComponentID[entity] = new PooledDictionary(); - entityToTypeToPendingComponentID[entity] = new PooledDictionary(); - entityToTypeToPendingComponentPriority[entity] = new PooledDictionary(); - entityToTypeToExistingComponentID[entity] = new PooledDictionary(); - } - - 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(ComponentMessage componentMessage) where TComponent : struct, IComponent - { - RegisterExistingOrPendingComponentMessage(componentMessage.entity, componentMessage.componentID, componentMessage.component); - - if (!componentMessageTypeToExistingComponentIDs.ContainsKey(typeof(TComponent))) - { - componentMessageTypeToExistingComponentIDs.Add(typeof(TComponent), new HashSet()); - } - - 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(PendingComponentMessage pendingComponentMessage) where TComponent : struct, IComponent - { - RegisterExistingOrPendingComponentMessage(pendingComponentMessage.entity, pendingComponentMessage.componentID, pendingComponentMessage.component); - - if (!componentMessageTypeToPendingComponentIDs.ContainsKey(typeof(TComponent))) - { - componentMessageTypeToPendingComponentIDs.Add(typeof(TComponent), new HashSet()); - } - - 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(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()); - } - componentMessageTypeToComponentIDs[typeof(TComponent)].Add(componentID); - - entityToTypeToComponentID[entity][typeof(TComponent)] = componentID; - } - - // general component reads by type - - internal IEnumerable<(Guid, TComponent)> ReadExistingAndPendingComponentsByType() where TComponent : struct, IComponent - { - if (componentMessageTypeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); - } - - return Enumerable.Empty<(Guid, TComponent)>(); - } - - internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByType() where TComponent : struct, IComponent - { - if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); - } - - return Enumerable.Empty<(Guid, TComponent)>(); - } - - internal IEnumerable<(Guid, TComponent)> ReadPendingComponentsByType() where TComponent : struct, IComponent - { - if (componentMessageTypeToPendingComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Select(id => (id, (TComponent)componentIDToComponent[id])); - } - - return Enumerable.Empty<(Guid, TComponent)>(); - } - - // singular component reads by type - - internal (Guid, TComponent) ReadFirstExistingOrPendingComponentByType() where TComponent : struct, IComponent - { - if (!SomeExistingOrPendingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - return ReadExistingAndPendingComponentsByType().First(); - } - - internal (Guid, TComponent) ReadFirstExistingComponentByType() where TComponent : struct, IComponent - { - if (!SomeExistingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - return ReadExistingComponentsByType().First(); - } - - internal (Guid, TComponent) ReadFirstPendingComponentByType() where TComponent : struct, IComponent - { - if (!SomeExistingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } - return ReadPendingComponentsByType().First(); - } - - // check if some component of type exists in the world - - internal bool SomeExistingOrPendingComponent() where TComponent : struct, IComponent - { - if (componentMessageTypeToComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; - } - - internal bool SomeExistingComponent() where TComponent : struct, IComponent - { - if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; - } - - internal bool SomePendingComponent() where TComponent : struct, IComponent - { - if (componentMessageTypeToPendingComponentIDs.TryGetValue(typeof(TComponent), out HashSet idSet)) - { - return idSet.Count > 0; - } - - return false; - } - - // read components by entity and type - - internal (Guid, TComponent) ReadExistingComponentByEntityAndType(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(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(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(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(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); - } - } - } -} diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs new file mode 100644 index 0000000..c61955c --- /dev/null +++ b/encompass-cs/ComponentUpdateManager.cs @@ -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> typeToEntityToPendingComponentPriority = new Dictionary>(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(Entity entity, TComponent component) where TComponent : struct, IComponent + { + RegisterExistingOrPendingComponentMessage(entity, component); + + existingComponentStore.Set(entity, component); + } + + internal bool AddPendingComponent(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(Entity entity, TComponent component) where TComponent : struct, IComponent + { + existingAndPendingComponentStore.Set(entity, component); + UpToDateComponentStore.Set(entity, component); + } + + public bool UpdateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent + { + return UpToDateComponentStore.Set(entity, component, priority); + } + + // general component reads by type + + internal IEnumerable<(Entity, TComponent)> ReadExistingAndPendingComponentsByType() where TComponent : struct, IComponent + { + return existingAndPendingComponentStore.All(); + } + + internal IEnumerable<(Entity, TComponent)> ReadExistingComponentsByType() where TComponent : struct, IComponent + { + return existingComponentStore.All(); + } + + internal IEnumerable<(Entity, TComponent)> ReadPendingComponentsByType() where TComponent : struct, IComponent + { + return pendingComponentStore.All(); + } + + // singular component reads by type + + internal (Entity, TComponent) ReadFirstExistingOrPendingComponentByType() where TComponent : struct, IComponent + { + if (!SomeExistingOrPendingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + return ReadExistingAndPendingComponentsByType().First(); + } + + internal (Entity, TComponent) ReadFirstExistingComponentByType() where TComponent : struct, IComponent + { + if (!SomeExistingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + return ReadExistingComponentsByType().First(); + } + + internal (Entity, TComponent) ReadFirstPendingComponentByType() where TComponent : struct, IComponent + { + if (!SomePendingComponent()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); } + return ReadPendingComponentsByType().First(); + } + + // check if some component of type exists in the world + + internal bool SomeExistingOrPendingComponent() where TComponent : struct, IComponent + { + return existingAndPendingComponentStore.Any(); + } + + internal bool SomeExistingComponent() where TComponent : struct, IComponent + { + return existingComponentStore.Any(); + } + + internal bool SomePendingComponent() where TComponent : struct, IComponent + { + return pendingComponentStore.Any(); + } + + // read components by entity and type + + internal TComponent ReadExistingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return existingComponentStore.Get(entity); + } + + internal TComponent ReadPendingComponentByEntityAndType(Entity entity) where TComponent : struct, IComponent + { + return pendingComponentStore.Get(entity); + } + + // check if entity has component of type + + internal bool HasExistingOrPendingComponent(Entity entity) where TComponent : struct, IComponent + { + return existingAndPendingComponentStore.Has(entity); + } + + internal bool HasExistingOrPendingComponent(Entity entity, Type type) + { + return existingAndPendingComponentStore.Has(type, entity); + } + + internal bool HasExistingComponent(Entity entity) where TComponent : struct, IComponent + { + return existingComponentStore.Has(entity); + } + + internal bool HasExistingComponent(Entity entity, Type type) + { + return existingComponentStore.Has(type, entity); + } + + internal bool HasPendingComponent(Entity entity) where TComponent : struct, IComponent + { + return pendingComponentStore.Has(entity); + } + + internal bool HasPendingComponent(Entity entity, Type type) + { + return pendingComponentStore.Has(type, entity); + } + + internal void Remove(Entity entity) where TComponent : struct, IComponent + { + UpToDateComponentStore.Remove(entity); + } + } +} diff --git a/encompass-cs/DrawLayerManager.cs b/encompass-cs/DrawLayerManager.cs index 926be6f..9785987 100644 --- a/encompass-cs/DrawLayerManager.cs +++ b/encompass-cs/DrawLayerManager.cs @@ -8,10 +8,10 @@ namespace Encompass { private readonly SortedList layerOrder = new SortedList(); - private readonly Dictionary> layerIndexToComponentIDs = new Dictionary>(); - private readonly Dictionary> layerIndexToGeneralRenderers = new Dictionary>(); + private readonly Dictionary layerIndexToComponentStore = new Dictionary(512); + private readonly Dictionary> layerIndexToGeneralRenderers = new Dictionary>(512); - private readonly Dictionary componentIDToLayerIndex = new Dictionary(); + private readonly Dictionary> typeToEntityToLayer = new Dictionary>(512); public IEnumerable LayerOrder { get { return layerOrder.Values; } } @@ -49,23 +49,28 @@ namespace Encompass RegisterGeneralRendererWithLayer(renderer, newLayer); } - public void RegisterComponentWithLayer(Guid id, int layer) + public void RegisterComponentWithLayer(Entity entity, TComponent component, int layer) where TComponent : struct, IComponent { - if (componentIDToLayerIndex.ContainsKey(id)) { UnRegisterComponentWithLayer(id); } - - if (layerIndexToComponentIDs.ContainsKey(layer)) + if (!typeToEntityToLayer.ContainsKey(typeof(TComponent))) { - var set = layerIndexToComponentIDs[layer]; - set.Add(id); + typeToEntityToLayer.Add(typeof(TComponent), new Dictionary()); + } + + if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) { UnRegisterComponentWithLayer(entity); } + + if (layerIndexToComponentStore.ContainsKey(layer)) + { + var set = layerIndexToComponentStore[layer]; + set.Set(entity, component); } else { - var set = new HashSet(); - layerIndexToComponentIDs.Add(layer, set); - set.Add(id); + var set = new ComponentStore(); + layerIndexToComponentStore.Add(layer, set); + set.Set(entity, component); } - componentIDToLayerIndex[id] = layer; + typeToEntityToLayer[typeof(TComponent)].Add(entity, layer); if (!layerOrder.ContainsKey(layer)) { @@ -73,21 +78,24 @@ namespace Encompass } } - public void UnRegisterComponentWithLayer(Guid id) + public void UnRegisterComponentWithLayer(Entity entity) where TComponent : struct, IComponent { - if (componentIDToLayerIndex.ContainsKey(id)) + if (!typeToEntityToLayer.ContainsKey(typeof(TComponent))) { return; } + + if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) { - var layer = componentIDToLayerIndex[id]; - layerIndexToComponentIDs[layer].Remove(id); + var layer = typeToEntityToLayer[typeof(TComponent)][entity]; + layerIndexToComponentStore[layer].Remove(entity); } - componentIDToLayerIndex.Remove(id); + typeToEntityToLayer[typeof(TComponent)].Remove(entity); } - public IEnumerable ComponentIDsByLayer(int layer) + public void UnRegisterEntityWithLayer(Entity entity) { - return layerIndexToComponentIDs.ContainsKey(layer) ? - layerIndexToComponentIDs[layer] : - Enumerable.Empty(); + foreach (var store in layerIndexToComponentStore.Values) + { + store.Remove(entity); + } } public IEnumerable GeneralRenderersByLayer(int layer) @@ -96,5 +104,12 @@ namespace Encompass layerIndexToGeneralRenderers[layer] : Enumerable.Empty(); } + + public IEnumerable<(Entity, Type, IComponent)> AllInLayer(int layer) + { + return layerIndexToComponentStore.ContainsKey(layer) ? + layerIndexToComponentStore[layer].AllInterfaceTyped() : + Enumerable.Empty<(Entity, Type, IComponent)>(); + } } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 1ca83d5..4bea453 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -15,23 +15,25 @@ namespace Encompass { internal Guid ID; + internal readonly HashSet readTypes = new HashSet(); + internal readonly HashSet readPendingTypes = new HashSet(); internal readonly HashSet sendTypes = new HashSet(); internal readonly HashSet receiveTypes = new HashSet(); + internal readonly HashSet writeTypes = new HashSet(); + internal readonly HashSet writePendingTypes = new HashSet(); internal readonly Dictionary writePriorities = new Dictionary(); + internal readonly int defaultWritePriority = 0; /// - /// If false, the Engine will ignore time dilation. + /// If false, the Engine will ignore time dilation. /// internal bool usesTimeDilation = true; public bool TimeDilationActive { get => usesTimeDilation && timeManager.TimeDilationActive; } - /// - /// Used when activating time dilation. Lower priority overrides higher priority. - /// private EntityManager entityManager; private MessageManager messageManager; private ComponentManager componentManager; - private ComponentMessageManager componentMessageManager; + private ComponentUpdateManager componentUpdateManager; private TimeManager timeManager; protected Engine() @@ -47,13 +49,19 @@ namespace Encompass var activatesAttribute = GetType().GetCustomAttribute(false); if (activatesAttribute != null) { - sendTypes.UnionWith(activatesAttribute.writePendingTypes); + writePendingTypes = activatesAttribute.writePendingTypes; } - var writesAttributes = GetType().GetCustomAttributes(false); - foreach (var writesAttribute in writesAttributes) + var defaultWritePriorityAttribute = GetType().GetCustomAttribute(false); + + if (defaultWritePriorityAttribute != null) { - sendTypes.UnionWith(writesAttribute.writeTypes); + defaultWritePriority = defaultWritePriorityAttribute.writePriority; + } + + foreach (var writesAttribute in GetType().GetCustomAttributes(false)) + { + writeTypes.UnionWith(writesAttribute.writeTypes); writePriorities = new Dictionary[2] { writePriorities, writesAttribute.priorities }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value); } @@ -66,21 +74,21 @@ namespace Encompass var readsAttribute = GetType().GetCustomAttribute(false); if (readsAttribute != null) { - receiveTypes.UnionWith(readsAttribute.readTypes); + readTypes = readsAttribute.readTypes; } var readsPendingAttribute = GetType().GetCustomAttribute(false); if (readsPendingAttribute != null) { - receiveTypes.UnionWith(readsPendingAttribute.readPendingTypes); + readPendingTypes = readsPendingAttribute.readPendingTypes; } } public override bool Equals(object obj) { - if (obj is Engine) + if (obj is Engine engine) { - return this.Equals((Engine)obj); + return Equals(engine); } return false; @@ -111,9 +119,9 @@ namespace Encompass this.messageManager = messageManager; } - internal void AssignComponentMessageManager(ComponentMessageManager componentMessageManager) + internal void AssignComponentUpdateManager(ComponentUpdateManager componentUpdateManager) { - this.componentMessageManager = componentMessageManager; + this.componentUpdateManager = componentUpdateManager; } internal void AssignTimeManager(TimeManager timeManager) @@ -154,37 +162,12 @@ namespace Encompass return entityManager.GetEntity(entityID); } - /// - /// Returns the Entity ID associated with the specified Component Type and ID. - /// - private Guid GetEntityIDByComponentID(Guid componentID) where TComponent : struct, IComponent - { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); - - if (!pendingRead && !existingRead) - { - throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); - } - - return componentMessageManager.GetEntityIDByComponentID(componentID); - } - - /// - /// Returns the Entity associated with the specified Component Type and ID. - /// - private Entity GetEntityByComponentID(Guid componentID) where TComponent : struct, IComponent - { - return GetEntity(GetEntityIDByComponentID(componentID)); - } - /// /// Returns an Entity containing the specified Component type. /// protected Entity ReadEntity() where TComponent : struct, IComponent { - var (id, component) = ReadComponentHelper(); - return GetEntityByComponentID(id); + return ReadComponentHelper().Item1; } /// @@ -192,60 +175,31 @@ namespace Encompass /// protected IEnumerable ReadEntities() where TComponent : struct, IComponent { - foreach (var (id, _) in ReadComponentsHelper()) - { - yield return GetEntityByComponentID(id); - } - } - - /// - /// Returns the Component struct with the specified Component Type and ID. - /// - internal TComponent GetComponentByID(Guid componentID) where TComponent : struct, IComponent - { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); - - if (!pendingRead && !existingRead) - { - throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); - } - - if (componentMessageManager.GetComponentTypeByID(componentID) != typeof(TComponent)) - { - throw new ComponentTypeMismatchException("Expected Component to be of type {0} but was actually of type {1}", typeof(TComponent).Name, componentMessageManager.GetComponentTypeByID(componentID).Name); - } - - return (TComponent)componentMessageManager.GetComponentByID(componentID); + return ReadComponentsHelper().Select(pair => pair.Item1); } // these next two are for the ComponentMessageEmitter only - internal IEnumerable<(Guid, TComponent)> ReadComponentsFromWorld() where TComponent : struct, IComponent + internal IEnumerable ReadComponentsFromWorld() where TComponent : struct, IComponent { return componentManager.GetComponentsByType(); } - internal Entity ReadEntityFromWorld(Guid componentID) + private IEnumerable<(Entity, TComponent)> ReadComponentsHelper() where TComponent : struct, IComponent { - return GetEntity(componentManager.GetEntityIDByComponentID(componentID)); - } - - private IEnumerable<(Guid, TComponent)> ReadComponentsHelper() where TComponent : struct, IComponent - { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); + var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && pendingRead) { - return componentMessageManager.ReadExistingAndPendingComponentsByType(); + return componentUpdateManager.ReadExistingAndPendingComponentsByType(); } else if (existingRead) { - return componentMessageManager.ReadExistingComponentsByType(); + return componentUpdateManager.ReadExistingComponentsByType(); } else if (pendingRead) { - return componentMessageManager.ReadPendingComponentsByType(); + return componentUpdateManager.ReadPendingComponentsByType(); } else { @@ -266,24 +220,29 @@ namespace Encompass /// protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity() where TComponent : struct, IComponent { - return ReadComponentsHelper().Select((tuple) => (tuple.Item2, GetEntityByComponentID(tuple.Item1))); + return ReadComponentsHelper().Select((tuple) => (tuple.Item2, tuple.Item1)); } - private (Guid, TComponent) ReadComponentHelper() where TComponent : struct, IComponent + internal IEnumerable<(TComponent, Entity)> InternalRead() where TComponent : struct, IComponent { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); + return componentManager.GetComponentsIncludingEntity(); + } + + private (Entity, TComponent) ReadComponentHelper() where TComponent : struct, IComponent + { + var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && pendingRead) { - return componentMessageManager.ReadFirstExistingOrPendingComponentByType(); + return componentUpdateManager.ReadFirstExistingOrPendingComponentByType(); } else if (existingRead) { - return componentMessageManager.ReadFirstExistingComponentByType(); + return componentUpdateManager.ReadFirstExistingComponentByType(); } else if (pendingRead) { - return componentMessageManager.ReadFirstPendingComponentByType(); + return componentUpdateManager.ReadFirstPendingComponentByType(); } else { @@ -304,8 +263,8 @@ namespace Encompass /// protected (TComponent, Entity) ReadComponentIncludingEntity() where TComponent : struct, IComponent { - var (id, component) = ReadComponentHelper(); - return (component, GetEntityByComponentID(id)); + var (entity, component) = ReadComponentHelper(); + return (component, entity); } /// @@ -313,19 +272,19 @@ namespace Encompass /// protected bool SomeComponent() where TComponent : struct, IComponent { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); + var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && pendingRead) { - return componentMessageManager.SomeExistingOrPendingComponent(); + return componentUpdateManager.SomeExistingOrPendingComponent(); } else if (existingRead) { - return componentMessageManager.SomeExistingComponent(); + return componentUpdateManager.SomeExistingComponent(); } else if (pendingRead) { - return componentMessageManager.SomePendingComponent(); + return componentUpdateManager.SomePendingComponent(); } else { @@ -333,19 +292,19 @@ namespace Encompass } } - private (Guid, TComponent) GetComponentHelper(Entity entity) where TComponent : struct, IComponent + private TComponent GetComponentHelper(Entity entity) where TComponent : struct, IComponent { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); + var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var existingRead = readTypes.Contains(typeof(TComponent)); if (existingRead && pendingRead) { - if (componentMessageManager.HasPendingComponent(entity)) + if (componentUpdateManager.HasPendingComponent(entity)) { - return componentMessageManager.ReadPendingComponentByEntityAndType(entity); + return componentUpdateManager.ReadPendingComponentByEntityAndType(entity); } - else if (componentMessageManager.HasExistingComponent(entity)) + else if (componentUpdateManager.HasExistingComponent(entity)) { - return componentMessageManager.ReadExistingComponentByEntityAndType(entity); + return componentUpdateManager.ReadExistingComponentByEntityAndType(entity); } else { @@ -354,11 +313,11 @@ namespace Encompass } else if (existingRead) { - return componentMessageManager.ReadExistingComponentByEntityAndType(entity); + return componentUpdateManager.ReadExistingComponentByEntityAndType(entity); } else if (pendingRead) { - return componentMessageManager.ReadPendingComponentByEntityAndType(entity); + return componentUpdateManager.ReadPendingComponentByEntityAndType(entity); } else { @@ -377,58 +336,7 @@ namespace Encompass /// protected TComponent GetComponent(Entity entity) where TComponent : struct, IComponent { - return GetComponentHelper(entity).Item2; - } - - private (Guid, IComponent) GetComponentHelper(Entity entity, Type type) - { - var pending = typeof(PendingComponentMessage<>).MakeGenericType(type); - var existing = typeof(ComponentMessage<>).MakeGenericType(type); - - var pendingRead = receiveTypes.Contains(pending); - var existingRead = receiveTypes.Contains(existing); - - if (existingRead && pendingRead) - { - if (componentMessageManager.HasPendingComponent(entity, pending)) - { - return componentMessageManager.ReadPendingComponentByEntityAndType(entity, pending); - } - else if (componentMessageManager.HasExistingComponent(entity, existing)) - { - return componentMessageManager.ReadExistingComponentByEntityAndType(entity, existing); - } - else - { - throw new NoComponentOfTypeOnEntityException("No Component of type {0} exists on Entity {1}", type.Name, entity.ID); - } - } - else if (existingRead) - { - return componentMessageManager.ReadExistingComponentByEntityAndType(entity, existing); - } - else if (pendingRead) - { - return componentMessageManager.ReadPendingComponentByEntityAndType(entity, pending); - } - else - { - throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, type.Name); - } - } - - /// - /// Returns a Component with the specified Type that exists on the Entity. - /// - /// - /// Thrown when the Entity does not have a Component of the specified Type - /// - /// - /// Thrown when the Engine does not declare that it reads the given Component Type. - /// - protected IComponent GetComponent(Entity entity, Type type) - { - return GetComponentHelper(entity, type).Item2; + return GetComponentHelper(entity); } /// @@ -439,20 +347,20 @@ namespace Encompass /// protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { - var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage)); - var existingRead = receiveTypes.Contains(typeof(ComponentMessage)); + var pendingRead = readPendingTypes.Contains(typeof(TComponent)); + var existingRead = readTypes.Contains(typeof(TComponent)); if (pendingRead && existingRead) { - return componentMessageManager.HasExistingOrPendingComponent(entity); + return componentUpdateManager.HasExistingOrPendingComponent(entity); } else if (existingRead) { - return componentMessageManager.HasExistingComponent(entity); + return componentUpdateManager.HasExistingComponent(entity); } else if (pendingRead) { - return componentMessageManager.HasPendingComponent(entity); + return componentUpdateManager.HasPendingComponent(entity); } else { @@ -468,23 +376,20 @@ namespace Encompass /// protected bool HasComponent(Entity entity, Type type) { - var pending = typeof(PendingComponentMessage<>).MakeGenericType(type); - var existing = typeof(ComponentMessage<>).MakeGenericType(type); - - var pendingRead = receiveTypes.Contains(pending); - var existingRead = receiveTypes.Contains(existing); + var pendingRead = readPendingTypes.Contains(type); + var existingRead = readTypes.Contains(type); if (pendingRead && existingRead) { - return componentMessageManager.HasExistingOrPendingComponent(entity, type); + return componentUpdateManager.HasExistingOrPendingComponent(entity, type); } else if (existingRead) { - return componentMessageManager.HasExistingComponent(entity, type); + return componentUpdateManager.HasExistingComponent(entity, type); } else if (pendingRead) { - return componentMessageManager.HasPendingComponent(entity, type); + return componentUpdateManager.HasPendingComponent(entity, type); } else { @@ -500,28 +405,26 @@ namespace Encompass /// protected void SetComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0; + var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : defaultWritePriority; - var componentID = componentManager.MarkComponentForWrite(entity, component, priority); - - if (!sendTypes.Contains(typeof(ComponentWriteMessage))) + if (!writeTypes.Contains(typeof(TComponent))) { throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name); } - if (sendTypes.Contains(typeof(PendingComponentMessage))) + bool written; + if (writePendingTypes.Contains(typeof(TComponent))) { - PendingComponentMessage newComponentMessage; - newComponentMessage.entity = entity; - newComponentMessage.componentID = componentID; - newComponentMessage.component = component; - newComponentMessage.priority = priority; - SendPendingComponentMessage(newComponentMessage); + written = AddPendingComponent(entity, component, priority); + } + else + { + written = componentUpdateManager.UpdateComponent(entity, component, priority); } - if (component is IDrawableComponent drawableComponent) + if (written && component is IDrawableComponent drawableComponent) { - componentManager.RegisterDrawableComponent(componentID, drawableComponent); + componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer); } } @@ -547,7 +450,7 @@ namespace Encompass /// The time in seconds that will elapse before the message is sent. protected void SendMessage(TMessage message, double time) where TMessage : struct, IMessage { - messageManager.AddMessageDelayed(message, time); + messageManager.AddMessage(message, time); } /// @@ -556,30 +459,17 @@ namespace Encompass /// The time in seconds that will elapse before the message is sent. protected void SendMessageIgnoringTimeDilation(TMessage message, double time) where TMessage : struct, IMessage { - messageManager.AddMessageDelayedIgnoringTimeDilation(message, time); + messageManager.AddMessageIgnoringTimeDilation(message, time); } - // unparameterized version to enable dynamic dispatch - protected void SendMessage(IMessage message) + internal void AddExistingComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - var type = message.GetType(); - - if (!sendTypes.Contains(type) || !type.IsValueType) - { - throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, type.Name); - } - - messageManager.AddMessage(message); + componentUpdateManager.AddExistingComponent(entity, component); } - internal void SendExistingComponentMessage(ComponentMessage message) where TComponent : struct, IComponent + internal bool AddPendingComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent { - componentMessageManager.AddExistingComponentMessage(message); - } - - internal void SendPendingComponentMessage(PendingComponentMessage message) where TComponent : struct, IComponent - { - componentMessageManager.AddPendingComponentMessage(message); + return componentUpdateManager.AddPendingComponent(entity, component, priority); } /// @@ -606,7 +496,7 @@ namespace Encompass /// protected TMessage ReadMessage() where TMessage : struct, IMessage { - return ReadMessages().First(); + return messageManager.First(); } /// @@ -622,16 +512,7 @@ namespace Encompass throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name); } - return ReadMessages().Any(); - } - - /// - /// 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. - /// - internal void Destroy(Guid entityID) - { - entityManager.MarkForDestroy(entityID); + return messageManager.Any(); } /// @@ -640,7 +521,7 @@ namespace Encompass /// protected void Destroy(Entity entity) { - entityManager.MarkForDestroy(entity.ID); + entityManager.MarkForDestroy(entity); } /// @@ -673,21 +554,10 @@ namespace Encompass { if (!HasComponent(entity)) { return false; } - var (componentID, component) = GetComponentHelper(entity); - - RemoveComponent(componentID); - + componentManager.Remove(entity); return true; } - /// - /// Removes the Component with the specified ID from its Entity. - /// - private void RemoveComponent(Guid componentID) - { - componentManager.MarkForRemoval(componentID); - } - /// /// Activates the Encompass time dilation system. /// Engines that have the IgnoresTimeDilation property will ignore all time dilation. diff --git a/encompass-cs/Engines/ComponentEmitter.cs b/encompass-cs/Engines/ComponentEmitter.cs new file mode 100644 index 0000000..b1f7f64 --- /dev/null +++ b/encompass-cs/Engines/ComponentEmitter.cs @@ -0,0 +1,18 @@ +namespace Encompass +{ + internal class ComponentEmitter : Engine where TComponent : struct, IComponent + { + public ComponentEmitter() + { + sendTypes.Add(typeof(TComponent)); + } + + public override void Update(double dt) + { + foreach (var (component, entity) in InternalRead()) + { + AddExistingComponent(entity, component); + } + } + } +} diff --git a/encompass-cs/Engines/ComponentMessageEmitter.cs b/encompass-cs/Engines/ComponentMessageEmitter.cs deleted file mode 100644 index 5addc68..0000000 --- a/encompass-cs/Engines/ComponentMessageEmitter.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Encompass -{ - internal class ComponentMessageEmitter : Engine where TComponent : struct, IComponent - { - public ComponentMessageEmitter() : base() - { - sendTypes.Add(typeof(ComponentMessage)); - } - - public override void Update(double dt) - { - foreach (var (componentID, component) in ReadComponentsFromWorld()) - { - ComponentMessage componentMessage; - componentMessage.entity = ReadEntityFromWorld(componentID); - componentMessage.componentID = componentID; - componentMessage.component = component; - SendMessage(componentMessage); - SendExistingComponentMessage(componentMessage); - } - } - } -} \ No newline at end of file diff --git a/encompass-cs/Entity.cs b/encompass-cs/Entity.cs index fa584e3..a08954c 100644 --- a/encompass-cs/Entity.cs +++ b/encompass-cs/Entity.cs @@ -12,14 +12,14 @@ namespace Encompass internal Entity(Guid id) { - this.ID = id; + ID = id; } public override bool Equals(object obj) { - if (obj is Entity) + if (obj is Entity entity) { - return this.Equals((Entity)obj); + return Equals(entity); } return false; diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index 6a6e525..0d89fa3 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -7,17 +7,15 @@ namespace Encompass { internal class EntityManager { - private readonly Dictionary IDToEntity = new Dictionary(); + private readonly Dictionary IDToEntity = new Dictionary(1024); - private readonly HashSet entitiesMarkedForDestroy = new HashSet(); + private readonly HashSet entitiesMarkedForDestroy = new HashSet(); private readonly ComponentManager componentManager; - private readonly ComponentMessageManager componentMessageManager; - public EntityManager(ComponentManager componentManager, ComponentMessageManager componentMessageManager) + public EntityManager(ComponentManager componentManager) { this.componentManager = componentManager; - this.componentMessageManager = componentMessageManager; } public Entity CreateEntity() @@ -25,8 +23,6 @@ namespace Encompass var id = NextID(); var entity = new Entity(id); IDToEntity[id] = entity; - componentManager.RegisterEntity(id); - componentMessageManager.RegisterEntity(entity); return entity; } @@ -47,19 +43,17 @@ namespace Encompass } } - public void MarkForDestroy(Guid entityID) + public void MarkForDestroy(Entity entity) { - entitiesMarkedForDestroy.Add(entityID); + entitiesMarkedForDestroy.Add(entity); } public void DestroyMarkedEntities() { - foreach (var entityID in entitiesMarkedForDestroy) + foreach (var entity in entitiesMarkedForDestroy) { - componentMessageManager.RegisterDestroyedEntity(GetEntity(entityID)); - componentManager.MarkAllComponentsOnEntityForRemoval(entityID); - IDToEntity.Remove(entityID); - componentManager.RegisterDestroyedEntity(entityID); + componentManager.MarkAllComponentsOnEntityForRemoval(entity); + IDToEntity.Remove(entity.ID); } entitiesMarkedForDestroy.Clear(); diff --git a/encompass-cs/MessageManager.cs b/encompass-cs/MessageManager.cs index 7846092..7da56bf 100644 --- a/encompass-cs/MessageManager.cs +++ b/encompass-cs/MessageManager.cs @@ -1,102 +1,55 @@ -using System; using System.Collections.Generic; -using System.Linq; namespace Encompass { internal class MessageManager { - private TimeManager timeManager; - - private readonly Dictionary> messageTypeToMessages = new Dictionary>(); - - private readonly List<(IMessage, double)> delayedMessages = new List<(IMessage, double)>(); - private readonly List<(IMessage, double)> delayedMessagesIgnoringTimeDilation = new List<(IMessage, double)>(); + private readonly TimeManager timeManager; + private readonly MessageStore messageStore = new MessageStore(); public MessageManager(TimeManager timeManager) { this.timeManager = timeManager; } - internal void RegisterMessageType(Type messageType) + internal void AddMessage(TMessage message) where TMessage : struct, IMessage { - if (!messageTypeToMessages.ContainsKey(messageType)) - { - messageTypeToMessages.Add(messageType, new List()); - } + messageStore.AddMessage(message); } - internal void AddMessage(IMessage message) + internal void AddMessage(TMessage message, double time) where TMessage : struct, IMessage { - var type = message.GetType(); - - messageTypeToMessages[type].Add(message); + messageStore.AddMessage(message, time); } - internal void AddMessageDelayed(IMessage message, double time) + internal void AddMessageIgnoringTimeDilation(TMessage message, double time) where TMessage : struct, IMessage { - if (!messageTypeToMessages.ContainsKey(message.GetType())) { messageTypeToMessages.Add(message.GetType(), new List()); } - - delayedMessages.Add((message, time)); - } - - internal void AddMessageDelayedIgnoringTimeDilation(IMessage message, double time) - { - if (!messageTypeToMessages.ContainsKey(message.GetType())) { messageTypeToMessages.Add(message.GetType(), new List()); } - - delayedMessagesIgnoringTimeDilation.Add((message, time)); + messageStore.AddMessageIgnoringTimeDilation(message, time); } internal void ClearMessages() { - foreach (var entry in messageTypeToMessages) - { - entry.Value.Clear(); - } + messageStore.ClearAll(); } internal void ProcessDelayedMessages(double dt) { - for (int i = delayedMessages.Count - 1; i >= 0; i--) - { - var (message, time) = delayedMessages[i]; - - var updatedTime = time - (dt * timeManager.TimeDilationFactor); - - if (updatedTime <= 0) - { - AddMessage(message); - delayedMessages.RemoveAt(i); - } - else - { - delayedMessages[i] = (message, updatedTime); - } - } - - for (int i = delayedMessagesIgnoringTimeDilation.Count - 1; i >= 0; i--) - { - var (message, time) = delayedMessagesIgnoringTimeDilation[i]; - - var updatedTime = time - dt; - - if (updatedTime <= 0) - { - AddMessage(message); - delayedMessagesIgnoringTimeDilation.RemoveAt(i); - } - else - { - delayedMessagesIgnoringTimeDilation[i] = (message, updatedTime); - } - } + messageStore.ProcessDelayedMessages(dt * timeManager.TimeDilationFactor, dt); } internal IEnumerable GetMessagesByType() where TMessage : struct, IMessage { - return messageTypeToMessages.ContainsKey(typeof(TMessage)) ? - messageTypeToMessages[typeof(TMessage)].Cast() : - Enumerable.Empty(); + return messageStore.All(); + } + + internal bool Any() where TMessage : struct, IMessage + { + return messageStore.Any(); + } + + internal TMessage First() where TMessage : struct, IMessage + { + return messageStore.First(); } } } diff --git a/encompass-cs/Messages/ComponentMessage.cs b/encompass-cs/Messages/ComponentMessage.cs deleted file mode 100644 index c1b74ee..0000000 --- a/encompass-cs/Messages/ComponentMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Encompass -{ - internal struct ComponentMessage : IMessage where TComponent : struct, IComponent - { - public Entity entity; - public Guid componentID; - public TComponent component; - } -} \ No newline at end of file diff --git a/encompass-cs/Messages/ComponentWriteMessage.cs b/encompass-cs/Messages/ComponentWriteMessage.cs deleted file mode 100644 index 424fa7b..0000000 --- a/encompass-cs/Messages/ComponentWriteMessage.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Encompass -{ - internal struct ComponentWriteMessage : IMessage where TComponent : struct, IComponent - { - public Guid componentID; - public TComponent component; - } -} \ No newline at end of file diff --git a/encompass-cs/Messages/PendingComponentMessage.cs b/encompass-cs/Messages/PendingComponentMessage.cs deleted file mode 100644 index 4a051b4..0000000 --- a/encompass-cs/Messages/PendingComponentMessage.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Encompass -{ - internal struct PendingComponentMessage : IMessage where TComponent : struct, IComponent - { - public Entity entity; - public Guid componentID; - public TComponent component; - public int priority; - } -} \ No newline at end of file diff --git a/encompass-cs/RenderManager.cs b/encompass-cs/RenderManager.cs index 35c2405..cea10c0 100644 --- a/encompass-cs/RenderManager.cs +++ b/encompass-cs/RenderManager.cs @@ -1,26 +1,17 @@ using System; -using System.Reflection; using System.Collections.Generic; namespace Encompass { internal class RenderManager { - private readonly ComponentManager componentManager; private readonly DrawLayerManager drawLayerManager; - private readonly EntityManager entityManager; - private readonly Dictionary> drawComponentTypeToOrderedRenderer = new Dictionary>(); + private readonly Dictionary> drawComponentTypeToOrderedRenderer = new Dictionary>(256); - public RenderManager( - ComponentManager componentManager, - DrawLayerManager drawLayerManager, - EntityManager entityManager - ) + public RenderManager(DrawLayerManager drawLayerManager) { - this.componentManager = componentManager; this.drawLayerManager = drawLayerManager; - this.entityManager = entityManager; } public void RegisterOrderedRenderer(Action renderAction) where TComponent : struct, IComponent @@ -37,16 +28,10 @@ namespace Encompass { foreach (var layer in drawLayerManager.LayerOrder) { - var componentIDSet = drawLayerManager.ComponentIDsByLayer(layer); var generalRendererSet = drawLayerManager.GeneralRenderersByLayer(layer); - foreach (var componentID in componentIDSet) + foreach (var (entity, componentType, component) in drawLayerManager.AllInLayer(layer)) { - var component = componentManager.GetComponentByID(componentID); - var componentType = componentManager.GetComponentTypeByID(componentID); - var entityID = componentManager.GetEntityIDByComponentID(componentID); - var entity = entityManager.GetEntity(entityID); - if (drawComponentTypeToOrderedRenderer.ContainsKey(componentType)) { var internalRenderAction = drawComponentTypeToOrderedRenderer[componentType]; diff --git a/encompass-cs/Renderer.cs b/encompass-cs/Renderer.cs index cb30820..e14a8c8 100644 --- a/encompass-cs/Renderer.cs +++ b/encompass-cs/Renderer.cs @@ -19,11 +19,6 @@ namespace Encompass this.componentManager = componentManager; } - internal Guid GetEntityIDByComponentID(Guid componentID) - { - return componentManager.GetEntityIDByComponentID(componentID); - } - internal Entity GetEntity(Guid entityID) { return entityManager.GetEntity(entityID); @@ -41,12 +36,12 @@ namespace Encompass protected IEnumerable ReadComponents() where TComponent : struct, IComponent { - return componentManager.GetComponentsByType().Select(tuple => tuple.Item2); + return componentManager.GetComponentsByType(); } protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity() where TComponent : struct, IComponent { - return componentManager.GetComponentsByType().Select(tuple => (tuple.Item2, GetEntity(GetEntityIDByComponentID(tuple.Item1)))); + return componentManager.GetComponentsIncludingEntity(); } protected TComponent ReadComponent() where TComponent : struct, IComponent @@ -61,7 +56,7 @@ namespace Encompass protected TComponent GetComponent(Entity entity) where TComponent : struct, IComponent { - return componentManager.GetComponentByEntityAndType(entity).Item2; + return componentManager.GetComponentByEntityAndType(entity); } protected bool HasComponent(Entity entity) where TComponent : struct, IComponent diff --git a/encompass-cs/TimeDilationData.cs b/encompass-cs/TimeDilationData.cs index 4939f43..b344589 100644 --- a/encompass-cs/TimeDilationData.cs +++ b/encompass-cs/TimeDilationData.cs @@ -14,23 +14,21 @@ namespace Encompass { get { - double calculatedFactor = 1; - if (elapsedTime < easeInTime) { - calculatedFactor = easeInFunction(elapsedTime, 1, factor - 1, easeInTime); + return easeInFunction(elapsedTime, 1, factor - 1, easeInTime); } else if (elapsedTime < easeInTime + activeTime) { - calculatedFactor = factor; + return factor; } else if (elapsedTime < easeInTime + activeTime + easeOutTime) { var elapsedOutTime = elapsedTime - easeInTime - activeTime; - calculatedFactor = easeOutFunction(elapsedOutTime, factor, 1 - factor, easeOutTime); + return easeOutFunction(elapsedOutTime, factor, 1 - factor, easeOutTime); } - return calculatedFactor; + return 1; } } } diff --git a/encompass-cs/TimeManager.cs b/encompass-cs/TimeManager.cs index 4c23dee..d29ac10 100644 --- a/encompass-cs/TimeManager.cs +++ b/encompass-cs/TimeManager.cs @@ -5,11 +5,11 @@ namespace Encompass { internal class TimeManager { - private List timeDilationDatas = new List(); + private readonly List timeDilationDatas = new List(32); private double Linear(double t, double b, double c, double d) { - return c * t / d + b; + return (c * t / d) + b; } public double TimeDilationFactor diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 2a3a099..1154ca3 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -11,7 +11,7 @@ namespace Encompass private readonly EntityManager entityManager; private readonly ComponentManager componentManager; private readonly MessageManager messageManager; - private readonly ComponentMessageManager componentMessageManager; + private readonly ComponentUpdateManager componentUpdateManager; private readonly TimeManager timeManager; private readonly RenderManager renderManager; @@ -20,7 +20,7 @@ namespace Encompass EntityManager entityManager, ComponentManager componentManager, MessageManager messageManager, - ComponentMessageManager componentMessageManager, + ComponentUpdateManager componentUpdateManager, TimeManager timeManager, RenderManager renderManager ) @@ -29,7 +29,7 @@ namespace Encompass this.entityManager = entityManager; this.componentManager = componentManager; this.messageManager = messageManager; - this.componentMessageManager = componentMessageManager; + this.componentUpdateManager = componentUpdateManager; this.timeManager = timeManager; this.renderManager = renderManager; } @@ -56,11 +56,12 @@ namespace Encompass } messageManager.ClearMessages(); - componentMessageManager.ClearMessages(); entityManager.DestroyMarkedEntities(); - componentManager.RemoveMarkedComponents(); componentManager.WriteComponents(); + componentManager.RemoveMarkedComponents(); + + componentUpdateManager.Clear(); } /// diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 77802de..331a08d 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -20,11 +20,13 @@ namespace Encompass { private readonly List engines = new List(); private readonly DirectedGraph engineGraph = GraphBuilder.DirectedGraph(); + private readonly ComponentStore startingComponentStoreForComponentManager = new ComponentStore(); + private readonly ComponentStore startingComponentStoreForComponentUpdateManager = new ComponentStore(); private readonly ComponentManager componentManager; private readonly EntityManager entityManager; private readonly MessageManager messageManager; - private readonly ComponentMessageManager componentMessageManager; + private readonly ComponentUpdateManager componentUpdateManager; private readonly TimeManager timeManager; private readonly DrawLayerManager drawLayerManager; private readonly RenderManager renderManager; @@ -39,11 +41,11 @@ namespace Encompass { drawLayerManager = new DrawLayerManager(); timeManager = new TimeManager(); - componentManager = new ComponentManager(drawLayerManager); + componentUpdateManager = new ComponentUpdateManager(); + componentManager = new ComponentManager(drawLayerManager, componentUpdateManager); messageManager = new MessageManager(timeManager); - componentMessageManager = new ComponentMessageManager(); - entityManager = new EntityManager(componentManager, componentMessageManager); - renderManager = new RenderManager(componentManager, drawLayerManager, entityManager); + entityManager = new EntityManager(componentManager); + renderManager = new RenderManager(drawLayerManager); } /// @@ -67,25 +69,28 @@ namespace Encompass /// public void SendMessage(TMessage message, double time) where TMessage : struct, IMessage { - messageManager.AddMessageDelayed(message, time); + messageManager.AddMessage(message, time); } /// /// Sets Component data for the specified Component Type on the specified Entity. /// - public void SetComponent(Entity entity, TComponent component, int priority = 0) where TComponent : struct, IComponent + public void SetComponent(Entity entity, TComponent component) where TComponent : struct, IComponent { - var componentID = componentManager.MarkComponentForWrite(entity, component, priority); + startingComponentStoreForComponentManager.Set(entity, component); + startingComponentStoreForComponentUpdateManager.Set(entity, component); + + RegisterComponent(typeof(TComponent)); + if (component is IDrawableComponent drawableComponent) { - componentManager.RegisterDrawableComponent(componentID, drawableComponent); + componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer); } } internal void RegisterComponent(Type componentType) { registeredComponentTypes.Add(componentType); - AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType))); } /// @@ -97,7 +102,7 @@ namespace Encompass engine.AssignEntityManager(entityManager); engine.AssignComponentManager(componentManager); engine.AssignMessageManager(messageManager); - engine.AssignComponentMessageManager(componentMessageManager); + engine.AssignComponentUpdateManager(componentUpdateManager); engine.AssignTimeManager(timeManager); engines.Add(engine); @@ -106,42 +111,28 @@ namespace Encompass var messageReceiveTypes = engine.receiveTypes; var messageSendTypes = engine.sendTypes; - foreach (var messageType in messageReceiveTypes.Union(messageSendTypes)) + foreach (var writePendingType in engine.writePendingTypes.Intersect(engine.readPendingTypes)) { - messageManager.RegisterMessageType(messageType); + throw new EngineSelfCycleException("Engine {0} both writes and reads pending Component {1}", engine.GetType().Name, writePendingType.Name); } foreach (var messageType in messageReceiveTypes.Intersect(messageSendTypes)) { - if ((messageType.IsGenericType && messageType.GetGenericTypeDefinition() == typeof(PendingComponentMessage<>))) - { - var componentType = messageType.GetGenericArguments().Single(); - throw new EngineSelfCycleException("Engine {0} both activates and reads pending Component {1}", engine.GetType().Name, componentType.Name); - } - throw new EngineSelfCycleException("Engine {0} both receives and sends Message {1}", engine.GetType().Name, messageType.Name); } - if (messageSendTypes.Any()) + if (messageSendTypes.Count > 0 || engine.writePendingTypes.Count > 0) { senders.Add(engine); } - foreach (var receiveType in engine.receiveTypes) + foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readPendingTypes)) { - if (receiveType.IsGenericType) - { - var genericTypeDefinition = receiveType.GetGenericTypeDefinition(); - if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(PendingComponentMessage<>)) - { - var componentType = receiveType.GetGenericArguments().Single(); - if (!registeredComponentTypes.Contains(componentType)) - { - RegisterComponent(componentType); - } - } - } + RegisterComponent(componentType); + } + foreach (var receiveType in engine.receiveTypes.Union(engine.readPendingTypes)) + { if (!typeToReaders.ContainsKey(receiveType)) { typeToReaders.Add(receiveType, new HashSet()); @@ -184,7 +175,7 @@ namespace Encompass { foreach (var senderEngine in senders) { - foreach (var messageType in senderEngine.sendTypes) + foreach (var messageType in senderEngine.sendTypes.Union(senderEngine.writePendingTypes)) { if (typeToReaders.ContainsKey(messageType)) { @@ -243,16 +234,12 @@ namespace Encompass var defaultWritePriorityAttribute = engine.GetType().GetCustomAttribute(false); - var writeTypes = engine.sendTypes.Where((type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ComponentWriteMessage<>)); - - foreach (var writeType in writeTypes) + foreach (var writeType in engine.writeTypes) { - var componentType = writeType.GetGenericArguments()[0]; - int? priority = null; - if (engine.writePriorities.ContainsKey(componentType)) + if (engine.writePriorities.ContainsKey(writeType)) { - priority = engine.writePriorities[componentType]; + priority = engine.writePriorities[writeType]; } else if (defaultWritePriorityAttribute != null) { @@ -261,44 +248,44 @@ namespace Encompass if (priority.HasValue) { - writtenComponentTypesWithPriority.Add(componentType); + writtenComponentTypesWithPriority.Add(writeType); - if (!writePriorities.ContainsKey(componentType)) + if (!writePriorities.ContainsKey(writeType)) { - writePriorities[componentType] = new HashSet(); + writePriorities[writeType] = new HashSet(); } - if (writePriorities[componentType].Contains(priority.Value)) + if (writePriorities[writeType].Contains(priority.Value)) { - duplicateWritesWithSamePriority.Add(componentType); + duplicateWritesWithSamePriority.Add(writeType); } - else if (writtenComponentTypesWithoutPriority.Contains(componentType)) + else if (writtenComponentTypesWithoutPriority.Contains(writeType)) { - duplicateWritesWithoutPriority.Add(componentType); + duplicateWritesWithoutPriority.Add(writeType); } else { - writePriorities[componentType].Add(priority.Value); + writePriorities[writeType].Add(priority.Value); } } else { - if (writtenComponentTypesWithoutPriority.Contains(componentType) || writtenComponentTypesWithPriority.Contains(componentType)) + if (writtenComponentTypesWithoutPriority.Contains(writeType) || writtenComponentTypesWithPriority.Contains(writeType)) { - duplicateWritesWithoutPriority.Add(componentType); + duplicateWritesWithoutPriority.Add(writeType); } else { - writtenComponentTypesWithoutPriority.Add(componentType); + writtenComponentTypesWithoutPriority.Add(writeType); } } - if (!writeMessageToEngines.ContainsKey(componentType)) + if (!writeMessageToEngines.ContainsKey(writeType)) { - writeMessageToEngines[componentType] = new List(); + writeMessageToEngines[writeType] = new List(); } - writeMessageToEngines[componentType].Add(engine); + writeMessageToEngines[writeType].Add(engine); } } @@ -331,6 +318,14 @@ namespace Encompass } var engineOrder = new List(); + + foreach (var registeredComponentType in registeredComponentTypes) + { + var emitterEngine = (Engine)Activator.CreateInstance(typeof(ComponentEmitter<>).MakeGenericType(registeredComponentType)); + AddEngine(emitterEngine); + engineOrder.Add(emitterEngine); + } + foreach (var engine in engineGraph.TopologicalSort()) { engineOrder.Add(engine); @@ -341,13 +336,13 @@ namespace Encompass entityManager, componentManager, messageManager, - componentMessageManager, + componentUpdateManager, timeManager, renderManager ); - componentManager.RemoveMarkedComponents(); - componentManager.WriteComponents(); + componentUpdateManager.SetStartingComponentStore(startingComponentStoreForComponentUpdateManager); + componentManager.SetComponentStore(startingComponentStoreForComponentManager); return world; } diff --git a/encompass-cs/encompass-cs.csproj b/encompass-cs/encompass-cs.csproj index 30aae01..4c9af10 100644 --- a/encompass-cs/encompass-cs.csproj +++ b/encompass-cs/encompass-cs.csproj @@ -3,13 +3,13 @@ netstandard2.0 Encompass EncompassECS.Framework - 0.17.0 + 0.18.0-preview8 Evan Hemsley true Moonside Games Encompass ECS https://github.com/encompass-ecs - + Evan Hemsley 2019 Encompass is an engine-agnostic Hyper ECS framework to help you code games, or other kinds of simulations. true @@ -19,11 +19,10 @@ True - + - - + diff --git a/test/ComponentTest.cs b/test/ComponentTest.cs index e7c5c54..febbd0b 100644 --- a/test/ComponentTest.cs +++ b/test/ComponentTest.cs @@ -1,12 +1,7 @@ -using System.ComponentModel; using NUnit.Framework; using FluentAssertions; using Encompass; -using System.Collections.Generic; -using System; -using System.Linq; -using Encompass.Exceptions; namespace Tests { @@ -89,16 +84,16 @@ namespace Tests worldBuilder.AddEngine(new ReadMockComponentEngine()); var entity = worldBuilder.CreateEntity(); - worldBuilder.SetComponent(entity, new MockComponent { myInt = 20, myString = "what" }, 2); - worldBuilder.SetComponent(entity, new MockComponent { myInt = 50, myString = "hi" }, 0); - worldBuilder.SetComponent(entity, new MockComponent { myInt = 40, myString = "wassup" }, 1); + worldBuilder.SetComponent(entity, new MockComponent { myInt = 20, myString = "what" }); + worldBuilder.SetComponent(entity, new MockComponent { myInt = 50, myString = "hi" }); + worldBuilder.SetComponent(entity, new MockComponent { myInt = 40, myString = "wassup" }); var world = worldBuilder.Build(); world.Update(0.01); - Assert.That(gottenMockComponent.myInt, Is.EqualTo(50)); - Assert.That(gottenMockComponent.myString, Is.EqualTo("hi")); + Assert.That(gottenMockComponent.myInt, Is.EqualTo(40)); + Assert.That(gottenMockComponent.myString, Is.EqualTo("wassup")); } [Reads(typeof(MockComponent))] @@ -267,45 +262,6 @@ namespace Tests Assert.AreEqual(mockComponent, gottenMockComponent); } - - [Receives(typeof(EntityMessage))] - [Reads(typeof(MockComponent))] - class GetMockComponentByRuntimeType : Engine - { - public override void Update(double dt) - { - foreach (var entityMessage in ReadMessages()) - { - 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(entity, mockComponent); - - EntityMessage entityMessage; - entityMessage.entity = entity; - worldBuilder.SendMessage(entityMessage); - - var world = worldBuilder.Build(); - - world.Update(0.01); - - Assert.AreEqual(mockComponent, gottenMockComponent); - } - struct HasComponentTestMessage : IMessage { public Entity entity; @@ -347,6 +303,8 @@ namespace Tests world.Update(0.01); } + static bool hasComponentRuntimeTypeResult; + [Receives(typeof(HasComponentTestMessage))] [Reads(typeof(MockComponent))] class HasComponentWithRuntimeTypeEngine : Engine @@ -355,7 +313,7 @@ namespace Tests { foreach (var hasComponentTestEngine in ReadMessages()) { - Assert.IsTrue(HasComponent(hasComponentTestEngine.entity, typeof(MockComponent))); + hasComponentRuntimeTypeResult = HasComponent(hasComponentTestEngine.entity, typeof(MockComponent)); } } } @@ -381,6 +339,27 @@ namespace Tests var world = worldBuilder.Build(); world.Update(0.01); + + Assert.IsTrue(hasComponentRuntimeTypeResult); + } + + [Test] + public void HasComponentWithRuntimeTypeFalseWhenNoneHaveBeenCreated() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(new HasComponentWithRuntimeTypeEngine()); + + var entity = worldBuilder.CreateEntity(); + + HasComponentTestMessage hasComponentTestMessage; + hasComponentTestMessage.entity = entity; + worldBuilder.SendMessage(hasComponentTestMessage); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + Assert.IsFalse(hasComponentRuntimeTypeResult); } struct RemoveComponentTestMessage : IMessage @@ -431,6 +410,8 @@ namespace Tests } } + static bool hasComponentResult; + [Receives(typeof(CheckHasMockComponentMessage))] [Reads(typeof(MockComponent))] class CheckHasMockComponentEngine : Engine @@ -439,13 +420,13 @@ namespace Tests { foreach (var checkHasMockComponentMessage in ReadMessages()) { - Assert.IsTrue(HasComponent(checkHasMockComponentMessage.entity)); + hasComponentResult = HasComponent(checkHasMockComponentMessage.entity); } } } [Test] - public void RemoveComponent() + public void RemovedComponentShouldStillExistOnSameFrame() { var worldBuilder = new WorldBuilder(); var entity = worldBuilder.CreateEntity(); @@ -467,6 +448,12 @@ namespace Tests var world = worldBuilder.Build(); world.Update(0.01f); + + hasComponentResult.Should().BeTrue(); + + world.Update(0.01f); + + hasComponentResult.Should().BeFalse(); } struct CheckHasMockComponentMessage : IMessage diff --git a/test/EngineTest.cs b/test/EngineTest.cs index a9dbbb4..1edd31f 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -557,7 +557,7 @@ namespace Tests world.Update(0.01); Assert.That(results, Does.Not.Contain((mockComponent, entity))); - Assert.That(results, Does.Not.Contain((mockComponent, entity))); + Assert.That(results, Does.Not.Contain((mockComponent, entityB))); Assert.That(results, Does.Contain((mockComponent, entityC))); } diff --git a/test/GeneralRendererTest.cs b/test/GeneralRendererTest.cs index 8fa01d5..bc1aedc 100644 --- a/test/GeneralRendererTest.cs +++ b/test/GeneralRendererTest.cs @@ -35,6 +35,9 @@ namespace Tests world.Update(0.01f); world.Draw(); + world.Update(0.01); + world.Draw(); + Assert.That(result, Is.EqualTo((aComponent, entity))); } @@ -55,7 +58,9 @@ namespace Tests var world = worldBuilder.Build(); world.Update(0.01f); + world.Draw(); + world.Update(0.01f); world.Draw(); Assert.That(result, Is.EqualTo((aComponent, entity)).Or.EqualTo((aComponentTwo, entityB))); diff --git a/test/WorldBuilderTest.cs b/test/WorldBuilderTest.cs index da93315..8e75f8a 100644 --- a/test/WorldBuilderTest.cs +++ b/test/WorldBuilderTest.cs @@ -223,14 +223,14 @@ namespace Tests [Receives(typeof(SetMessage))] [Writes(typeof(AComponent))] [WritesPending(typeof(AComponent))] - [Encompass.DefaultWritePriority(1)] + [Encompass.DefaultWritePriority(4)] class AEngine : Engine { public override void Update(double dt) { foreach (var setMessage in ReadMessages()) { - 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()) + { + SetComponent(setMessage.entity, new AComponent { myInt = 3 }); + } + } + } + static AComponent resultComponent; [ReadsPending(typeof(AComponent))] @@ -267,6 +281,8 @@ namespace Tests var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new AEngine()); worldBuilder.AddEngine(new BEngine()); + worldBuilder.AddEngine(new CEngine()); + worldBuilder.AddEngine(new ReadComponentEngine()); var entity = worldBuilder.CreateEntity(); worldBuilder.SendMessage(new SetMessage { entity = entity }); @@ -275,7 +291,7 @@ namespace Tests world.Update(0.01); - Assert.That(resultComponent.myInt, Is.EqualTo(0)); + Assert.That(resultComponent.myInt, Is.EqualTo(3)); } }