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)]
public class Updates : Attribute
{
public readonly HashSet<Type> updateTypes;
public readonly HashSet<Type> updateTypes = new HashSet<Type>();
public Updates(params Type[] updateTypes)
{
@ -19,9 +19,9 @@ namespace Encompass
{
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> receiveTypes = new HashSet<Type>();
internal readonly HashSet<Type> updateTypes = new HashSet<Type>();
private EntityManager entityManager;
private ComponentManager componentManager;
@ -30,6 +29,12 @@ namespace Encompass
sendTypes.UnionWith(activatesAttribute.activateTypes);
}
var updatesAttribute = GetType().GetCustomAttribute<Updates>(false);
if (updatesAttribute != null)
{
sendTypes.UnionWith(updatesAttribute.updateTypes);
}
var receivesAttribute = GetType().GetCustomAttribute<Receives>(false);
if (receivesAttribute != null)
{
@ -47,16 +52,6 @@ namespace Encompass
{
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)
@ -228,24 +223,27 @@ namespace Encompass
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);
}
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
{
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);

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)));
}
internal void RegisterNewComponentEmitter(Type componentType)
internal void RegisterNewComponentUpdater(Type componentType)
{
registeredNewComponentTypes.Add(componentType);
AddEngine((Engine)Activator.CreateInstance(typeof(NewComponentMessageEmitter<>).MakeGenericType(componentType)));
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentUpdater<>).MakeGenericType(componentType)));
}
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
@ -129,13 +128,10 @@ namespace Encompass
if (sendType.IsGenericType)
{
var genericTypeDefinition = sendType.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(PendingComponentMessage<>))
if (genericTypeDefinition == typeof(ComponentUpdateMessage<>))
{
var componentType = sendType.GetGenericArguments().Single();
if (!registeredNewComponentTypes.Contains(componentType))
{
RegisterNewComponentEmitter(componentType);
}
RegisterNewComponentUpdater(componentType);
}
}
}
@ -208,11 +204,13 @@ namespace Encompass
var mutatedComponentTypes = new HashSet<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 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))
{
@ -223,12 +221,12 @@ namespace Encompass
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" +
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);

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]
public void RemoveComponent()
{
@ -470,7 +483,7 @@ namespace Tests
[Receives(typeof(CheckHasMockComponentMessage))]
[ReadsPending(typeof(MockComponent))]
class CheckHasMockComponentEngine : Engine
class CheckHasPendingMockComponentEngine : Engine
{
public override void Update(double dt)
{
@ -488,7 +501,7 @@ namespace Tests
var entity = worldBuilder.CreateEntity();
worldBuilder.AddEngine(new ActivateComponentEngine());
worldBuilder.AddEngine(new CheckHasMockComponentEngine());
worldBuilder.AddEngine(new CheckHasPendingMockComponentEngine());
worldBuilder.AddEngine(new DoActivateCheckEngine(entity));
MockComponent mockComponent;

View File

@ -191,7 +191,7 @@ namespace Tests
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"));
}
@ -645,21 +645,6 @@ namespace Tests
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
{
public Guid componentID;