Merge pull request #3 from thatcosmonaut/component_write_priority
Component write prioritypull/5/head
						commit
						5872e916ac
					
				|  | @ -1,26 +0,0 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Encompass.Exceptions; | ||||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|     public class Activates : Attribute | ||||
|     { | ||||
|         public readonly HashSet<Type> activateTypes = new HashSet<Type>(); | ||||
| 
 | ||||
|         public Activates(params Type[] activateTypes) | ||||
|         { | ||||
|             foreach (var activateType in activateTypes) | ||||
|             { | ||||
|                 var isComponent = activateType.GetInterfaces().Contains(typeof(IComponent)); | ||||
|                 if (!isComponent) | ||||
|                 { | ||||
|                     throw new IllegalActivateTypeException("{0} must be a Component", activateType.Name); | ||||
|                 } | ||||
| 
 | ||||
|                 this.activateTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(activateType)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,27 +0,0 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Encompass.Exceptions; | ||||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|     [AttributeUsage(AttributeTargets.Class)] | ||||
|     public class Updates : Attribute | ||||
|     { | ||||
|         public readonly HashSet<Type> updateTypes = new HashSet<Type>(); | ||||
| 
 | ||||
|         public Updates(params Type[] updateTypes) | ||||
|         { | ||||
|             foreach (var updateType in updateTypes) | ||||
|             { | ||||
|                 var isComponent = updateType.GetInterfaces().Contains(typeof(IComponent)); | ||||
|                 if (!isComponent) | ||||
|                 { | ||||
|                     throw new IllegalUpdateTypeException("{0} must be a Component", updateType.Name); | ||||
|                 } | ||||
| 
 | ||||
|                 this.updateTypes.Add(typeof(ComponentUpdateMessage<>).MakeGenericType(updateType)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,40 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Encompass.Exceptions; | ||||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|     [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] | ||||
|     public class Writes : Attribute | ||||
|     { | ||||
|         public readonly HashSet<Type> writeTypes = new HashSet<Type>(); | ||||
|         public Dictionary<Type, int> priorities = new Dictionary<Type, int>(); | ||||
| 
 | ||||
|         public Writes(params Type[] writeTypes) | ||||
|         { | ||||
|             foreach (var writeType in writeTypes) | ||||
|             { | ||||
|                 var isComponent = writeType.GetInterfaces().Contains(typeof(IComponent)); | ||||
|                 if (!isComponent) | ||||
|                 { | ||||
|                     throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name); | ||||
|                 } | ||||
| 
 | ||||
|                 this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public Writes(Type writeType, int priority) | ||||
|         { | ||||
|             var isComponent = writeType.GetInterfaces().Contains(typeof(IComponent)); | ||||
|             if (!isComponent) | ||||
|             { | ||||
|                 throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name); | ||||
|             } | ||||
| 
 | ||||
|             this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType)); | ||||
|             this.priorities.Add(writeType, priority); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,26 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Encompass.Exceptions; | ||||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|     public class WritesPending : Attribute | ||||
|     { | ||||
|         public readonly HashSet<Type> writePendingTypes = new HashSet<Type>(); | ||||
| 
 | ||||
|         public WritesPending(params Type[] writePendingTypes) | ||||
|         { | ||||
|             foreach (var writePendingType in writePendingTypes) | ||||
|             { | ||||
|                 var isComponent = writePendingType.GetInterfaces().Contains(typeof(IComponent)); | ||||
|                 if (!isComponent) | ||||
|                 { | ||||
|                     throw new IllegalWritePendingTypeException("{0} must be a Component", writePendingType.Name); | ||||
|                 } | ||||
| 
 | ||||
|                 this.writePendingTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(writePendingType)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -19,10 +19,10 @@ namespace Encompass | |||
| 
 | ||||
|         private readonly Dictionary<Type, HashSet<Guid>> typeToComponentIDs = new Dictionary<Type, HashSet<Guid>>(); | ||||
| 
 | ||||
|         private readonly List<(Entity, Type, Guid, IComponent)> componentAddData = new List<(Entity, Type, Guid, IComponent)>(); | ||||
|         private readonly HashSet<Guid> componentIDsMarkedForAdd = new HashSet<Guid>(); | ||||
|         private readonly Dictionary<(Entity, Type), (Guid, IComponent)> componentWriteData = new Dictionary<(Entity, Type), (Guid, IComponent)>(); | ||||
|         private readonly Dictionary<(Entity, Type), int> componentWritePriorities = new Dictionary<(Entity, Type), int>(); | ||||
|         private readonly HashSet<Guid> componentIDsMarkedForWrite = new HashSet<Guid>(); | ||||
|         private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>(); | ||||
|         private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>(); | ||||
| 
 | ||||
|         public ComponentManager(DrawLayerManager drawLayerManager) | ||||
|         { | ||||
|  | @ -40,26 +40,46 @@ namespace Encompass | |||
|             return Guid.NewGuid(); | ||||
|         } | ||||
| 
 | ||||
|         internal Guid MarkComponentForAdd<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent | ||||
|         internal Guid MarkComponentForWrite<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent | ||||
|         { | ||||
|             var id = NextID(); | ||||
|             componentAddData.Add((entity, typeof(TComponent), id, component)); | ||||
|             componentIDsMarkedForAdd.Add(id); | ||||
|             Guid id; | ||||
|             if (EntityHasComponentOfType<TComponent>(entity)) | ||||
|             { | ||||
|                 id = GetComponentByEntityAndType<TComponent>(entity).Item1; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 id = NextID(); | ||||
|             } | ||||
| 
 | ||||
|             if (componentWriteData.ContainsKey((entity, typeof(TComponent)))) | ||||
|             { | ||||
|                 var currentPriority = componentWritePriorities[(entity, typeof(TComponent))]; | ||||
|                 if (priority < currentPriority) | ||||
|                 { | ||||
|                     componentWriteData[(entity, typeof(TComponent))] = (id, component); | ||||
|                     componentWritePriorities[(entity, typeof(TComponent))] = priority; | ||||
|                     componentIDsMarkedForWrite.Add(id); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 componentWriteData.Add((entity, typeof(TComponent)), (id, component)); | ||||
|                 componentWritePriorities[(entity, typeof(TComponent))] = priority; | ||||
|                 componentIDsMarkedForWrite.Add(id); | ||||
|             } | ||||
| 
 | ||||
|             return id; | ||||
|         } | ||||
| 
 | ||||
|         internal Guid MarkDrawComponentForAdd<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent | ||||
|         internal Guid MarkDrawComponentForWrite<TComponent>(Entity entity, TComponent component, int priority, int layer = 0) where TComponent : struct, IComponent | ||||
|         { | ||||
|             var id = MarkComponentForAdd(entity, component); | ||||
|             var id = MarkComponentForWrite(entity, component, priority); | ||||
|             drawLayerManager.RegisterComponentWithLayer(id, layer); | ||||
|             return id; | ||||
|         } | ||||
| 
 | ||||
|         internal void AddComponent(Entity entity, Type type, Guid componentID, IComponent component) | ||||
|         { | ||||
|             if (!entityIDToComponentTypeToComponentID.ContainsKey(entity.ID)) { return; } | ||||
| 
 | ||||
|             if (!entityIDToComponentTypeToComponentID[entity.ID].ContainsKey(type)) | ||||
|         { | ||||
|             IDToComponent[componentID] = component; | ||||
|             componentIDToEntityID[componentID] = entity.ID; | ||||
|  | @ -72,21 +92,34 @@ namespace Encompass | |||
|             typeToComponentIDs[type].Add(componentID); | ||||
|             entityIDToComponentIDs[entity.ID].Add(componentID); | ||||
|         } | ||||
|             else | ||||
| 
 | ||||
|         internal void UpdateComponent(Guid componentID, IComponent component) | ||||
|         { | ||||
|                 throw new MultipleComponentOfSameTypeException("Entity {0} cannot have multiple components of type {1}", entity.ID, type.Name); | ||||
|             } | ||||
|             IDToComponent[componentID] = component; | ||||
|         } | ||||
| 
 | ||||
|         internal void AddMarkedComponents() | ||||
|         internal void WriteComponents() | ||||
|         { | ||||
|             foreach (var (entity, type, componentID, component) in componentAddData) | ||||
|             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); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             componentAddData.Clear(); | ||||
|             componentIDsMarkedForAdd.Clear(); | ||||
|             componentWriteData.Clear(); | ||||
|             componentIDsMarkedForWrite.Clear(); | ||||
|             componentWritePriorities.Clear(); | ||||
|         } | ||||
| 
 | ||||
|         internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID) | ||||
|  | @ -133,38 +166,39 @@ namespace Encompass | |||
|         } | ||||
| 
 | ||||
|         internal IComponent GetComponentByID(Guid componentID) | ||||
|         { | ||||
|             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]; | ||||
|             } | ||||
| 
 | ||||
|         internal void AddUpdateComponentOperation<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent | ||||
|             else | ||||
|             { | ||||
|             if (pendingUpdates.ContainsKey(componentID)) | ||||
|             { | ||||
|                 throw new RepeatUpdateComponentException("Component {0} with ID {1} was updated multiple times this frame", typeof(TComponent).Name, componentID); | ||||
|                 throw new ComponentNotFoundException("Component with ID {0} does not exist.", componentID); | ||||
|             } | ||||
| 
 | ||||
|             pendingUpdates.Add(componentID, newComponentValue); | ||||
|         } | ||||
| 
 | ||||
|         internal void PerformComponentUpdates() | ||||
|         { | ||||
|             foreach (var idPair in pendingUpdates) | ||||
|             { | ||||
|                 IDToComponent[idPair.Key] = idPair.Value; | ||||
|             } | ||||
| 
 | ||||
|             pendingUpdates.Clear(); | ||||
|         } | ||||
| 
 | ||||
|         internal void MarkAllComponentsOnEntityForRemoval(Guid entityID) | ||||
|  | @ -184,11 +218,12 @@ namespace Encompass | |||
|         { | ||||
|             foreach (var componentID in componentsMarkedForRemoval) | ||||
|             { | ||||
|                 if (componentIDsMarkedForAdd.Contains(componentID)) | ||||
|                 if (componentIDsMarkedForWrite.Contains(componentID)) | ||||
|                 { | ||||
|                     componentIDsMarkedForAdd.Remove(componentID); | ||||
|                     componentIDsMarkedForWrite.Remove(componentID); | ||||
|                 } | ||||
|                 else | ||||
|                  | ||||
|                 if (IDToComponent.ContainsKey(componentID)) | ||||
|                 { | ||||
|                     Remove(componentID); | ||||
|                 } | ||||
|  |  | |||
|  | @ -21,10 +21,13 @@ namespace Encompass | |||
|         private readonly Dictionary<Entity, PooledDictionary<Type, Guid>> entityToTypeToPendingComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>(); | ||||
|         private readonly Dictionary<Entity, PooledDictionary<Type, Guid>> entityToTypeToComponentID = new Dictionary<Entity, PooledDictionary<Type, Guid>>(); | ||||
| 
 | ||||
|         private readonly Dictionary<Entity, PooledDictionary<Type, int>> entityToTypeToPendingComponentPriority = new Dictionary<Entity, PooledDictionary<Type, int>>(); | ||||
| 
 | ||||
|         internal void RegisterEntity(Entity entity) | ||||
|         { | ||||
|             entityToTypeToComponentID[entity] = new PooledDictionary<Type, Guid>(); | ||||
|             entityToTypeToPendingComponentID[entity] = new PooledDictionary<Type, Guid>(); | ||||
|             entityToTypeToPendingComponentPriority[entity] = new PooledDictionary<Type, int>(); | ||||
|             entityToTypeToExistingComponentID[entity] = new PooledDictionary<Type, Guid>(); | ||||
|         } | ||||
| 
 | ||||
|  | @ -36,6 +39,9 @@ namespace Encompass | |||
|             entityToTypeToPendingComponentID[entity].Dispose(); | ||||
|             entityToTypeToPendingComponentID.Remove(entity); | ||||
| 
 | ||||
|             entityToTypeToPendingComponentPriority[entity].Dispose(); | ||||
|             entityToTypeToPendingComponentPriority.Remove(entity); | ||||
| 
 | ||||
|             entityToTypeToExistingComponentID[entity].Dispose(); | ||||
|             entityToTypeToExistingComponentID.Remove(entity); | ||||
|         } | ||||
|  | @ -75,6 +81,11 @@ namespace Encompass | |||
|             { | ||||
|                 dictionary.Clear(); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var dictionary in entityToTypeToPendingComponentPriority.Values) | ||||
|             { | ||||
|                 dictionary.Clear(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal void AddExistingComponentMessage<TComponent>(ComponentMessage<TComponent> componentMessage) where TComponent : struct, IComponent | ||||
|  | @ -107,15 +118,21 @@ namespace Encompass | |||
|                 componentMessageTypeToPendingComponentIDs.Add(typeof(TComponent), new HashSet<Guid>()); | ||||
|             } | ||||
| 
 | ||||
|             componentMessageTypeToPendingComponentIDs[typeof(TComponent)].Add(pendingComponentMessage.componentID); | ||||
| 
 | ||||
|             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 | ||||
|             { | ||||
|                 throw new MultipleComponentOfSameTypeException("Entity {0} cannot have multiple components of type {1}", pendingComponentMessage.entity.ID, typeof(TComponent).Name); | ||||
|                 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); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -259,18 +276,39 @@ namespace Encompass | |||
|         } | ||||
| 
 | ||||
|         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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ namespace Encompass | |||
|     { | ||||
|         internal readonly HashSet<Type> sendTypes = new HashSet<Type>(); | ||||
|         internal readonly HashSet<Type> receiveTypes = new HashSet<Type>(); | ||||
|         internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>(); | ||||
| 
 | ||||
|         private EntityManager entityManager; | ||||
|         private MessageManager messageManager; | ||||
|  | @ -24,16 +25,17 @@ namespace Encompass | |||
|                 sendTypes = sendsAttribute.sendTypes; | ||||
|             } | ||||
| 
 | ||||
|             var activatesAttribute = GetType().GetCustomAttribute<Activates>(false); | ||||
|             var activatesAttribute = GetType().GetCustomAttribute<WritesPending>(false); | ||||
|             if (activatesAttribute != null) | ||||
|             { | ||||
|                 sendTypes.UnionWith(activatesAttribute.activateTypes); | ||||
|                 sendTypes.UnionWith(activatesAttribute.writePendingTypes); | ||||
|             } | ||||
| 
 | ||||
|             var updatesAttribute = GetType().GetCustomAttribute<Updates>(false); | ||||
|             if (updatesAttribute != null) | ||||
|             var writesAttributes = GetType().GetCustomAttributes<Writes>(false); | ||||
|             foreach (var writesAttribute in writesAttributes) | ||||
|             { | ||||
|                 sendTypes.UnionWith(updatesAttribute.updateTypes); | ||||
|                 sendTypes.UnionWith(writesAttribute.writeTypes); | ||||
|                 writePriorities = new Dictionary<Type, int>[2] { writePriorities, writesAttribute.priorities }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value); | ||||
|             } | ||||
| 
 | ||||
|             var receivesAttribute = GetType().GetCustomAttribute<Receives>(false); | ||||
|  | @ -92,18 +94,34 @@ namespace Encompass | |||
|             return entityManager.GetEntity(entityID); | ||||
|         } | ||||
| 
 | ||||
|         protected Guid GetEntityIDByComponentID(Guid componentID) | ||||
|         protected Guid GetEntityIDByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent | ||||
|         { | ||||
|             var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); | ||||
|             var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); | ||||
| 
 | ||||
|             if (!pendingRead && !existingRead) | ||||
|             { | ||||
|                 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); | ||||
|             } | ||||
| 
 | ||||
|             return componentMessageManager.GetEntityIDByComponentID(componentID); | ||||
|         } | ||||
| 
 | ||||
|         protected Entity GetEntityByComponentID(Guid componentID) | ||||
|         protected Entity GetEntityByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent | ||||
|         { | ||||
|             return GetEntity(GetEntityIDByComponentID(componentID)); | ||||
|             return GetEntity(GetEntityIDByComponentID<TComponent>(componentID)); | ||||
|         } | ||||
| 
 | ||||
|         protected TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent | ||||
|         { | ||||
|             var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); | ||||
|             var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); | ||||
| 
 | ||||
|             if (!pendingRead && !existingRead) | ||||
|             { | ||||
|                 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name); | ||||
|             } | ||||
| 
 | ||||
|             if (componentMessageManager.GetComponentTypeByID(componentID) != typeof(TComponent)) | ||||
|             { | ||||
|                 throw new ComponentTypeMismatchException("Expected Component to be of type {0} but was actually of type {1}", typeof(TComponent).Name, componentMessageManager.GetComponentTypeByID(componentID).Name); | ||||
|  | @ -124,40 +142,6 @@ namespace Encompass | |||
|             return GetEntity(componentManager.GetEntityIDByComponentID(componentID)); | ||||
|         } | ||||
| 
 | ||||
|         protected Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent | ||||
|         { | ||||
|             var componentID = componentManager.MarkComponentForAdd(entity, component); | ||||
| 
 | ||||
|             if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>))) | ||||
|             { | ||||
|                 PendingComponentMessage<TComponent> newComponentMessage; | ||||
|                 newComponentMessage.entity = entity; | ||||
|                 newComponentMessage.componentID = componentID; | ||||
|                 newComponentMessage.component = component; | ||||
|                 SendMessage(newComponentMessage); | ||||
|                 SendPendingComponentMessage(newComponentMessage); | ||||
|             } | ||||
| 
 | ||||
