started the component write priority system
parent
5429783633
commit
9813c260ae
2
TODO
2
TODO
|
@ -1,2 +1,4 @@
|
|||
- look at test coverage
|
||||
- docs
|
||||
|
||||
- WritesPending and writes redundant?
|
|
@ -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,8 +19,9 @@ 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 List<(Entity, Type, Guid, IComponent)> componentWriteData = new List<(Entity, Type, Guid, IComponent)>();
|
||||
private 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>();
|
||||
|
||||
|
@ -40,17 +41,41 @@ 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 (componentWritePriorities.ContainsKey((entity, typeof(TComponent))))
|
||||
{
|
||||
var currentPriority = componentWritePriorities[(entity, typeof(TComponent))];
|
||||
if (priority < currentPriority)
|
||||
{
|
||||
componentWriteData.Add((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;
|
||||
}
|
||||
|
@ -78,15 +103,16 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
internal void AddMarkedComponents()
|
||||
internal void WriteComponents()
|
||||
{
|
||||
foreach (var (entity, type, componentID, component) in componentAddData)
|
||||
foreach (var (entity, type, componentID, component) in componentWriteData)
|
||||
{
|
||||
AddComponent(entity, type, componentID, component);
|
||||
}
|
||||
|
||||
componentAddData.Clear();
|
||||
componentIDsMarkedForAdd.Clear();
|
||||
componentWriteData.Clear();
|
||||
componentIDsMarkedForWrite.Clear();
|
||||
componentWritePriorities.Clear();
|
||||
}
|
||||
|
||||
internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID)
|
||||
|
@ -184,9 +210,9 @@ namespace Encompass
|
|||
{
|
||||
foreach (var componentID in componentsMarkedForRemoval)
|
||||
{
|
||||
if (componentIDsMarkedForAdd.Contains(componentID))
|
||||
if (componentIDsMarkedForWrite.Contains(componentID))
|
||||
{
|
||||
componentIDsMarkedForAdd.Remove(componentID);
|
||||
componentIDsMarkedForWrite.Remove(componentID);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 writesAttribute = GetType().GetCustomAttribute<Writes>(false);
|
||||
if (writesAttribute != null)
|
||||
{
|
||||
sendTypes.UnionWith(updatesAttribute.updateTypes);
|
||||
sendTypes.UnionWith(writesAttribute.writeTypes);
|
||||
writePriorities = writesAttribute.priorities;
|
||||
}
|
||||
|
||||
var receivesAttribute = GetType().GetCustomAttribute<Receives>(false);
|
||||
|
@ -124,40 +126,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>));
|
||||
|
@ -285,17 +253,67 @@ namespace Encompass
|
|||
componentManager.AddUpdateComponentOperation(componentID, newComponent);
|
||||
}
|
||||
|
||||
protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
|
||||
protected Guid SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
|
||||
{
|
||||
if (!sendTypes.Contains(typeof(ComponentUpdateMessage<TComponent>)))
|
||||
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 IllegalUpdateException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||
throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||
}
|
||||
|
||||
ComponentUpdateMessage<TComponent> componentUpdateMessage;
|
||||
if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>)))
|
||||
{
|
||||
PendingComponentMessage<TComponent> newComponentMessage;
|
||||
newComponentMessage.entity = entity;
|
||||
newComponentMessage.componentID = componentID;
|
||||
newComponentMessage.component = component;
|
||||
newComponentMessage.priority = priority;
|
||||
SendPendingComponentMessage(newComponentMessage);
|
||||
}
|
||||
|
||||
ComponentWriteMessage<TComponent> componentUpdateMessage;
|
||||
componentUpdateMessage.componentID = componentID;
|
||||
componentUpdateMessage.component = newComponentValue;
|
||||
componentUpdateMessage.component = component;
|
||||
SendMessage(componentUpdateMessage);
|
||||
|
||||
return componentID;
|
||||
}
|
||||
|
||||
protected Guid SetComponent<TComponent>(Guid componentID, TComponent component) where TComponent : struct, IComponent
|
||||
{
|
||||
return SetComponent(GetEntityByComponentID(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);
|
||||
}
|
||||
|
||||
ComponentWriteMessage<TComponent> componentUpdateMessage;
|
||||
componentUpdateMessage.componentID = componentID;
|
||||
componentUpdateMessage.component = component;
|
||||
SendMessage(componentUpdateMessage);
|
||||
|
||||
return componentID;
|
||||
}
|
||||
|
||||
protected void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
|
||||
|
|
|
@ -4,12 +4,12 @@ namespace Encompass.Engines
|
|||
{
|
||||
public ComponentUpdater() : base()
|
||||
{
|
||||
receiveTypes.Add(typeof(ComponentUpdateMessage<TComponent>));
|
||||
receiveTypes.Add(typeof(ComponentWriteMessage<TComponent>));
|
||||
}
|
||||
|
||||
public override void Update(double dt)
|
||||
{
|
||||
foreach (var componentUpdateMessage in ReadMessages<ComponentUpdateMessage<TComponent>>())
|
||||
foreach (var componentUpdateMessage in ReadMessages<ComponentWriteMessage<TComponent>>())
|
||||
{
|
||||
UpdateComponentInWorld(componentUpdateMessage.componentID, componentUpdateMessage.component);
|
||||
}
|
||||
|
|
|
@ -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 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 IllegalUpdateTypeException : Exception
|
||||
public class IllegalWritePendingException : Exception
|
||||
{
|
||||
public IllegalUpdateTypeException(
|
||||
public IllegalWritePendingException(
|
||||
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 IllegalWritePendingTypeException : Exception
|
||||
{
|
||||
public IllegalActivateTypeException(
|
||||
public IllegalWritePendingTypeException(
|
||||
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 IllegalWriteTypeException : Exception
|
||||
{
|
||||
public IllegalActivateException(
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ namespace Encompass
|
|||
|
||||
componentManager.PerformComponentUpdates();
|
||||
componentManager.RemoveMarkedComponents();
|
||||
componentManager.AddMarkedComponents();
|
||||
componentManager.WriteComponents();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
|
|
|
@ -52,12 +52,12 @@ namespace Encompass
|
|||
|
||||
public Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
|
||||
{
|
||||
return componentManager.MarkComponentForAdd(entity, component);
|
||||
return componentManager.MarkComponentForWrite(entity, component, 0);
|
||||
}
|
||||
|
||||
public Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent
|
||||
{
|
||||
return componentManager.MarkDrawComponentForAdd(entity, component, layer);
|
||||
return componentManager.MarkDrawComponentForWrite(entity, component, layer);
|
||||
}
|
||||
|
||||
internal void RegisterComponent(Type componentType)
|
||||
|
@ -133,7 +133,7 @@ namespace Encompass
|
|||
if (sendType.IsGenericType)
|
||||
{
|
||||
var genericTypeDefinition = sendType.GetGenericTypeDefinition();
|
||||
if (genericTypeDefinition == typeof(ComponentUpdateMessage<>))
|
||||
if (genericTypeDefinition == typeof(ComponentWriteMessage<>))
|
||||
{
|
||||
var componentType = sendType.GetGenericArguments().Single();
|
||||
RegisterNewComponentUpdater(componentType);
|
||||
|
@ -201,45 +201,56 @@ 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 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);
|
||||
if (writtenComponentTypesWithoutPriority.Contains(componentType) || writtenComponentTypesWithPriority.Contains(componentType))
|
||||
{
|
||||
duplicateWritesWithoutPriority.Add(componentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
writtenComponentTypesWithoutPriority.Add(componentType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mutatedComponentTypes.Add(updateType);
|
||||
writtenComponentTypesWithPriority.Add(componentType);
|
||||
}
|
||||
|
||||
if (!updateMessageToEngines.ContainsKey(updateType))
|
||||
if (!writeMessageToEngines.ContainsKey(componentType))
|
||||
{
|
||||
updateMessageToEngines[updateType] = new List<Engine>();
|
||||
writeMessageToEngines[componentType] = new List<Engine>();
|
||||
}
|
||||
|
||||
updateMessageToEngines[updateType].Add(engine);
|
||||
writeMessageToEngines[componentType].Add(engine);
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicateMutations.Count > 0)
|
||||
if (duplicateWritesWithoutPriority.Count > 0)
|
||||
{
|
||||
var errorString = "Multiple Engines update the same Component: ";
|
||||
foreach (var componentType in duplicateMutations)
|
||||
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 EngineUpdateConflictException(errorString);
|
||||
throw new EngineWriteConflictException(errorString);
|
||||
}
|
||||
|
||||
var engineOrder = new List<Engine>();
|
||||
|
@ -259,7 +270,7 @@ namespace Encompass
|
|||
|
||||
componentManager.PerformComponentUpdates();
|
||||
componentManager.RemoveMarkedComponents();
|
||||
componentManager.AddMarkedComponents();
|
||||
componentManager.WriteComponents();
|
||||
|
||||
return world;
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ namespace Tests
|
|||
}
|
||||
|
||||
[Reads(typeof(MockComponent))]
|
||||
[Writes(typeof(MockComponent))]
|
||||
class MultipleAddEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
|
@ -107,7 +108,7 @@ namespace Tests
|
|||
{
|
||||
var entity = GetEntityByComponentID(mockComponentID);
|
||||
|
||||
AddComponent(entity, new MockComponent());
|
||||
SetComponent(entity, new MockComponent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +128,7 @@ namespace Tests
|
|||
}
|
||||
|
||||
[Reads(typeof(MockComponent))]
|
||||
[Writes(typeof(MockComponent))]
|
||||
class AddAndRemoveComponentEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
|
@ -134,7 +136,7 @@ namespace Tests
|
|||
foreach (var (mockComponentID, mockComponent) in ReadComponents<MockComponent>())
|
||||
{
|
||||
var entity = GetEntityByComponentID(mockComponentID);
|
||||
AddComponent(entity, mockComponent);
|
||||
SetComponent(entity, mockComponent);
|
||||
RemoveComponent(mockComponentID);
|
||||
}
|
||||
}
|
||||
|
@ -183,15 +185,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(componentID, component);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,6 +161,7 @@ namespace Tests
|
|||
}
|
||||
|
||||
[Reads(typeof(MockComponent))]
|
||||
[Writes(typeof(MockComponent))]
|
||||
public class UndeclaredUpdateComponentTestEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
|
@ -169,7 +170,7 @@ namespace Tests
|
|||
|
||||
component.myInt = 420;
|
||||
component.myString = "blaze it";
|
||||
UpdateComponent(componentID, component);
|
||||
SetComponent(componentID, component);
|
||||
|
||||
component = ReadComponent<MockComponent>().Item2;
|
||||
}
|
||||
|
@ -191,7 +192,7 @@ namespace Tests
|
|||
|
||||
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"));
|
||||
}
|
||||
|
||||
|
@ -551,8 +552,9 @@ 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)
|
||||
|
@ -561,7 +563,7 @@ namespace Tests
|
|||
{
|
||||
var entity = GetEntityByComponentID(mockComponentID);
|
||||
RemoveComponent(mockComponentID);
|
||||
AddComponent(entity, new MockComponent());
|
||||
SetComponent(entity, new MockComponent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -728,15 +730,15 @@ namespace Tests
|
|||
}
|
||||
|
||||
[Receives(typeof(MockComponentUpdateMessage))]
|
||||
[Updates(typeof(MockComponent))]
|
||||
[Writes(typeof(MockComponent))]
|
||||
class RepeatUpdateEngine : 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue