update engine generator

pull/5/head
thatcosmonaut 2019-07-19 17:50:13 -07:00
parent 573657fd39
commit fea5c57860
11 changed files with 87 additions and 77 deletions

View File

@ -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));
}
} }
} }
} }

View File

@ -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);

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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)) { }
}
}

View File

@ -0,0 +1,10 @@
using System;
namespace Encompass
{
public struct ComponentUpdateMessage<TComponent> : IMessage where TComponent : struct, IComponent
{
public Guid componentID;
public TComponent component;
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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;