|             return componentID; | ||||
|         } | ||||
| 
 | ||||
|         protected Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent | ||||
|         { | ||||
|             var componentID = componentManager.MarkDrawComponentForAdd(entity, component, layer); | ||||
| 
 | ||||
|             if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>))) | ||||
|             { | ||||
|                 PendingComponentMessage<TComponent> newComponentMessage; | ||||
|                 newComponentMessage.entity = entity; | ||||
|                 newComponentMessage.componentID = componentID; | ||||
|                 newComponentMessage.component = component; | ||||
|                 SendMessage(newComponentMessage); | ||||
|                 SendPendingComponentMessage(newComponentMessage); | ||||
|             } | ||||
| 
 | ||||
|             return componentID; | ||||
|         } | ||||
| 
 | ||||
|         protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent | ||||
|         { | ||||
|             var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); | ||||
|  | @ -280,22 +264,57 @@ namespace Encompass | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent | ||||
|         protected Guid SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent | ||||
|         { | ||||
|             componentManager.AddUpdateComponentOperation(componentID, newComponent); | ||||
|             var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0; | ||||
| 
 | ||||
|             var componentID = componentManager.MarkComponentForWrite(entity, component, priority); | ||||
| 
 | ||||
|             if (!sendTypes.Contains(typeof(ComponentWriteMessage<TComponent>))) | ||||
|             { | ||||
|                 throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name); | ||||
|             } | ||||
| 
 | ||||
