the big ComponentMessage rework

pull/5/head
thatcosmonaut 2019-07-18 14:02:57 -07:00
parent 0b2ef55634
commit a42123f58d
17 changed files with 409 additions and 119 deletions

11
TODO
View File

@ -1,11 +1,6 @@
- guard rail tests for nonexistent lookups in manager classes
- custom exception for nonexistent Entity ID
- custom exception for nonexistent Component ID
- Change "Writes" To "Sends" and make it only take Messages
- Add "Updates" for updating components
- special Engine kinds (detector, modifier, spawner)
- maybe AddEngine should take a constructed engine similarly to AddComponent?
- component getters should return ValueTuple instead of KeyValuePair so we can do destructuring assignments
- so we have four attributes: Activates, Reads, Updates, and Sends
- docs

View File

@ -0,0 +1,26 @@
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(ComponentMessage<>).MakeGenericType(activateType));
}
}
}
}

View File

@ -5,22 +5,32 @@ using Encompass.Exceptions;
namespace Encompass
{
[System.AttributeUsage(System.AttributeTargets.Class)]
public class Reads : System.Attribute
[AttributeUsage(AttributeTargets.Class)]
public class Reads : Attribute
{
public readonly HashSet<Type> readTypes;
public readonly HashSet<Type> readTypes = new HashSet<Type>();
public Reads(params Type[] readTypes)
{
foreach (var readType in readTypes)
{
if (!readType.GetInterfaces().Contains(typeof(IMessage)))
var isMessage = readType.GetInterfaces().Contains(typeof(IMessage));
var isComponent = readType.GetInterfaces().Contains(typeof(IComponent));
if (!isMessage && !isComponent)
{
throw new IllegalReadTypeException("{0} must be a Message", readType.Name);
throw new IllegalReadTypeException("{0} must be a Message or Component", readType.Name);
}
if (isComponent)
{
this.readTypes.Add(typeof(ComponentMessage<>).MakeGenericType(readType));
}
else
{
this.readTypes.Add(readType);
}
}
this.readTypes = new HashSet<Type>(readTypes);
}
}
}

View File

@ -62,8 +62,7 @@ namespace Encompass
entityIDToComponentIDs[entityID].Add(componentID);
componentIDToEntityID[componentID] = entityID;
inactiveComponents.Add(componentID);
MarkForActivation(componentID);
activeComponents.Add(componentID);
entitiesWithAddedComponents.Add(entityID);
@ -89,11 +88,11 @@ namespace Encompass
return GetComponentIDsByEntityID(entityID).Intersect(activeComponents).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id]));
}
internal IEnumerable<ValueTuple<Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
internal IEnumerable<ValueTuple<Guid, Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
{
return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
typeToComponentIDs[typeof(TComponent)].Intersect(activeComponents).Select((id) => new ValueTuple<Guid, TComponent>(id, (TComponent)IDToComponent[id])) :
Enumerable.Empty<ValueTuple<Guid, TComponent>>();
typeToComponentIDs[typeof(TComponent)].Intersect(activeComponents).Select((id) => new ValueTuple<Guid, Guid, TComponent>(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) :
Enumerable.Empty<ValueTuple<Guid, Guid, TComponent>>();
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetActiveComponentsByType(Type type)
@ -103,16 +102,11 @@ namespace Encompass
Enumerable.Empty<ValueTuple<Guid, IComponent>>();
}
internal ValueTuple<Guid, TComponent> GetActiveComponentByType<TComponent>() where TComponent : struct, IComponent
{
return GetActiveComponentsByType<TComponent>().Single();
}
internal IEnumerable<ValueTuple<Guid, TComponent>> GetComponentsByEntityAndType<TComponent>(Guid entityID) where TComponent : struct, IComponent
{
var entityComponentsByType = GetComponentsByEntity(entityID).Where((pair) => componentIDToType[pair.Item1] == typeof(TComponent)).Select((pair) => new ValueTuple<Guid, TComponent>(pair.Item1, (TComponent)pair.Item2));
var activeComponentsByType = GetActiveComponentsByType<TComponent>();
return activeComponentsByType.Intersect(entityComponentsByType);
return activeComponentsByType.Select((triple) => (triple.Item2, triple.Item3)).Intersect(entityComponentsByType);
}
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntityAndType(Guid entityID, Type type)
@ -180,22 +174,7 @@ namespace Encompass
}
}
internal void MarkForActivation(Guid componentID)
{
componentsMarkedForActivation.Add(componentID);
}
internal void ActivateMarkedComponents()
{
foreach (var componentID in componentsMarkedForActivation)
{
Activate(componentID);
}
componentsMarkedForActivation.Clear();
}
private void Activate(Guid componentID)
internal void Activate(Guid componentID)
{
if (inactiveComponents.Remove(componentID))
{

View File

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

View File

@ -10,6 +10,7 @@ namespace Encompass
{
internal readonly HashSet<Type> writeTypes = new HashSet<Type>();
internal readonly HashSet<Type> readTypes = new HashSet<Type>();
internal readonly HashSet<Type> activateTypes = new HashSet<Type>();
private EntityManager entityManager;
private ComponentManager componentManager;
@ -28,6 +29,12 @@ namespace Encompass
{
readTypes = readsAttribute.readTypes;
}
var activatesAttribute = GetType().GetCustomAttribute<Activates>(false);
if (activatesAttribute != null)
{
activateTypes = activatesAttribute.activateTypes;
}
}
internal void AssignEntityManager(EntityManager entityManager)
@ -82,29 +89,64 @@ namespace Encompass
return (TComponent)componentManager.GetComponentByID(componentID);
}
protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
internal IEnumerable<ValueTuple<Entity, Guid, TComponent>> ReadComponentsFromWorld<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetActiveComponentsByType<TComponent>();
}
protected ValueTuple<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetActiveComponentByType<TComponent>();
return componentManager.GetActiveComponentsByType<TComponent>().Select((triple) => (GetEntity(triple.Item1), triple.Item2, triple.Item3));
}
protected Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
return componentManager.AddComponent(entity.ID, component);
if (!activateTypes.Contains(typeof(ComponentMessage<TComponent>)))
{
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
var componentID = componentManager.AddComponent(entity.ID, component);
ComponentMessage<TComponent> componentMessage;
componentMessage.entity = entity;
componentMessage.componentID = componentID;
componentMessage.component = component;
EmitMessage(componentMessage);
return componentID;
}
protected Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent
{
return componentManager.AddDrawComponent(entity.ID, component, layer);
if (!activateTypes.Contains(typeof(ComponentMessage<TComponent>)))
{
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
var componentID = componentManager.AddDrawComponent(entity.ID, component, layer);
ComponentMessage<TComponent> componentMessage;
componentMessage.entity = entity;
componentMessage.componentID = componentID;
componentMessage.component = component;
EmitMessage(componentMessage);
return componentID;
}
protected void ActivateComponent(Guid componentID)
protected void ActivateComponent<TComponent>(Guid componentID) where TComponent : struct, IComponent
{
componentManager.MarkForActivation(componentID);
if (!activateTypes.Contains(typeof(ComponentMessage<TComponent>)))
{
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
var entity = GetEntity(componentManager.GetEntityIDByComponentID(componentID));
var component = GetComponentByID<TComponent>(componentID);
ComponentMessage<TComponent> componentMessage;
componentMessage.entity = entity;
componentMessage.componentID = componentID;
componentMessage.component = component;
EmitMessage(componentMessage);
componentManager.Activate(componentID);
}
protected void DeactivateComponent(Guid componentID)
@ -114,6 +156,11 @@ namespace Encompass
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (!readTypes.Contains(typeof(ComponentMessage<TComponent>)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
return componentManager.GetComponentsByEntityAndType<TComponent>(entity.ID);
}
@ -124,7 +171,12 @@ namespace Encompass
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return componentManager.EntityHasComponentOfType<TComponent>(entity.ID);
if (!readTypes.Contains(typeof(ComponentMessage<TComponent>)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
return messageManager.GetMessagesByType<ComponentMessage<TComponent>>().Where((message) => message.entity == entity).Any();
}
internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent
@ -162,11 +214,31 @@ namespace Encompass
return messageManager.GetMessagesByType<TMessage>();
}
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
{
return ReadMessages<TMessage>().Single();
}
protected IEnumerable<(Guid, TComponent)> ReadComponents<TComponent>() where TComponent : struct, IComponent
{
if (!readTypes.Contains(typeof(ComponentMessage<TComponent>)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return ReadMessages<ComponentMessage<TComponent>>().Select((message) => (message.componentID, message.component));
}
protected (Guid, TComponent) ReadComponent<TComponent>() where TComponent : struct, IComponent
{
return ReadComponents<TComponent>().Single();
}
protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
{
if (!readTypes.Contains(typeof(TMessage)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
}
return messageManager.GetMessagesByType<TMessage>().Any();
@ -174,6 +246,11 @@ namespace Encompass
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{
if (!readTypes.Contains(typeof(ComponentMessage<TComponent>)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
return componentManager.GetActiveComponentsByType<TComponent>().Any();
}

View File

@ -0,0 +1,30 @@
using System.Reflection;
namespace Encompass.Engines
{
internal class ComponentMessageEmitter<TComponent> : Engine where TComponent : struct, IComponent
{
public ComponentMessageEmitter() : base()
{
var writesAttribute = GetType().GetCustomAttribute<Writes>(false);
if (writesAttribute != null)
{
writesAttribute.writeTypes.Add(typeof(ComponentMessage<TComponent>));
}
writeTypes.Add(typeof(ComponentMessage<TComponent>));
}
public override void Update(double dt)
{
foreach (var (entity, componentID, component) in ReadComponentsFromWorld<TComponent>())
{
ComponentMessage<TComponent> componentMessage;
componentMessage.entity = entity;
componentMessage.componentID = componentID;
componentMessage.component = component;
EmitMessage(componentMessage);
}
}
}
}

View File

@ -2,7 +2,7 @@
namespace Encompass
{
public struct Entity
public struct Entity : IEquatable<Entity>
{
public readonly Guid ID;
@ -10,5 +10,35 @@ namespace Encompass
{
this.ID = id;
}
public override bool Equals(object obj)
{
if (obj is Entity)
{
return this.Equals((Entity)obj);
}
return false;
}
public bool Equals(Entity other)
{
return other.ID == ID;
}
public static bool operator ==(Entity one, Entity two)
{
return one.Equals(two);
}
public static bool operator !=(Entity one, Entity two)
{
return !one.Equals(two);
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Encompass.Exceptions
{
public class IllegalActivateException : Exception
{
public IllegalActivateException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Encompass.Exceptions
{
public class IllegalActivateTypeException : Exception
{
public IllegalActivateTypeException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Encompass.Exceptions
{
public class UnregisteredComponentReadException : Exception
{
public UnregisteredComponentReadException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -41,12 +41,12 @@ namespace Encompass
protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetActiveComponentsByType<TComponent>();
return componentManager.GetActiveComponentsByType<TComponent>().Select((triple) => (triple.Item2, triple.Item3));
}
protected ValueTuple<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetActiveComponentByType<TComponent>();
return ReadComponents<TComponent>().Single();
}
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent

View File

@ -36,7 +36,6 @@ namespace Encompass
entityManager.DestroyMarkedEntities();
componentManager.PerformComponentUpdates();
componentManager.ActivateMarkedComponents();
componentManager.DeactivateMarkedComponents();
componentManager.RemoveMarkedComponents();

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using Encompass.Exceptions;
using Encompass.Engines;
namespace Encompass
{
@ -17,9 +18,12 @@ namespace Encompass
private readonly DrawLayerManager drawLayerManager;
private readonly RenderManager renderManager;
private readonly Dictionary<Type, HashSet<Engine>> typeToEmitters = new Dictionary<Type, HashSet<Engine>>();
private readonly Dictionary<Type, HashSet<Engine>> typeToReaders = new Dictionary<Type, HashSet<Engine>>();
private readonly HashSet<Engine> senders = new HashSet<Engine>();
private readonly HashSet<Type> registeredComponentTypes = new HashSet<Type>();
public WorldBuilder()
{
var entitiesWithAddedComponents = new HashSet<Guid>();
@ -56,6 +60,12 @@ namespace Encompass
componentManager.MarkForDeactivation(componentID);
}
internal void RegisterComponent(Type componentType)
{
registeredComponentTypes.Add(componentType);
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
}
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
{
engine.AssignEntityManager(entityManager);
@ -65,60 +75,42 @@ namespace Encompass
engines.Add(engine);
engineGraph.AddVertex(engine);
foreach (var writeType in engine.writeTypes.Where((type) => type.GetInterfaces().Contains(typeof(IMessage))))
foreach (var activateType in engine.activateTypes)
{
if (!typeToEmitters.ContainsKey(writeType))
{
typeToEmitters.Add(writeType, new HashSet<Engine>());
}
typeToEmitters[writeType].Add(engine);
if (typeToReaders.ContainsKey(writeType))
{
foreach (var reader in typeToReaders[writeType])
{
if (engine == reader)
{
if (writeType.GetInterfaces().Contains(typeof(IMessage)))
{
throw new EngineMessageSelfCycleException("Engine both reads and writes Message {0}", writeType.Name);
}
}
else
{
engineGraph.AddEdge(engine, reader);
}
}
}
engine.writeTypes.Add(activateType);
}
foreach (var readType in engine.readTypes.Where((type) => type.GetInterfaces().Contains(typeof(IMessage))))
var messageReadTypes = engine.readTypes.Where((type) => type.GetInterfaces().Contains(typeof(IMessage)));
var messageSendTypes = engine.writeTypes.Where((type) => type.GetInterfaces().Contains(typeof(IMessage)));
if (messageReadTypes.Intersect(messageSendTypes).Any())
{
var type = messageReadTypes.Intersect(messageSendTypes).First();
throw new EngineMessageSelfCycleException("Engine {0} both reads and writes Message {1}", engine.GetType().Name, type.Name);
}
if (messageSendTypes.Any())
{
senders.Add(engine);
}
foreach (var readType in engine.readTypes)
{
if (readType.IsGenericType && readType.GetGenericTypeDefinition() == typeof(ComponentMessage<>))
{
var componentType = readType.GetGenericArguments().Single();
if (!registeredComponentTypes.Contains(componentType))
{
RegisterComponent(componentType);
}
}
if (!typeToReaders.ContainsKey(readType))
{
typeToReaders.Add(readType, new HashSet<Engine>());
}
typeToReaders[readType].Add(engine);
if (typeToEmitters.ContainsKey(readType))
{
foreach (var emitter in typeToEmitters[readType])
{
if (emitter == engine)
{
if (readType.GetInterfaces().Contains(typeof(IMessage)))
{
throw new EngineMessageSelfCycleException("Engine both reads and writes Message {0}", readType.Name);
}
}
else
{
engineGraph.AddEdge(emitter, engine);
}
}
}
}
return engine;
@ -148,8 +140,27 @@ namespace Encompass
return renderer;
}
private void BuildEngineGraph()
{
foreach (var senderEngine in senders)
{
foreach (var messageType in senderEngine.writeTypes.Where((type) => type.GetInterfaces().Contains(typeof(IMessage))))
{
if (typeToReaders.ContainsKey(messageType))
{
foreach (var readerEngine in typeToReaders[messageType])
{
engineGraph.AddEdge(senderEngine, readerEngine);
}
}
}
}
}
public World Build()
{
BuildEngineGraph();
if (engineGraph.Cyclic())
{
var cycles = engineGraph.SimpleCycles();
@ -225,7 +236,6 @@ namespace Encompass
);
componentManager.PerformComponentUpdates();
componentManager.ActivateMarkedComponents();
componentManager.DeactivateMarkedComponents();
componentManager.RemoveMarkedComponents();

View File

@ -24,7 +24,7 @@ namespace Tests
static IEnumerable<(Guid, MockComponent)> gottenMockComponentIDPairs = Enumerable.Empty<(Guid, MockComponent)>();
static (Guid, MockComponent) gottenMockComponentIDPair;
[Reads(typeof(EntityMessage))]
[Reads(typeof(EntityMessage), typeof(MockComponent))]
class GetMockComponentsEngine : Engine
{
public override void Update(double dt)
@ -38,7 +38,7 @@ namespace Tests
}
}
[Reads(typeof(EntityMessage))]
[Reads(typeof(EntityMessage), typeof(MockComponent))]
class GetMockComponentEngine : Engine
{
public override void Update(double dt)
@ -56,8 +56,8 @@ namespace Tests
public MockComponent mockComponent;
}
[Reads(typeof(AddComponentTestMessage))]
class AddComponentEngine : Engine
[Reads(typeof(AddComponentTestMessage), typeof(MockComponent))]
class AddComponentTestEngine : Engine
{
public override void Update(double dt)
{
@ -73,8 +73,7 @@ namespace Tests
public void AddComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AddComponentEngine());
worldBuilder.AddEngine(new GetMockComponentEngine());
worldBuilder.AddEngine(new AddComponentTestEngine());
var entity = worldBuilder.CreateEntity();
@ -94,6 +93,79 @@ namespace Tests
world.Update(0.01);
}
struct AddMockComponentMessage : IMessage
{
public Entity entity;
public MockComponent mockComponent;
}
[Writes(typeof(AddMockComponentMessage))]
class EmitMockComponentMessageEngine : Engine
{
private Entity entity;
public EmitMockComponentMessageEngine(Entity entity)
{
this.entity = entity;
}
public override void Update(double dt)
{
MockComponent mockComponent;
mockComponent.myInt = 10;
mockComponent.myString = "four";
AddMockComponentMessage addMockComponentMessage;
addMockComponentMessage.entity = entity;
addMockComponentMessage.mockComponent = mockComponent;
EmitMessage(addMockComponentMessage);
}
}
[Activates(typeof(MockComponent))]
[Reads(typeof(AddMockComponentMessage))]
class AddMockComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var message in ReadMessages<AddMockComponentMessage>())
{
AddComponent(message.entity, message.mockComponent);
}
}
}
[Reads(typeof(MockComponent))]
class HasMockComponentEngine : Engine
{
private Entity entity;
public HasMockComponentEngine(Entity entity)
{
this.entity = entity;
}
public override void Update(double dt)
{
Assert.IsTrue(HasComponent<MockComponent>(entity));
}
}
[Test]
public void AddComponentAndReadSameFrame()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
worldBuilder.AddEngine(new EmitMockComponentMessageEngine(entity));
worldBuilder.AddEngine(new AddMockComponentEngine());
worldBuilder.AddEngine(new HasMockComponentEngine(entity));
var world = worldBuilder.Build();
world.Update(0.01);
}
[Test]
public void GetComponents()
{
@ -161,7 +233,7 @@ namespace Tests
public Entity entity;
}
[Reads(typeof(HasComponentTestMessage))]
[Reads(typeof(HasComponentTestMessage), typeof(MockComponent))]
class HasComponentTestEngine : Engine
{
public override void Update(double dt)
@ -201,7 +273,7 @@ namespace Tests
public Entity entity;
}
[Reads(typeof(HasComponentWhenInactiveTestMessage))]
[Reads(typeof(HasComponentWhenInactiveTestMessage), typeof(MockComponent))]
class HasComponentWhenInactiveTestEngine : Engine
{
public override void Update(double dt)
@ -244,7 +316,6 @@ namespace Tests
}
[Reads(typeof(RemoveComponentTestMessage))]
[Writes(typeof(MockComponent))]
class RemoveComponentTestEngine : Engine
{
public override void Update(double dt)
@ -318,15 +389,15 @@ namespace Tests
public Guid componentID;
}
[Activates(typeof(MockComponent))]
[Reads(typeof(ActivateComponentMessage))]
[Writes(typeof(MockComponent))]
class ActivateComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var activateComponentMessage in ReadMessages<ActivateComponentMessage>())
{
ActivateComponent(activateComponentMessage.componentID);
ActivateComponent<MockComponent>(activateComponentMessage.componentID);
}
}
}
@ -367,7 +438,7 @@ namespace Tests
}
}
[Reads(typeof(CheckHasMockComponentMessage))]
[Reads(typeof(CheckHasMockComponentMessage), typeof(MockComponent))]
class CheckHasMockComponentEngine : Engine
{
public override void Update(double dt)
@ -386,6 +457,8 @@ namespace Tests
}
}
// TODO: need to rethink this test because ActivateComponent is instant now
[Test]
public void ActivateComponent()
{

View File

@ -23,6 +23,7 @@ namespace Tests
static List<MockMessage> resultMessages;
[Reads(typeof(MockComponent))]
public class ReadComponentsTestEngine : Engine
{
public override void Update(double dt)
@ -32,6 +33,7 @@ namespace Tests
}
[Reads(typeof(MockComponent))]
public class ReadComponentTestEngine : Engine
{
public override void Update(double dt)
@ -117,6 +119,7 @@ namespace Tests
Assert.Throws<InvalidOperationException>(() => world.Update(0.01f));
}
[Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
public class UpdateComponentTestEngine : Engine
{
@ -136,6 +139,7 @@ namespace Tests
public void UpdateComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new UpdateComponentTestEngine());
worldBuilder.AddEngine(new ReadComponentTestEngine());
@ -156,11 +160,12 @@ namespace Tests
Assert.AreEqual("blaze it", resultComponent.myString);
}
[Reads(typeof(MockComponent))]
public class UndeclaredUpdateComponentTestEngine : Engine
{
public override void Update(double dt)
{
(var componentID, var component) = this.ReadComponent<MockComponent>();
(var componentID, var component) = ReadComponent<MockComponent>();
component.myInt = 420;
component.myString = "blaze it";
@ -358,6 +363,7 @@ namespace Tests
static ValueTuple<Guid, MockComponent> pairA;
static ValueTuple<Guid, MockComponent> pairB;
[Reads(typeof(MockComponent))]
class SameValueComponentReadEngine : Engine
{
public override void Update(double dt)
@ -397,6 +403,7 @@ namespace Tests
static IEnumerable<ValueTuple<Guid, MockComponent>> emptyComponentReadResult;
[Reads(typeof(MockComponent))]
class ReadEmptyMockComponentsEngine : Engine
{
public override void Update(double dt)
@ -419,6 +426,7 @@ namespace Tests
struct DestroyerComponent : IComponent { }
[Reads(typeof(DestroyerComponent))]
class DestroyerEngine : Engine
{
public override void Update(double dt)
@ -434,6 +442,7 @@ namespace Tests
static IEnumerable<ValueTuple<Guid, MockComponent>> results;
[Reads(typeof(MockComponent))]
class ReaderEngine : Engine
{
public override void Update(double dt)
@ -471,6 +480,7 @@ namespace Tests
Assert.That(results, Does.Not.Contain((componentBID, mockComponent)));
}
[Reads(typeof(DestroyerComponent), typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class DestroyAndAddComponentEngine : Engine
{
@ -506,6 +516,7 @@ namespace Tests
static Entity entityFromComponentIDResult;
[Reads(typeof(MockComponent))]
class GetEntityFromComponentIDEngine : Engine
{
public override void Update(double dt)
@ -536,6 +547,7 @@ namespace Tests
static MockComponent mockComponentByIDResult;
[Reads(typeof(MockComponent))]
class GetComponentByIDEngine : Engine
{
public override void Update(double dt)
@ -565,6 +577,7 @@ namespace Tests
struct OtherComponent : IComponent { }
[Reads(typeof(MockComponent))]
class GetComponentByIDWithTypeMismatchEngine : Engine
{
public override void Update(double dt)
@ -595,6 +608,7 @@ namespace Tests
struct EntityIDComponent : IComponent { public Guid entityID; }
static bool hasEntity;
[Reads(typeof(EntityIDComponent))]
class HasEntityTestEngine : Engine
{
public override void Update(double dt)

View File

@ -23,7 +23,7 @@ namespace Tests
}
}
[Writes(typeof(TestComponent))]
[Activates(typeof(TestComponent))]
class TestSpawner : Spawner<SpawnMessageA>
{
protected override void Spawn(SpawnMessageA message)