update engine generator
parent
573657fd39
commit
fea5c57860
|
@ -8,7 +8,7 @@ namespace Encompass
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class Updates : Attribute
|
public class Updates : Attribute
|
||||||
{
|
{
|
||||||
public readonly HashSet<Type> updateTypes;
|
public readonly HashSet<Type> updateTypes = new HashSet<Type>();
|
||||||
|
|
||||||
public Updates(params Type[] updateTypes)
|
public Updates(params Type[] updateTypes)
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,9 @@ namespace Encompass
|
||||||
{
|
{
|
||||||
throw new IllegalUpdateTypeException("{0} must be a Component", updateType.Name);
|
throw new IllegalUpdateTypeException("{0} must be a Component", updateType.Name);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.updateTypes = new HashSet<Type>(updateTypes);
|
this.updateTypes.Add(typeof(ComponentUpdateMessage<>).MakeGenericType(updateType));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,7 +10,6 @@ namespace Encompass
|
||||||
{
|
{
|
||||||
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
||||||
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
|
||||||
internal readonly HashSet<Type> updateTypes = new HashSet<Type>();
|
|
||||||
|
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
private ComponentManager componentManager;
|
private ComponentManager componentManager;
|
||||||
|
@ -30,6 +29,12 @@ namespace Encompass
|
||||||
sendTypes.UnionWith(activatesAttribute.activateTypes);
|
sendTypes.UnionWith(activatesAttribute.activateTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var updatesAttribute = GetType().GetCustomAttribute<Updates>(false);
|
||||||
|
if (updatesAttribute != null)
|
||||||
|
{
|
||||||
|
sendTypes.UnionWith(updatesAttribute.updateTypes);
|
||||||
|
}
|
||||||
|
|
||||||
var receivesAttribute = GetType().GetCustomAttribute<Receives>(false);
|
var receivesAttribute = GetType().GetCustomAttribute<Receives>(false);
|
||||||
if (receivesAttribute != null)
|
if (receivesAttribute != null)
|
||||||
{
|
{
|
||||||
|
@ -47,16 +52,6 @@ namespace Encompass
|
||||||
{
|
{
|
||||||
receiveTypes.UnionWith(readsPendingAttribute.readPendingTypes);
|
receiveTypes.UnionWith(readsPendingAttribute.readPendingTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatesAttribute = GetType().GetCustomAttribute<Updates>(false);
|
|
||||||
if (updatesAttribute != null)
|
|
||||||
{
|
|
||||||
updateTypes = updatesAttribute.updateTypes;
|
|
||||||
if (sendTypes.Any())
|
|
||||||
{
|
|
||||||
throw new IllegalEngineAttributesException("Engine {0} sends Message(s) and updates Component(s)", GetType().Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AssignEntityManager(EntityManager entityManager)
|
internal void AssignEntityManager(EntityManager entityManager)
|
||||||
|
@ -228,24 +223,27 @@ namespace Encompass
|
||||||
|
|
||||||
internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent
|
internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
if (!updateTypes.Contains(typeof(TComponent)))
|
|
||||||
{
|
|
||||||
throw new IllegalSendException("Engine {0} tried to update undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentManager.AddUpdateComponentOperation(componentID, newComponent);
|
componentManager.AddUpdateComponentOperation(componentID, newComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
|
protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
UpdateComponentInWorld(componentID, newComponentValue);
|
if (!sendTypes.Contains(typeof(ComponentUpdateMessage<TComponent>)))
|
||||||
|
{
|
||||||
|
throw new IllegalUpdateException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentUpdateMessage<TComponent> componentUpdateMessage;
|
||||||
|
componentUpdateMessage.componentID = componentID;
|
||||||
|
componentUpdateMessage.component = newComponentValue;
|
||||||
|
SendMessage(componentUpdateMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
|
protected void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
if (!sendTypes.Contains(typeof(TMessage)))
|
if (!sendTypes.Contains(typeof(TMessage)))
|
||||||
{
|
{
|
||||||
throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
|
throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
messageManager.AddMessage(message);
|
messageManager.AddMessage(message);
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
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,24 +0,0 @@
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Encompass.Engines
|
|
||||||
{
|
|
||||||
internal class NewComponentMessageEmitter<TComponent> : Engine where TComponent : struct, IComponent
|
|
||||||
{
|
|
||||||
public NewComponentMessageEmitter() : base()
|
|
||||||
{
|
|
||||||
sendTypes.Add(typeof(PendingComponentMessage<TComponent>));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(double dt)
|
|
||||||
{
|
|
||||||
foreach (var (entity, componentID, component) in ReadComponentsFromWorld<TComponent>())
|
|
||||||
{
|
|
||||||
PendingComponentMessage<TComponent> newComponentMessage;
|
|
||||||
newComponentMessage.entity = entity;
|
|
||||||
newComponentMessage.componentID = componentID;
|
|
||||||
newComponentMessage.component = component;
|
|
||||||
SendMessage(newComponentMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Encompass.Exceptions
|
||||||
|
{
|
||||||
|
public class IllegalUpdateException : Exception
|
||||||
|
{
|
||||||
|
public IllegalUpdateException(
|
||||||
|
string format,
|
||||||
|
params object[] args
|
||||||
|
) : base(string.Format(format, args)) { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Encompass
|
||||||
|
{
|
||||||
|
public struct ComponentUpdateMessage<TComponent> : IMessage where TComponent : struct, IComponent
|
||||||
|
{
|
||||||
|
public Guid componentID;
|
||||||
|
public TComponent component;
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,10 +67,9 @@ namespace Encompass
|
||||||
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
|
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RegisterNewComponentEmitter(Type componentType)
|
internal void RegisterNewComponentUpdater(Type componentType)
|
||||||
{
|
{
|
||||||
registeredNewComponentTypes.Add(componentType);
|
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentUpdater<>).MakeGenericType(componentType)));
|
||||||
AddEngine((Engine)Activator.CreateInstance(typeof(NewComponentMessageEmitter<>).MakeGenericType(componentType)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
|
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
|
||||||
|
@ -129,13 +128,10 @@ namespace Encompass
|
||||||
if (sendType.IsGenericType)
|
if (sendType.IsGenericType)
|
||||||
{
|
{
|
||||||
var genericTypeDefinition = sendType.GetGenericTypeDefinition();
|
var genericTypeDefinition = sendType.GetGenericTypeDefinition();
|
||||||
if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(PendingComponentMessage<>))
|
if (genericTypeDefinition == typeof(ComponentUpdateMessage<>))
|
||||||
{
|
{
|
||||||
var componentType = sendType.GetGenericArguments().Single();
|
var componentType = sendType.GetGenericArguments().Single();
|
||||||
if (!registeredNewComponentTypes.Contains(componentType))
|
RegisterNewComponentUpdater(componentType);
|
||||||
{
|
|
||||||
RegisterNewComponentEmitter(componentType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,11 +204,13 @@ namespace Encompass
|
||||||
|
|
||||||
var mutatedComponentTypes = new HashSet<Type>();
|
var mutatedComponentTypes = new HashSet<Type>();
|
||||||
var duplicateMutations = new List<Type>();
|
var duplicateMutations = new List<Type>();
|
||||||
var componentToEngines = new Dictionary<Type, List<Engine>>();
|
var updateMessageToEngines = new Dictionary<Type, List<Engine>>();
|
||||||
|
|
||||||
foreach (var engine in engines)
|
foreach (var engine in engines)
|
||||||
{
|
{
|
||||||
foreach (var updateType in engine.updateTypes)
|
var updateTypes = engine.sendTypes.Where((type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ComponentUpdateMessage<>));
|
||||||
|
|
||||||
|
foreach (var updateType in updateTypes)
|
||||||
{
|
{
|
||||||
if (mutatedComponentTypes.Contains(updateType))
|
if (mutatedComponentTypes.Contains(updateType))
|
||||||
{
|
{
|
||||||
|
@ -223,12 +221,12 @@ namespace Encompass
|
||||||
mutatedComponentTypes.Add(updateType);
|
mutatedComponentTypes.Add(updateType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!componentToEngines.ContainsKey(updateType))
|
if (!updateMessageToEngines.ContainsKey(updateType))
|
||||||
{
|
{
|
||||||
componentToEngines[updateType] = new List<Engine>();
|
updateMessageToEngines[updateType] = new List<Engine>();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentToEngines[updateType].Add(engine);
|
updateMessageToEngines[updateType].Add(engine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +237,7 @@ namespace Encompass
|
||||||
{
|
{
|
||||||
errorString += "\n" +
|
errorString += "\n" +
|
||||||
componentType.Name + " updated by: " +
|
componentType.Name + " updated by: " +
|
||||||
string.Join(", ", componentToEngines[componentType].Select((engine) => engine.GetType().Name));
|
string.Join(", ", updateMessageToEngines[componentType].Select((engine) => engine.GetType().Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new EngineUpdateConflictException(errorString);
|
throw new EngineUpdateConflictException(errorString);
|
||||||
|
|
|
@ -394,6 +394,19 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Receives(typeof(CheckHasMockComponentMessage))]
|
||||||
|
[Reads(typeof(MockComponent))]
|
||||||
|
class CheckHasMockComponentEngine : Engine
|
||||||
|
{
|
||||||
|
public override void Update(double dt)
|
||||||
|
{
|
||||||
|
foreach (var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
|
||||||
|
{
|
||||||
|
Assert.IsTrue(HasComponent<MockComponent>(checkHasMockComponentMessage.entity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void RemoveComponent()
|
public void RemoveComponent()
|
||||||
{
|
{
|
||||||
|
@ -470,7 +483,7 @@ namespace Tests
|
||||||
|
|
||||||
[Receives(typeof(CheckHasMockComponentMessage))]
|
[Receives(typeof(CheckHasMockComponentMessage))]
|
||||||
[ReadsPending(typeof(MockComponent))]
|
[ReadsPending(typeof(MockComponent))]
|
||||||
class CheckHasMockComponentEngine : Engine
|
class CheckHasPendingMockComponentEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
{
|
{
|
||||||
|
@ -488,7 +501,7 @@ namespace Tests
|
||||||
var entity = worldBuilder.CreateEntity();
|
var entity = worldBuilder.CreateEntity();
|
||||||
|
|
||||||
worldBuilder.AddEngine(new ActivateComponentEngine());
|
worldBuilder.AddEngine(new ActivateComponentEngine());
|
||||||
worldBuilder.AddEngine(new CheckHasMockComponentEngine());
|
worldBuilder.AddEngine(new CheckHasPendingMockComponentEngine());
|
||||||
worldBuilder.AddEngine(new DoActivateCheckEngine(entity));
|
worldBuilder.AddEngine(new DoActivateCheckEngine(entity));
|
||||||
|
|
||||||
MockComponent mockComponent;
|
MockComponent mockComponent;
|
||||||
|
|
|
@ -191,7 +191,7 @@ namespace Tests
|
||||||
|
|
||||||
var world = worldBuilder.Build();
|
var world = worldBuilder.Build();
|
||||||
|
|
||||||
var ex = Assert.Throws<IllegalSendException>(() => world.Update(0.01f));
|
var ex = Assert.Throws<IllegalUpdateException>(() => world.Update(0.01f));
|
||||||
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent"));
|
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,21 +645,6 @@ namespace Tests
|
||||||
Assert.IsFalse(hasEntity);
|
Assert.IsFalse(hasEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Sends(typeof(MockMessage))]
|
|
||||||
[Updates(typeof(MockComponent))]
|
|
||||||
class EngineThatWritesComponentAndMessage : Engine
|
|
||||||
{
|
|
||||||
public override void Update(double dt) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void EngineWritesComponentAndMessage()
|
|
||||||
{
|
|
||||||
var worldBuilder = new WorldBuilder();
|
|
||||||
|
|
||||||
Assert.Throws<IllegalEngineAttributesException>(() => worldBuilder.AddEngine(new EngineThatWritesComponentAndMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MockComponentUpdateMessage : IMessage
|
struct MockComponentUpdateMessage : IMessage
|
||||||
{
|
{
|
||||||
public Guid componentID;
|
public Guid componentID;
|
||||||
|
|
Loading…
Reference in New Issue