|         protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent | ||||
|             if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>))) | ||||
|             { | ||||
|             if (!sendTypes.Contains(typeof(ComponentUpdateMessage<TComponent>))) | ||||
|             { | ||||
|                 throw new IllegalUpdateException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name); | ||||
|                 PendingComponentMessage<TComponent> newComponentMessage; | ||||
|                 newComponentMessage.entity = entity; | ||||
|                 newComponentMessage.componentID = componentID; | ||||
|                 newComponentMessage.component = component; | ||||
|                 newComponentMessage.priority = priority; | ||||
|                 SendPendingComponentMessage(newComponentMessage); | ||||
|             } | ||||
| 
 | ||||
|             ComponentUpdateMessage<TComponent> componentUpdateMessage; | ||||
|             componentUpdateMessage.componentID = componentID; | ||||
|             componentUpdateMessage.component = newComponentValue; | ||||
|             SendMessage(componentUpdateMessage); | ||||
|             return componentID; | ||||
|         } | ||||
| 
 | ||||
|         protected Guid SetComponent<TComponent>(Guid componentID, TComponent component) where TComponent : struct, IComponent | ||||
|         { | ||||
|             return SetComponent(GetEntityByComponentID<TComponent>(componentID), component); | ||||
|         } | ||||
| 
 | ||||
|         protected Guid SetDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent | ||||
|         { | ||||
|             var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0; | ||||
| 
 | ||||
|             var componentID = componentManager.MarkDrawComponentForWrite(entity, component, priority, layer); | ||||
| 
 | ||||
|             if (!sendTypes.Contains(typeof(ComponentWriteMessage<TComponent>))) | ||||
|             { | ||||
|                 throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", GetType().Name, typeof(TComponent).Name); | ||||
|             } | ||||
| 
 | ||||
|             if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>))) | ||||
|             { | ||||
|                 PendingComponentMessage<TComponent> newComponentMessage; | ||||
|                 newComponentMessage.entity = entity; | ||||
|                 newComponentMessage.componentID = componentID; | ||||
|                 newComponentMessage.component = component; | ||||
|                 newComponentMessage.priority = priority; | ||||
|                 SendPendingComponentMessage(newComponentMessage); | ||||
|             } | ||||
| 
 | ||||
|             return componentID; | ||||
|         } | ||||
| 
 | ||||
|         protected void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage | ||||
|  |  | |||
|  | @ -1,18 +0,0 @@ | |||
| namespace Encompass.Engines | ||||
| { | ||||
|     internal class ComponentUpdater<TComponent> : Engine where TComponent : struct, IComponent | ||||
|     { | ||||
|         public ComponentUpdater() : base() | ||||
|         { | ||||
|             receiveTypes.Add(typeof(ComponentUpdateMessage<TComponent>)); | ||||
|         } | ||||
| 
 | ||||
|         public override void Update(double dt) | ||||
|         { | ||||
|             foreach (var componentUpdateMessage in ReadMessages<ComponentUpdateMessage<TComponent>>()) | ||||
|             { | ||||
|                 UpdateComponentInWorld(componentUpdateMessage.componentID, componentUpdateMessage.component); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,5 +1,7 @@ | |||
| using System.Linq; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Encompass.Exceptions; | ||||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|  | @ -34,9 +36,16 @@ namespace Encompass | |||
|         } | ||||
| 
 | ||||
|         public Entity GetEntity(Guid id) | ||||
|         { | ||||
|             if (IDToEntity.ContainsKey(id)) | ||||
|             { | ||||
|                 return IDToEntity[id]; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 throw new EntityNotFoundException("Entity with ID {0} does not exist.", id); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public void MarkForDestroy(Guid entityID) | ||||
|         { | ||||
|  |  | |||
|  | @ -2,9 +2,9 @@ using System; | |||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class IllegalUpdateTypeException : Exception | ||||
|     public class ComponentNotFoundException : Exception | ||||
|     { | ||||
|         public IllegalUpdateTypeException( | ||||
|         public ComponentNotFoundException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|  | @ -2,9 +2,9 @@ using System; | |||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class EngineUpdateConflictException : Exception | ||||
|     public class EngineWriteConflictException : Exception | ||||
|     { | ||||
|         public EngineUpdateConflictException( | ||||
|         public EngineWriteConflictException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|  |  | |||
|  | @ -2,9 +2,9 @@ using System; | |||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class IllegalActivateException : Exception | ||||
|     public class EntityNotFoundException : Exception | ||||
|     { | ||||
|         public IllegalActivateException( | ||||
|         public EntityNotFoundException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|  | @ -2,9 +2,9 @@ using System; | |||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class IllegalUpdateException : Exception | ||||
|     public class IllegalWriteException : Exception | ||||
|     { | ||||
|         public IllegalUpdateException( | ||||
|         public IllegalWriteException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|  | @ -2,9 +2,9 @@ using System; | |||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class IllegalActivateTypeException : Exception | ||||
|     public class IllegalWritePendingException : Exception | ||||
|     { | ||||
|         public IllegalActivateTypeException( | ||||
|         public IllegalWritePendingException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|  | @ -0,0 +1,12 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class IllegalWritePendingTypeException : Exception | ||||
|     { | ||||
|         public IllegalWritePendingTypeException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class IllegalWriteTypeException : Exception | ||||
|     { | ||||
|         public IllegalWriteTypeException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|     } | ||||
| } | ||||
|  | @ -2,7 +2,7 @@ using System; | |||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|     internal struct ComponentUpdateMessage<TComponent> : IMessage where TComponent : struct, IComponent | ||||
|     internal struct ComponentWriteMessage<TComponent> : IMessage where TComponent : struct, IComponent | ||||
|     { | ||||
|         public Guid componentID; | ||||
|         public TComponent component; | ||||
|  | @ -7,5 +7,6 @@ namespace Encompass | |||
|         public Entity entity; | ||||
|         public Guid componentID; | ||||
|         public TComponent component; | ||||
|         public int priority; | ||||
|     } | ||||
| } | ||||
|  | @ -41,9 +41,8 @@ namespace Encompass | |||
|             componentMessageManager.ClearMessages(); | ||||
|             entityManager.DestroyMarkedEntities(); | ||||
| 
 | ||||
|             componentManager.PerformComponentUpdates(); | ||||
|             componentManager.RemoveMarkedComponents(); | ||||
|             componentManager.AddMarkedComponents(); | ||||
|             componentManager.WriteComponents(); | ||||
|         } | ||||
| 
 | ||||
|         public void Draw() | ||||
|  |  | |||
|  | @ -50,14 +50,14 @@ namespace Encompass | |||
|             messageManager.AddMessageDelayed(message, time); | ||||
|         } | ||||
| 
 | ||||
|         public Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent | ||||
|         public Guid SetComponent<TComponent>(Entity entity, TComponent component, int priority = 0) where TComponent : struct, IComponent | ||||
|         { | ||||
|             return componentManager.MarkComponentForAdd(entity, component); | ||||
|             return componentManager.MarkComponentForWrite(entity, component, priority); | ||||
|         } | ||||
| 
 | ||||
|         public Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent | ||||
|         public Guid SetDrawComponent<TComponent>(Entity entity, TComponent component, int priority = 0, int layer = 0) where TComponent : struct, IComponent | ||||
|         { | ||||
|             return componentManager.MarkDrawComponentForAdd(entity, component, layer); | ||||
|             return componentManager.MarkDrawComponentForWrite(entity, component, priority, layer); | ||||
|         } | ||||
| 
 | ||||
|         internal void RegisterComponent(Type componentType) | ||||
|  | @ -66,11 +66,6 @@ namespace Encompass | |||
|             AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType))); | ||||
|         } | ||||
| 
 | ||||
|         internal void RegisterNewComponentUpdater(Type componentType) | ||||
|         { | ||||
|             AddEngine((Engine)Activator.CreateInstance(typeof(ComponentUpdater<>).MakeGenericType(componentType))); | ||||
|         } | ||||
| 
 | ||||
|         public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine | ||||
|         { | ||||
|             engine.AssignEntityManager(entityManager); | ||||
|  | @ -128,19 +123,6 @@ namespace Encompass | |||
|                 typeToReaders[receiveType].Add(engine); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var sendType in engine.sendTypes) | ||||
|             { | ||||
|                 if (sendType.IsGenericType) | ||||
|                 { | ||||
|                     var genericTypeDefinition = sendType.GetGenericTypeDefinition(); | ||||
|                     if (genericTypeDefinition == typeof(ComponentUpdateMessage<>)) | ||||
|                     { | ||||
|                         var componentType = sendType.GetGenericArguments().Single(); | ||||
|                         RegisterNewComponentUpdater(componentType); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return engine; | ||||
|         } | ||||
| 
 | ||||
|  | @ -201,45 +183,92 @@ namespace Encompass | |||
|                 throw new EngineCycleException(errorString); | ||||
|             } | ||||
| 
 | ||||
|             var mutatedComponentTypes = new HashSet<Type>(); | ||||
|             var duplicateMutations = new List<Type>(); | ||||
|             var updateMessageToEngines = new Dictionary<Type, List<Engine>>(); | ||||
|             var writtenComponentTypesWithoutPriority = new HashSet<Type>(); | ||||
|             var writtenComponentTypesWithPriority = new HashSet<Type>(); | ||||
|             var duplicateWritesWithoutPriority = new List<Type>(); | ||||
|             var duplicateWritesWithSamePriority = new List<Type>(); | ||||
|             var writePriorities = new Dictionary<Type, HashSet<int>>(); | ||||
|             var writeMessageToEngines = new Dictionary<Type, List<Engine>>(); | ||||
| 
 | ||||
|             foreach (var engine in engines) | ||||
|             { | ||||
|                 var updateTypes = engine.sendTypes.Where((type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ComponentUpdateMessage<>)); | ||||
|                 var writeTypes = engine.sendTypes.Where((type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ComponentWriteMessage<>)); | ||||
| 
 | ||||
|                 foreach (var updateType in updateTypes) | ||||
|                 foreach (var writeType in writeTypes) | ||||
|                 { | ||||
|                     if (mutatedComponentTypes.Contains(updateType)) | ||||
|                     var componentType = writeType.GetGenericArguments()[0]; | ||||
| 
 | ||||
|                     if (engine.writePriorities.ContainsKey(componentType)) | ||||
|                     { | ||||
|                         duplicateMutations.Add(updateType); | ||||
|                         var priority = engine.writePriorities[componentType]; | ||||
| 
 | ||||
|                         writtenComponentTypesWithPriority.Add(componentType); | ||||
| 
 | ||||
|                         if (!writePriorities.ContainsKey(componentType)) | ||||
|                         { | ||||
|                             writePriorities[componentType] = new HashSet<int>(); | ||||
|                         } | ||||
| 
 | ||||
|                         if (writePriorities[componentType].Contains(priority)) | ||||
|                         { | ||||
|                             duplicateWritesWithSamePriority.Add(componentType); | ||||
|                         } | ||||
|                         else if (writtenComponentTypesWithoutPriority.Contains(componentType)) | ||||
|                         { | ||||
|                             duplicateWritesWithoutPriority.Add(componentType); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                         mutatedComponentTypes.Add(updateType); | ||||
|                             writePriorities[componentType].Add(priority); | ||||
|                         } | ||||
| 
 | ||||
|                     if (!updateMessageToEngines.ContainsKey(updateType)) | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         updateMessageToEngines[updateType] = new List<Engine>(); | ||||
|                     } | ||||
| 
 | ||||
|                     updateMessageToEngines[updateType].Add(engine); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (duplicateMutations.Count > 0) | ||||
|                         if (writtenComponentTypesWithoutPriority.Contains(componentType) || writtenComponentTypesWithPriority.Contains(componentType)) | ||||
|                         { | ||||
|                 var errorString = "Multiple Engines update the same Component: "; | ||||
|                 foreach (var componentType in duplicateMutations) | ||||
|                             duplicateWritesWithoutPriority.Add(componentType); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             writtenComponentTypesWithoutPriority.Add(componentType); | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     if (!writeMessageToEngines.ContainsKey(componentType)) | ||||
|                     { | ||||
|                         writeMessageToEngines[componentType] = new List<Engine>(); | ||||
|                     } | ||||
| 
 | ||||
|                     writeMessageToEngines[componentType].Add(engine); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (duplicateWritesWithoutPriority.Count > 0) | ||||
|             { | ||||
|                 var errorString = "Multiple Engines write the same Component without declaring priority: "; | ||||
|                 foreach (var componentType in duplicateWritesWithoutPriority) | ||||
|                 { | ||||
|                     errorString += "\n" + | ||||
|                     componentType.Name + " updated by: " + | ||||
|                     string.Join(", ", updateMessageToEngines[componentType].Select((engine) => engine.GetType().Name)); | ||||
|                     componentType.Name + " written by: " + | ||||
|                     string.Join(", ", writeMessageToEngines[componentType].Select((engine) => engine.GetType().Name)); | ||||
|                 } | ||||
|                 errorString += "\nTo resolve the conflict, add priority arguments to the Writes declarations."; | ||||
| 
 | ||||
|                 throw new EngineWriteConflictException(errorString); | ||||
|             } | ||||
| 
 | ||||
|                 throw new EngineUpdateConflictException(errorString); | ||||
|             if (duplicateWritesWithSamePriority.Count > 0) | ||||
|             { | ||||
|                 var errorString = "Multiple Engines write the same Component with the same priority: "; | ||||
|                 foreach (var componentType in duplicateWritesWithSamePriority) | ||||
|                 { | ||||
|                     errorString += "\n" + | ||||
|                     componentType.Name + " written by: " + | ||||
|                     string.Join(", ", writeMessageToEngines[componentType].Select(engine => engine.GetType().Name)); | ||||
|                 } | ||||
|                 errorString += "\nTo resolve the conflict, add priority arguments to the Writes declarations."; | ||||
| 
 | ||||
|                 throw new EngineWriteConflictException(errorString); | ||||
|             } | ||||
| 
 | ||||
|             var engineOrder = new List<Engine>(); | ||||
|  | @ -257,9 +286,8 @@ namespace Encompass | |||
|                 renderManager | ||||
|             ); | ||||
| 
 | ||||
|             componentManager.PerformComponentUpdates(); | ||||
|             componentManager.RemoveMarkedComponents(); | ||||
|             componentManager.AddMarkedComponents(); | ||||
|             componentManager.WriteComponents(); | ||||
| 
 | ||||
|             return world; | ||||
|         } | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
|     <TargetFramework>netstandard2.0</TargetFramework> | ||||
|     <RootNamespace>Encompass</RootNamespace> | ||||
|     <PackageId>EncompassECS.Framework</PackageId> | ||||
|     <Version>0.13.0-rc2</Version> | ||||
|     <Version>0.13.0-rc7</Version> | ||||
|     <Authors>Evan Hemsley</Authors> | ||||
|     <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
|     <Company>Moonside Games</Company> | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| using System.ComponentModel; | ||||
| using NUnit.Framework; | ||||
| using FluentAssertions; | ||||
| 
 | ||||
|  | @ -11,7 +12,7 @@ namespace Tests | |||
| { | ||||
|     public class ComponentTests | ||||
|     { | ||||
|         struct MockComponent : IComponent | ||||
|         struct MockComponent : Encompass.IComponent | ||||
|         { | ||||
|             public string myString; | ||||
|             public int myInt; | ||||
|  | @ -22,7 +23,6 @@ namespace Tests | |||
|             public Entity entity; | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<(Guid, MockComponent)> gottenMockComponentIDPairs = Enumerable.Empty<(Guid, MockComponent)>(); | ||||
|         static (Guid, MockComponent) gottenMockComponentIDPair; | ||||
| 
 | ||||
|         [Receives(typeof(EntityMessage))] | ||||
|  | @ -70,7 +70,7 @@ namespace Tests | |||
|             mockComponent.myInt = 3; | ||||
|             mockComponent.myString = "hello"; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             AddComponentTestMessage addComponentTestMessage; | ||||
|             addComponentTestMessage.entity = entity; | ||||
|  | @ -83,58 +83,76 @@ namespace Tests | |||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void AddMultipleComponentOfSameTypeToEntity() | ||||
|         public void SetMultipleComponentOfSameTypeOnEntity() | ||||
|         { | ||||
|             var worldBuilder = new WorldBuilder(); | ||||
| 
 | ||||
|             MockComponent mockComponent; | ||||
|             mockComponent.myInt = 3; | ||||
|             mockComponent.myString = "hello"; | ||||
|             worldBuilder.AddEngine(new ReadMockComponentEngine()); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             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); | ||||
| 
 | ||||
|             Assert.Throws<MultipleComponentOfSameTypeException>(() => worldBuilder.Build()); | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|             world.Update(0.01); | ||||
| 
 | ||||
|             Assert.That(gottenMockComponentIDPair.Item2.myInt, Is.EqualTo(50)); | ||||
|             Assert.That(gottenMockComponentIDPair.Item2.myString, Is.EqualTo("hi")); | ||||
|         } | ||||
| 
 | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         class MultipleAddEngine : Engine | ||||
|         [WritesPending(typeof(MockComponent))] | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         class OverwriteEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 foreach (var (mockComponentID, mockComponent) in ReadComponents<MockComponent>()) | ||||
|                 { | ||||
|                     var entity = GetEntityByComponentID(mockComponentID); | ||||
|                     var entity = GetEntityByComponentID<MockComponent>(mockComponentID); | ||||
| 
 | ||||
|                     AddComponent(entity, new MockComponent()); | ||||
|                     SetComponent(entity, new MockComponent { myInt = 420 }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [ReadsPending(typeof(MockComponent))] | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         class ReadMockComponentEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 gottenMockComponentIDPair = ReadComponent<MockComponent>(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void EngineAddMultipleComponentOfSameTypeToEntity() | ||||
|         public void EngineOverwriteComponent() | ||||
|         { | ||||
|             var worldBuilder = new WorldBuilder(); | ||||
|             worldBuilder.AddEngine(new MultipleAddEngine()); | ||||
|             worldBuilder.AddEngine(new OverwriteEngine()); | ||||
|             worldBuilder.AddEngine(new ReadMockComponentEngine()); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, new MockComponent()); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent {}); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
|             world.Update(0.01); | ||||
| 
 | ||||
|             Assert.Throws<MultipleComponentOfSameTypeException>(() => world.Update(0.01)); | ||||
|             Assert.That(gottenMockComponentIDPair.Item2.myInt, Is.EqualTo(420)); | ||||
|         } | ||||
| 
 | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         class AddAndRemoveComponentEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 foreach (var (mockComponentID, mockComponent) in ReadComponents<MockComponent>()) | ||||
|                 { | ||||
|                     var entity = GetEntityByComponentID(mockComponentID); | ||||
|                     AddComponent(entity, mockComponent); | ||||
|                     var entity = GetEntityByComponentID<MockComponent>(mockComponentID); | ||||
|                     SetComponent(entity, mockComponent); | ||||
|                     RemoveComponent(mockComponentID); | ||||
|                 } | ||||
|             } | ||||
|  | @ -147,7 +165,7 @@ namespace Tests | |||
|             worldBuilder.AddEngine(new AddAndRemoveComponentEngine()); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, new MockComponent()); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent()); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -183,15 +201,16 @@ namespace Tests | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [Activates(typeof(MockComponent))] | ||||
|         [WritesPending(typeof(MockComponent))] | ||||
|         [Receives(typeof(AddMockComponentMessage))] | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         class AddMockComponentEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 foreach (var message in ReadMessages<AddMockComponentMessage>()) | ||||
|                 { | ||||
|                     AddComponent(message.entity, message.mockComponent); | ||||
|                     SetComponent(message.entity, message.mockComponent); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -239,7 +258,7 @@ namespace Tests | |||
|             mockComponent.myInt = 3; | ||||
|             mockComponent.myString = "hello"; | ||||
| 
 | ||||
|             var componentID = worldBuilder.AddComponent<MockComponent>(entity, mockComponent); | ||||
|             var componentID = worldBuilder.SetComponent<MockComponent>(entity, mockComponent); | ||||
| 
 | ||||
|             EntityMessage entityMessage; | ||||
|             entityMessage.entity = entity; | ||||
|  | @ -282,7 +301,7 @@ namespace Tests | |||
|             mockComponent.myInt = 3; | ||||
|             mockComponent.myString = "hello"; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             HasComponentTestMessage hasComponentTestMessage; | ||||
|             hasComponentTestMessage.entity = entity; | ||||
|  | @ -368,7 +387,7 @@ namespace Tests | |||
|             mockComponent.myInt = 3; | ||||
|             mockComponent.myString = "hello"; | ||||
| 
 | ||||
|             var componentID = worldBuilder.AddComponent(entity, mockComponent); | ||||
|             var componentID = worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             RemoveComponentTestMessage removeComponentMessage; | ||||
|             removeComponentMessage.entity = entity; | ||||
|  |  | |||
|  | @ -59,8 +59,8 @@ namespace Tests | |||
|             mockComponentB.myInt = 1; | ||||
|             mockComponentB.myString = "howdy"; | ||||
| 
 | ||||
|             var componentAID = worldBuilder.AddComponent(entity, mockComponent); | ||||
|             var componentBID = worldBuilder.AddComponent(entityB, mockComponentB); | ||||
|             var componentAID = worldBuilder.SetComponent(entity, mockComponent); | ||||
|             var componentBID = worldBuilder.SetComponent(entityB, mockComponentB); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -83,7 +83,7 @@ namespace Tests | |||
|             mockComponent.myInt = 0; | ||||
|             mockComponent.myString = "hello"; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -109,8 +109,8 @@ namespace Tests | |||
|             mockComponentB.myInt = 1; | ||||
|             mockComponentB.myString = "howdy"; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.AddComponent(entityB, mockComponentB); | ||||
|             worldBuilder.SetComponent(entity, mockComponent); | ||||
|             worldBuilder.SetComponent(entityB, mockComponentB); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -120,7 +120,7 @@ namespace Tests | |||
|         } | ||||
| 
 | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         [Updates(typeof(MockComponent))] | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         public class UpdateComponentTestEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|  | @ -129,7 +129,7 @@ namespace Tests | |||
| 
 | ||||
|                 component.myInt = 420; | ||||
|                 component.myString = "blaze it"; | ||||
|                 UpdateComponent(componentID, component); | ||||
|                 SetComponent(GetEntityByComponentID<MockComponent>(componentID), component); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -149,7 +149,7 @@ namespace Tests | |||
|             mockComponent.myInt = 0; | ||||
|             mockComponent.myString = "hello"; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -169,7 +169,7 @@ namespace Tests | |||
| 
 | ||||
|                 component.myInt = 420; | ||||
|                 component.myString = "blaze it"; | ||||
|                 UpdateComponent(componentID, component); | ||||
|                 SetComponent(componentID, component); | ||||
| 
 | ||||
|                 component = ReadComponent<MockComponent>().Item2; | ||||
|             } | ||||
|  | @ -187,11 +187,11 @@ namespace Tests | |||
|             mockComponent.myInt = 0; | ||||
|             mockComponent.myString = "hello"; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|             var ex = Assert.Throws<IllegalUpdateException>(() => world.Update(0.01f)); | ||||
|             var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f)); | ||||
|             Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent")); | ||||
|         } | ||||
| 
 | ||||
|  | @ -353,7 +353,7 @@ namespace Tests | |||
|             var worldBuilder = new WorldBuilder(); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, new MockComponent()); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent()); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -391,10 +391,10 @@ namespace Tests | |||
|             componentB.myString = "hello"; | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, componentA); | ||||
|             worldBuilder.SetComponent(entity, componentA); | ||||
| 
 | ||||
|             var entityB = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entityB, componentB); | ||||
|             worldBuilder.SetComponent(entityB, componentB); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
|             world.Update(0.01f); | ||||
|  | @ -436,7 +436,7 @@ namespace Tests | |||
|                 foreach (var componentPair in ReadComponents<DestroyerComponent>()) | ||||
|                 { | ||||
|                     var componentID = componentPair.Item1; | ||||
|                     var entityID = GetEntityIDByComponentID(componentID); | ||||
|                     var entityID = GetEntityIDByComponentID<DestroyerComponent>(componentID); | ||||
|                     Destroy(entityID); | ||||
|                 } | ||||
|             } | ||||
|  | @ -469,13 +469,13 @@ namespace Tests | |||
|             mockComponent.myInt = 2; | ||||
|             mockComponent.myString = "blah"; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, destroyerComponent); | ||||
|             var componentID = worldBuilder.AddComponent(entity, mockComponent); | ||||
|             worldBuilder.SetComponent(entity, destroyerComponent); | ||||
|             var componentID = worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entityB, destroyerComponent); | ||||
|             var componentBID = worldBuilder.AddComponent(entityB, mockComponent); | ||||
|             worldBuilder.SetComponent(entityB, destroyerComponent); | ||||
|             var componentBID = worldBuilder.SetComponent(entityB, mockComponent); | ||||
| 
 | ||||
|             var componentCID = worldBuilder.AddComponent(entityC, mockComponent); | ||||
|             var componentCID = worldBuilder.SetComponent(entityC, mockComponent); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -495,7 +495,7 @@ namespace Tests | |||
|                 foreach (var componentPair in ReadComponents<DestroyerComponent>()) | ||||
|                 { | ||||
|                     var componentID = componentPair.Item1; | ||||
|                     var entity = GetEntityByComponentID(componentID); | ||||
|                     var entity = GetEntityByComponentID<MockComponent>(componentID); | ||||
|                     var (id, _) = GetComponent<MockComponent>(entity); | ||||
|                     RemoveComponent(id); | ||||
|                     Destroy(entity.ID); | ||||
|  | @ -512,8 +512,8 @@ namespace Tests | |||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, new DestroyerComponent()); | ||||
|             worldBuilder.AddComponent(entity, new MockComponent()); | ||||
|             worldBuilder.SetComponent(entity, new DestroyerComponent()); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent()); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -528,7 +528,7 @@ namespace Tests | |||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 var componentID = ReadComponent<MockComponent>().Item1; | ||||
|                 entityFromComponentIDResult = GetEntityByComponentID(componentID); | ||||
|                 entityFromComponentIDResult = GetEntityByComponentID<MockComponent>(componentID); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -543,7 +543,7 @@ namespace Tests | |||
|             component.myString = "howdy"; | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, component); | ||||
|             worldBuilder.SetComponent(entity, component); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
|             world.Update(0.01); | ||||
|  | @ -551,17 +551,18 @@ namespace Tests | |||
|             Assert.That(entity, Is.EqualTo(entityFromComponentIDResult)); | ||||
|         } | ||||
| 
 | ||||
|         [Activates(typeof(MockComponent))] | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         [WritesPending(typeof(MockComponent))] | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         class AddAndRemoveMockComponentEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 foreach (var (mockComponentID, mockComponent) in ReadComponents<MockComponent>()) | ||||
|                 { | ||||
|                     var entity = GetEntityByComponentID(mockComponentID); | ||||
|                     var entity = GetEntityByComponentID<MockComponent>(mockComponentID); | ||||
|                     RemoveComponent(mockComponentID); | ||||
|                     AddComponent(entity, new MockComponent()); | ||||
|                     SetComponent(entity, new MockComponent()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -573,7 +574,7 @@ namespace Tests | |||
|             { | ||||
|                 foreach (var (mockComponentID, mockComponent) in ReadComponents<MockComponent>()) | ||||
|                 { | ||||
|                     entityFromComponentIDResult = GetEntityByComponentID(mockComponentID); | ||||
|                     entityFromComponentIDResult = GetEntityByComponentID<MockComponent>(mockComponentID); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -586,7 +587,7 @@ namespace Tests | |||
|             worldBuilder.AddEngine(new GetEntityFromPendingComponentIDEngine()); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, new MockComponent()); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent()); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -613,7 +614,7 @@ namespace Tests | |||
|             worldBuilder.AddEngine(new GetPendingComponentFromIDEngine()); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, new MockComponent()); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent()); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -642,7 +643,7 @@ namespace Tests | |||
|             component.myString = "howdy"; | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, component); | ||||
|             worldBuilder.SetComponent(entity, component); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
|             world.Update(0.01f); | ||||
|  | @ -652,7 +653,7 @@ namespace Tests | |||
| 
 | ||||
|         struct OtherComponent : IComponent { } | ||||
| 
 | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         [Reads(typeof(MockComponent), typeof(OtherComponent))] | ||||
|         class GetComponentByIDWithTypeMismatchEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|  | @ -673,7 +674,7 @@ namespace Tests | |||
|             component.myString = "howdy"; | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, component); | ||||
|             worldBuilder.SetComponent(entity, component); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -708,7 +709,7 @@ namespace Tests | |||
|             EntityIDComponent entityIDComponent; | ||||
|             entityIDComponent.entityID = entityTwo.ID; | ||||
| 
 | ||||
|             worldBuilder.AddComponent(entity, entityIDComponent); | ||||
|             worldBuilder.SetComponent(entity, entityIDComponent); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -727,25 +728,26 @@ namespace Tests | |||
|             public MockComponent mockComponent; | ||||
|         } | ||||
| 
 | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         [Receives(typeof(MockComponentUpdateMessage))] | ||||
|         [Updates(typeof(MockComponent))] | ||||
|         class RepeatUpdateEngine : Engine | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         class UpdateByComponentIDEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 foreach (var mockComponentUpdateMessage in ReadMessages<MockComponentUpdateMessage>()) | ||||
|                 { | ||||
|                     UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent); | ||||
|                     UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent); | ||||
|                     SetComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent); | ||||
|                     SetComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void EngineUpdatesComponentMultipleTimes() | ||||
|         public void EngineUpdateByComponentID() | ||||
|         { | ||||
|             var worldBuilder = new WorldBuilder(); | ||||
|             worldBuilder.AddEngine(new RepeatUpdateEngine()); | ||||
|             worldBuilder.AddEngine(new UpdateByComponentIDEngine()); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
| 
 | ||||
|  | @ -753,7 +755,7 @@ namespace Tests | |||
|             mockComponent.myInt = 1; | ||||
|             mockComponent.myString = "5"; | ||||
| 
 | ||||
|             var mockComponentID = worldBuilder.AddComponent(entity, mockComponent); | ||||
|             var mockComponentID = worldBuilder.SetComponent(entity, mockComponent); | ||||
| 
 | ||||
|             MockComponentUpdateMessage mockComponentUpdateMessage; | ||||
|             mockComponentUpdateMessage.componentID = mockComponentID; | ||||
|  | @ -761,7 +763,7 @@ namespace Tests | |||
|             worldBuilder.SendMessage(mockComponentUpdateMessage); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
|             Assert.Throws<RepeatUpdateComponentException>(() => world.Update(0.01)); | ||||
|             Assert.DoesNotThrow(() => world.Update(0.01)); | ||||
|         } | ||||
| 
 | ||||
|         [Reads(typeof(MockComponent))] | ||||
|  | @ -787,7 +789,7 @@ namespace Tests | |||
|             worldBuilder.AddEngine(new MessageReadEngine()); | ||||
|              | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, new MockComponent {}); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent {}); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -806,7 +808,8 @@ namespace Tests | |||
|         } | ||||
| 
 | ||||
|         [Receives(typeof(MockMessage))] | ||||
|         [Activates(typeof(MockComponent))] | ||||
|         [WritesPending(typeof(MockComponent))] | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         class ActivateComponentEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|  | @ -814,7 +817,7 @@ namespace Tests | |||
|                 foreach (var message in ReadMessages<MockMessage>()) | ||||
|                 { | ||||
|                     var entity = CreateEntity(); | ||||
|                     AddComponent(entity, new MockComponent {}); | ||||
|                     SetComponent(entity, new MockComponent {}); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -844,5 +847,49 @@ namespace Tests | |||
| 
 | ||||
|             Assert.DoesNotThrow(() => world.Update(0.01)); | ||||
|         } | ||||
| 
 | ||||
|         struct DestroyComponentMessage : IMessage { public Entity entity; } | ||||
| 
 | ||||
|         [Reads(typeof(MockComponent))] | ||||
|         [Writes(typeof(MockComponent))] | ||||
|         class AddComponentEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 foreach (var (componentID, component) in ReadComponents<MockComponent>()) | ||||
|                 { | ||||
|                     SetComponent(componentID, new MockComponent {}); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [Receives(typeof(DestroyComponentMessage))] | ||||
|         class DestroyEntityEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 foreach (var message in ReadMessages<DestroyComponentMessage>()) | ||||
|                 { | ||||
|                     Destroy(message.entity.ID); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void EngineSetComponentAndDestroyEntitySameFrame() | ||||
|         { | ||||
|             var worldBuilder = new WorldBuilder(); | ||||
|             worldBuilder.AddEngine(new AddComponentEngine()); | ||||
|             worldBuilder.AddEngine(new DestroyEntityEngine()); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.SetComponent(entity, new MockComponent {}); | ||||
|             worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity }); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
|             world.Update(0.01); | ||||
|              | ||||
|             Assert.DoesNotThrow(() => world.Update(0.01)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ namespace Tests | |||
|                 AComponent aComponent; | ||||
| 
 | ||||
|                 var entity = worldBuilder.CreateEntity(); | ||||
|                 var componentID = worldBuilder.AddComponent(entity, aComponent); | ||||
|                 var componentID = worldBuilder.SetComponent(entity, aComponent); | ||||
| 
 | ||||
|                 var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -52,10 +52,10 @@ namespace Tests | |||
|                 AComponent aComponentTwo; | ||||
| 
 | ||||
|                 var entity = worldBuilder.CreateEntity(); | ||||
|                 var componentID = worldBuilder.AddComponent(entity, aComponent); | ||||
|                 var componentID = worldBuilder.SetComponent(entity, aComponent); | ||||
| 
 | ||||
|                 var entityB = worldBuilder.CreateEntity(); | ||||
|                 var componentTwoID = worldBuilder.AddComponent(entityB, aComponentTwo); | ||||
|                 var componentTwoID = worldBuilder.SetComponent(entityB, aComponentTwo); | ||||
|                 var world = worldBuilder.Build(); | ||||
| 
 | ||||
|                 world.Update(0.01f); | ||||
|  |  | |||
|  | @ -52,9 +52,9 @@ namespace Tests | |||
|             TestDrawComponent testDrawComponent; | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, aComponent); | ||||
|             worldBuilder.AddComponent(entity, cComponent); | ||||
|             var testDrawComponentID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 2); | ||||
|             worldBuilder.SetComponent(entity, aComponent); | ||||
|             worldBuilder.SetComponent(entity, cComponent); | ||||
|             var testDrawComponentID = worldBuilder.SetDrawComponent(entity, testDrawComponent, 2); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  | @ -72,7 +72,7 @@ namespace Tests | |||
|             { | ||||
|                 foreach (var (componentID, component) in ReadComponents<TestDrawComponent>()) | ||||
|                 { | ||||
|                     Destroy(GetEntityIDByComponentID(componentID)); | ||||
|                     Destroy(GetEntityIDByComponentID<TestDrawComponent>(componentID)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -88,7 +88,7 @@ namespace Tests | |||
|             TestDrawComponent testDrawComponent; | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             var testDrawComponentID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 1); | ||||
|             var testDrawComponentID = worldBuilder.SetDrawComponent(entity, testDrawComponent, 1); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,13 +23,14 @@ namespace Tests | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [Activates(typeof(TestComponent))] | ||||
|         [WritesPending(typeof(TestComponent))] | ||||
|         [Writes(typeof(TestComponent))] | ||||
|         class TestSpawner : Spawner<SpawnMessageA> | ||||
|         { | ||||
|             protected override void Spawn(SpawnMessageA message) | ||||
|             { | ||||
|                 resultEntity = CreateEntity(); | ||||
|                 AddComponent(resultEntity, new TestComponent()); | ||||
|                 SetComponent(resultEntity, new TestComponent()); | ||||
|                 Assert.Pass(); | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -112,30 +112,99 @@ namespace Tests | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public class MutationConflict | ||||
|         public class MultipleEngineWriteConflict | ||||
|         { | ||||
|             struct AComponent : IComponent { } | ||||
| 
 | ||||
|             [Updates(typeof(AComponent))] | ||||
|             [Writes(typeof(AComponent))] | ||||
|             class AEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt) { } | ||||
|             } | ||||
| 
 | ||||
|             [Updates(typeof(AComponent))] | ||||
|             [Writes(typeof(AComponent))] | ||||
|             class BEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt) { } | ||||
|             } | ||||
| 
 | ||||
|             [Test] | ||||
|             public void MutationConflictException() | ||||
|             public void EngineWriteConflictException() | ||||
|             { | ||||
|                 var worldBuilder = new WorldBuilder(); | ||||
|                 worldBuilder.AddEngine(new AEngine()); | ||||
|                 worldBuilder.AddEngine(new BEngine()); | ||||
| 
 | ||||
|                 Assert.Throws<EngineUpdateConflictException>(() => worldBuilder.Build()); | ||||
|                 Assert.Throws<EngineWriteConflictException>(() => worldBuilder.Build()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public class MultipleEngineWriteWithPriority | ||||
|         { | ||||
|             struct SetMessage : IMessage  | ||||
|             { | ||||
|                 public Entity entity; | ||||
|             } | ||||
| 
 | ||||
|             struct AComponent : IComponent  | ||||
|             { | ||||
|                 public int myInt; | ||||
|             } | ||||
| 
 | ||||
|             [Receives(typeof(SetMessage))] | ||||
|             [Writes(typeof(AComponent), 0)] | ||||
|             [WritesPending(typeof(AComponent))] | ||||
|             class AEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt)  | ||||
|                 { | ||||
|                     foreach (var setMessage in ReadMessages<SetMessage>()) | ||||
|                     { | ||||
|                         SetComponent(setMessage.entity, new AComponent { myInt = 0 }); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             [Receives(typeof(SetMessage))] | ||||
|             [Writes(typeof(AComponent), 1)] | ||||
|             [WritesPending(typeof(AComponent))] | ||||
|             class BEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt)  | ||||
|                 { | ||||
|                     foreach (var setMessage in ReadMessages<SetMessage>()) | ||||
|                     { | ||||
|                         SetComponent(setMessage.entity, new AComponent { myInt = 1 }); | ||||
|                     } | ||||
|                 }          | ||||
|             } | ||||
| 
 | ||||
|             static AComponent resultComponent; | ||||
| 
 | ||||
|             [ReadsPending(typeof(AComponent))] | ||||
|             class ReadComponentEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt) | ||||
|                 { | ||||
|                     resultComponent = ReadComponent<AComponent>().Item2; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             [Test] | ||||
|             public void LowerPriorityWrites() | ||||
|             { | ||||
|                 var worldBuilder = new WorldBuilder(); | ||||
|                 worldBuilder.AddEngine(new AEngine()); | ||||
|                 worldBuilder.AddEngine(new BEngine()); | ||||
| 
 | ||||
|                 var entity = worldBuilder.CreateEntity(); | ||||
|                 worldBuilder.SendMessage(new SetMessage { entity = entity }); | ||||
| 
 | ||||
|                 var world = worldBuilder.Build(); | ||||
| 
 | ||||
|                 world.Update(0.01); | ||||
| 
 | ||||
|                 Assert.That(resultComponent.myInt, Is.EqualTo(0)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -206,6 +275,68 @@ namespace Tests | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public class PriorityConflict | ||||
|         { | ||||
|             [Writes(typeof(MockComponent), 2)] | ||||
|             class AEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt) | ||||
|                 { | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             [Writes(typeof(MockComponent), 2)] | ||||
|             class BEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt) | ||||
|                 { | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             [Test] | ||||
|             public void PriorityConflictTest() | ||||
|             { | ||||
|                 var worldBuilder = new WorldBuilder(); | ||||
|                 worldBuilder.AddEngine(new AEngine()); | ||||
|                 worldBuilder.AddEngine(new BEngine()); | ||||
| 
 | ||||
|                 Assert.Throws<EngineWriteConflictException>(() => worldBuilder.Build()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public class EngineWriteConflict | ||||
|         { | ||||
|             [Writes(typeof(MockComponent))] | ||||
|             class AEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt) | ||||
|                 { | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             [Writes(typeof(MockComponent), 2)] | ||||
|             class BEngine : Engine | ||||
|             { | ||||
|                 public override void Update(double dt) | ||||
|                 { | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             [Test] | ||||
|             public void EngineWriteConflictPriorityAndNoPriorityTest() | ||||
|             { | ||||
|                 var worldBuilder = new WorldBuilder(); | ||||
|                 worldBuilder.AddEngine(new AEngine()); | ||||
|                 worldBuilder.AddEngine(new BEngine()); | ||||
| 
 | ||||
|                 Assert.Throws<EngineWriteConflictException>(() => worldBuilder.Build()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public class LegalEngines | ||||
|         { | ||||
|             static List<Engine> order = new List<Engine>(); | ||||
|  |  | |||
|  | @ -43,26 +43,26 @@ namespace Tests | |||
|             TestDrawComponent testDrawComponent = default(TestDrawComponent); | ||||
| 
 | ||||
|             var entity = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entity, testComponent); | ||||
|             var testDrawComponentOneID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 3); | ||||
|             worldBuilder.SetComponent(entity, testComponent); | ||||
|             var testDrawComponentOneID = worldBuilder.SetDrawComponent(entity, testDrawComponent, 3); | ||||
| 
 | ||||
|             TestDrawComponent testDrawComponentTwo = default(TestDrawComponent); | ||||
| 
 | ||||
|             var entityTwo = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entityTwo, testComponent); | ||||
|             var testDrawComponentTwoID = worldBuilder.AddDrawComponent(entityTwo, testDrawComponentTwo, 1); | ||||
|             worldBuilder.SetComponent(entityTwo, testComponent); | ||||
|             var testDrawComponentTwoID = worldBuilder.SetDrawComponent(entityTwo, testDrawComponentTwo, 1); | ||||
| 
 | ||||
|             TestDrawComponent testDrawComponentThree = default(TestDrawComponent); | ||||
| 
 | ||||
|             var entityThree = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entityThree, testComponent); | ||||
|             var testDrawComponentThreeID = worldBuilder.AddDrawComponent(entityThree, testDrawComponentThree, 5); | ||||
|             worldBuilder.SetComponent(entityThree, testComponent); | ||||
|             var testDrawComponentThreeID = worldBuilder.SetDrawComponent(entityThree, testDrawComponentThree, 5); | ||||
| 
 | ||||
|             TestDrawComponent testDrawComponentFour = default(TestDrawComponent); | ||||
| 
 | ||||
|             var entityFour = worldBuilder.CreateEntity(); | ||||
|             worldBuilder.AddComponent(entityFour, testComponent); | ||||
|             var testDrawComponentFourID = worldBuilder.AddDrawComponent(entityFour, testDrawComponentFour, -5); | ||||
|             worldBuilder.SetComponent(entityFour, testComponent); | ||||
|             var testDrawComponentFourID = worldBuilder.SetDrawComponent(entityFour, testDrawComponentFour, -5); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue