component writes moved back to end of frame

pull/5/head
thatcosmonaut 2019-07-17 18:12:29 -07:00
parent 1016f05d42
commit f960712f57
11 changed files with 248 additions and 144 deletions

View File

@ -14,9 +14,9 @@ namespace Encompass
{ {
foreach (var readType in readTypes) foreach (var readType in readTypes)
{ {
if (!readType.GetInterfaces().Contains(typeof(IMessage)) && !readType.GetInterfaces().Contains(typeof(IComponent))) if (!readType.GetInterfaces().Contains(typeof(IMessage)))
{ {
throw new IllegalReadTypeException("{0} must be a Message or Component", readType.Name); throw new IllegalReadTypeException("{0} must be a Message", readType.Name);
} }
} }

View File

@ -20,6 +20,11 @@ namespace Encompass
} }
} }
if (writeTypes.Any((type) => type.GetInterfaces().Contains(typeof(IMessage))) && writeTypes.Any((type) => type.GetInterfaces().Contains(typeof(IComponent))))
{
throw new ComponentAndMessageWriteException("An Engine which writes Components cannot also write Messages");
}
this.writeTypes = new HashSet<Type>(writeTypes); this.writeTypes = new HashSet<Type>(writeTypes);
} }
} }

View File

@ -10,14 +10,18 @@ namespace Encompass
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>(); private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>();
private readonly Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>(); private readonly Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>();
private readonly Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<Guid>>(); private readonly Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<Guid>>(); // TODO: hashset
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>(); private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
private readonly Dictionary<Type, List<Guid>> typeToComponentIDs = new Dictionary<Type, List<Guid>>(); private readonly Dictionary<Type, List<Guid>> typeToComponentIDs = new Dictionary<Type, List<Guid>>(); // TODO: hashset
private readonly List<Guid> activeComponents = new List<Guid>(); private readonly List<Guid> activeComponents = new List<Guid>();
private readonly List<Guid> inactiveComponents = new List<Guid>(); private readonly List<Guid> inactiveComponents = new List<Guid>();
private readonly HashSet<Guid> componentsMarkedForActivation = new HashSet<Guid>();
private readonly HashSet<Guid> componentsMarkedForDeactivation = new HashSet<Guid>();
private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>();
//shared references with EntityManager //shared references with EntityManager
private readonly HashSet<Guid> entitiesWithAddedComponents; private readonly HashSet<Guid> entitiesWithAddedComponents;
private readonly HashSet<Guid> entitiesWithRemovedComponents; private readonly HashSet<Guid> entitiesWithRemovedComponents;
@ -56,7 +60,7 @@ namespace Encompass
componentIDToEntityID[componentID] = entityID; componentIDToEntityID[componentID] = entityID;
inactiveComponents.Add(componentID); inactiveComponents.Add(componentID);
Activate(componentID); MarkForActivation(componentID);
entitiesWithAddedComponents.Add(entityID); entitiesWithAddedComponents.Add(entityID);
@ -150,17 +154,30 @@ namespace Encompass
IDToComponent[componentID] = newComponentValue; IDToComponent[componentID] = newComponentValue;
} }
internal void RemoveAllComponentsFromEntity(Guid entityID) internal void MarkAllComponentsOnEntityForRemoval(Guid entityID)
{ {
var componentIDs = entityIDToComponentIDs[entityID]; foreach (var componentID in GetComponentIDsByEntityID(entityID))
for (int i = componentIDs.Count - 1; i >= 0; i--)
{ {
Remove(componentIDs[i]); MarkForRemoval(componentID);
} }
} }
internal void Activate(Guid componentID) 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)
{ {
if (inactiveComponents.Remove(componentID)) if (inactiveComponents.Remove(componentID))
{ {
@ -171,7 +188,22 @@ namespace Encompass
entitiesWithAddedComponents.Add(entityID); entitiesWithAddedComponents.Add(entityID);
} }
internal void Deactivate(Guid componentID) internal void MarkForDeactivation(Guid componentID)
{
componentsMarkedForDeactivation.Add(componentID);
}
internal void DeactivateMarkedComponents()
{
foreach (var componentID in componentsMarkedForDeactivation)
{
Deactivate(componentID);
}
componentsMarkedForDeactivation.Clear();
}
private void Deactivate(Guid componentID)
{ {
if (activeComponents.Remove(componentID)) if (activeComponents.Remove(componentID))
{ {
@ -182,7 +214,22 @@ namespace Encompass
entitiesWithRemovedComponents.Add(entityID); entitiesWithRemovedComponents.Add(entityID);
} }
internal void Remove(Guid componentID) internal void MarkForRemoval(Guid componentID)
{
componentsMarkedForRemoval.Add(componentID);
}
internal void RemoveMarkedComponents()
{
foreach (var componentID in componentsMarkedForRemoval)
{
Remove(componentID);
}
componentsMarkedForRemoval.Clear();
}
private void Remove(Guid componentID)
{ {
var component = IDToComponent[componentID]; var component = IDToComponent[componentID];
var type = componentIDToType[componentID]; var type = componentIDToType[componentID];

View File

@ -74,11 +74,6 @@ namespace Encompass
protected TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent protected TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent
{ {
if (!readTypes.Contains(typeof(TComponent)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
if (componentManager.GetComponentTypeByID(componentID) != typeof(TComponent)) if (componentManager.GetComponentTypeByID(componentID) != typeof(TComponent))
{ {
throw new ComponentTypeMismatchException("Expected Component to be of type {0} but was actually of type {1}", typeof(TComponent).Name, componentManager.GetComponentTypeByID(componentID).Name); throw new ComponentTypeMismatchException("Expected Component to be of type {0} but was actually of type {1}", typeof(TComponent).Name, componentManager.GetComponentTypeByID(componentID).Name);
@ -89,73 +84,36 @@ namespace Encompass
protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
{ {
if (!readTypes.Contains(typeof(TComponent)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return componentManager.GetActiveComponentsByType<TComponent>(); return componentManager.GetActiveComponentsByType<TComponent>();
} }
protected ValueTuple<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent protected ValueTuple<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
{ {
if (!readTypes.Contains(typeof(TComponent)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return componentManager.GetActiveComponentByType<TComponent>(); return componentManager.GetActiveComponentByType<TComponent>();
} }
protected Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent protected Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{ {
if (!writeTypes.Contains(typeof(TComponent)))
{
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return componentManager.AddComponent(entity.ID, component); return componentManager.AddComponent(entity.ID, component);
} }
protected Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent protected Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent
{ {
if (!writeTypes.Contains(typeof(TComponent)))
{
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return componentManager.AddDrawComponent(entity.ID, component, layer); return componentManager.AddDrawComponent(entity.ID, component, layer);
} }
protected void ActivateComponent(Guid componentID) protected void ActivateComponent(Guid componentID)
{ {
var type = componentManager.GetComponentTypeByID(componentID); componentManager.MarkForActivation(componentID);
if (!writeTypes.Contains(type))
{
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, type.Name);
}
componentManager.Activate(componentID);
} }
protected void DeactivateComponent(Guid componentID) protected void DeactivateComponent(Guid componentID)
{ {
var type = componentManager.GetComponentTypeByID(componentID); componentManager.MarkForDeactivation(componentID);
if (!writeTypes.Contains(type))
{
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, type.Name);
}
componentManager.Deactivate(componentID);
} }
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (!readTypes.Contains(typeof(TComponent)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return componentManager.GetComponentsByEntityAndType<TComponent>(entity.ID); return componentManager.GetComponentsByEntityAndType<TComponent>(entity.ID);
} }
@ -166,11 +124,6 @@ namespace Encompass
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (!readTypes.Contains(typeof(TComponent)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return componentManager.EntityHasComponentOfType<TComponent>(entity.ID); return componentManager.EntityHasComponentOfType<TComponent>(entity.ID);
} }
@ -193,11 +146,10 @@ namespace Encompass
{ {
if (!writeTypes.Contains(typeof(TMessage))) if (!writeTypes.Contains(typeof(TMessage)))
{ {
throw new IllegalWriteException("Engine {0} tried to emit undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name); throw new IllegalWriteException("Engine {0} tried to write undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
} }
messageManager.AddMessage(message); messageManager.AddMessage(message);
} }
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
@ -222,11 +174,6 @@ namespace Encompass
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{ {
if (!readTypes.Contains(typeof(TComponent)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
return componentManager.GetActiveComponentsByType<TComponent>().Any(); return componentManager.GetActiveComponentsByType<TComponent>().Any();
} }
@ -237,13 +184,7 @@ namespace Encompass
protected void RemoveComponent(Guid componentID) protected void RemoveComponent(Guid componentID)
{ {
var type = componentManager.GetComponentTypeByID(componentID); componentManager.MarkForRemoval(componentID);
if (!writeTypes.Contains(type))
{
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, type.Name);
}
componentManager.Remove(componentID);
} }
} }
} }

View File

@ -56,7 +56,7 @@ namespace Encompass
{ {
foreach (var entityID in entitiesMarkedForDestroy) foreach (var entityID in entitiesMarkedForDestroy)
{ {
componentManager.RemoveAllComponentsFromEntity(entityID); componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
IDToEntity.Remove(entityID); IDToEntity.Remove(entityID);
entityToEntityTrackers.Remove(entityID); entityToEntityTrackers.Remove(entityID);
componentManager.RegisterDestroyedEntity(entityID); componentManager.RegisterDestroyedEntity(entityID);

View File

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

View File

@ -35,6 +35,10 @@ namespace Encompass
messageManager.ClearMessages(); messageManager.ClearMessages();
entityManager.DestroyMarkedEntities(); entityManager.DestroyMarkedEntities();
componentManager.ActivateMarkedComponents();
componentManager.DeactivateMarkedComponents();
componentManager.RemoveMarkedComponents();
entityManager.CheckEntitiesWithAddedComponents(); entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents(); entityManager.CheckEntitiesWithRemovedComponents();
} }

View File

@ -53,7 +53,7 @@ namespace Encompass
public void DeactivateComponent(Guid componentID) public void DeactivateComponent(Guid componentID)
{ {
componentManager.Deactivate(componentID); componentManager.MarkForDeactivation(componentID);
} }
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
@ -65,12 +65,7 @@ namespace Encompass
engines.Add(engine); engines.Add(engine);
engineGraph.AddVertex(engine); engineGraph.AddVertex(engine);
if (engine is IEntityTracker) foreach (var writeType in engine.writeTypes.Where((type) => type.GetInterfaces().Contains(typeof(IMessage))))
{
entityManager.RegisterEntityTracker(engine as IEntityTracker);
}
foreach (var writeType in engine.writeTypes)
{ {
if (!typeToEmitters.ContainsKey(writeType)) if (!typeToEmitters.ContainsKey(writeType))
{ {
@ -98,7 +93,7 @@ namespace Encompass
} }
} }
foreach (var readType in engine.readTypes) foreach (var readType in engine.readTypes.Where((type) => type.GetInterfaces().Contains(typeof(IMessage))))
{ {
if (!typeToReaders.ContainsKey(readType)) if (!typeToReaders.ContainsKey(readType))
{ {
@ -229,6 +224,10 @@ namespace Encompass
renderManager renderManager
); );
componentManager.ActivateMarkedComponents();
componentManager.DeactivateMarkedComponents();
componentManager.RemoveMarkedComponents();
entityManager.CheckEntitiesWithAddedComponents(); entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents(); entityManager.CheckEntitiesWithRemovedComponents();

View File

@ -24,7 +24,7 @@ namespace Tests
static IEnumerable<(Guid, MockComponent)> gottenMockComponentIDPairs = Enumerable.Empty<(Guid, MockComponent)>(); static IEnumerable<(Guid, MockComponent)> gottenMockComponentIDPairs = Enumerable.Empty<(Guid, MockComponent)>();
static (Guid, MockComponent) gottenMockComponentIDPair; static (Guid, MockComponent) gottenMockComponentIDPair;
[Reads(typeof(EntityMessage), typeof(MockComponent))] [Reads(typeof(EntityMessage))]
class GetMockComponentsEngine : Engine class GetMockComponentsEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -38,7 +38,7 @@ namespace Tests
} }
} }
[Reads(typeof(EntityMessage), typeof(MockComponent))] [Reads(typeof(EntityMessage))]
class GetMockComponentEngine : Engine class GetMockComponentEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -56,7 +56,7 @@ namespace Tests
public MockComponent mockComponent; public MockComponent mockComponent;
} }
[Reads(typeof(AddComponentTestMessage), typeof(MockComponent))] [Reads(typeof(AddComponentTestMessage))]
class AddComponentEngine : Engine class AddComponentEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -161,7 +161,7 @@ namespace Tests
public Entity entity; public Entity entity;
} }
[Reads(typeof(HasComponentTestMessage), typeof(MockComponent))] [Reads(typeof(HasComponentTestMessage))]
class HasComponentTestEngine : Engine class HasComponentTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -201,7 +201,7 @@ namespace Tests
public Entity entity; public Entity entity;
} }
[Reads(typeof(HasComponentWhenInactiveTestMessage), typeof(MockComponent))] [Reads(typeof(HasComponentWhenInactiveTestMessage))]
class HasComponentWhenInactiveTestEngine : Engine class HasComponentWhenInactiveTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -243,7 +243,7 @@ namespace Tests
public Guid componentID; public Guid componentID;
} }
[Reads(typeof(RemoveComponentTestMessage), typeof(MockComponent))] [Reads(typeof(RemoveComponentTestMessage))]
[Writes(typeof(MockComponent))] [Writes(typeof(MockComponent))]
class RemoveComponentTestEngine : Engine class RemoveComponentTestEngine : Engine
{ {
@ -252,9 +252,36 @@ namespace Tests
foreach (var removeComponentMessage in ReadMessages<RemoveComponentTestMessage>()) foreach (var removeComponentMessage in ReadMessages<RemoveComponentTestMessage>())
{ {
RemoveComponent(removeComponentMessage.componentID); RemoveComponent(removeComponentMessage.componentID);
}
}
}
Assert.IsFalse(HasComponent<MockComponent>(removeComponentMessage.entity)); [Reads(typeof(RemoveComponentTestMessage))]
Assert.IsEmpty(GetComponents<MockComponent>(removeComponentMessage.entity)); [Writes(typeof(CheckHasMockComponentMessage))]
class DoRemoveCheckEngine : Engine
{
private Entity entity;
public DoRemoveCheckEngine(Entity entity)
{
this.entity = entity;
}
public override void Update(double dt)
{
if (SomeMessage<RemoveComponentTestMessage>())
{
CheckHasMockComponentMessage checkHasMockComponentMessage;
checkHasMockComponentMessage.entity = entity;
checkHasMockComponentMessage.shouldHaveComponent = true;
EmitMessage(checkHasMockComponentMessage);
}
else
{
CheckHasMockComponentMessage checkHasMockComponentMessage;
checkHasMockComponentMessage.entity = entity;
checkHasMockComponentMessage.shouldHaveComponent = false;
EmitMessage(checkHasMockComponentMessage);
} }
} }
} }
@ -263,10 +290,12 @@ namespace Tests
public void RemoveComponent() public void RemoveComponent()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new RemoveComponentTestEngine());
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
worldBuilder.AddEngine(new RemoveComponentTestEngine());
worldBuilder.AddEngine(new CheckHasMockComponentEngine());
worldBuilder.AddEngine(new DoRemoveCheckEngine(entity));
MockComponent mockComponent; MockComponent mockComponent;
mockComponent.myInt = 3; mockComponent.myInt = 3;
mockComponent.myString = "hello"; mockComponent.myString = "hello";
@ -280,7 +309,6 @@ namespace Tests
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);
} }
@ -290,7 +318,7 @@ namespace Tests
public Guid componentID; public Guid componentID;
} }
[Reads(typeof(ActivateComponentMessage), typeof(MockComponent))] [Reads(typeof(ActivateComponentMessage))]
[Writes(typeof(MockComponent))] [Writes(typeof(MockComponent))]
class ActivateComponentEngine : Engine class ActivateComponentEngine : Engine
{ {
@ -299,7 +327,61 @@ namespace Tests
foreach (var activateComponentMessage in ReadMessages<ActivateComponentMessage>()) foreach (var activateComponentMessage in ReadMessages<ActivateComponentMessage>())
{ {
ActivateComponent(activateComponentMessage.componentID); ActivateComponent(activateComponentMessage.componentID);
Assert.IsTrue(HasComponent<MockComponent>(activateComponentMessage.entity)); }
}
}
struct CheckHasMockComponentMessage : IMessage
{
public Entity entity;
public bool shouldHaveComponent;
}
[Reads(typeof(ActivateComponentMessage))]
[Writes(typeof(CheckHasMockComponentMessage))]
class DoActivateCheckEngine : Engine
{
private Entity entity;
public DoActivateCheckEngine(Entity entity)
{
this.entity = entity;
}
public override void Update(double dt)
{
if (SomeMessage<ActivateComponentMessage>())
{
CheckHasMockComponentMessage checkHasMockComponentMessage;
checkHasMockComponentMessage.entity = entity;
checkHasMockComponentMessage.shouldHaveComponent = false;
EmitMessage(checkHasMockComponentMessage);
}
else
{
CheckHasMockComponentMessage checkHasMockComponentMessage;
checkHasMockComponentMessage.entity = entity;
checkHasMockComponentMessage.shouldHaveComponent = true;
EmitMessage(checkHasMockComponentMessage);
}
}
}
[Reads(typeof(CheckHasMockComponentMessage))]
class CheckHasMockComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
{
if (checkHasMockComponentMessage.shouldHaveComponent)
{
Assert.IsTrue(HasComponent<MockComponent>(checkHasMockComponentMessage.entity));
}
else
{
Assert.IsFalse(HasComponent<MockComponent>(checkHasMockComponentMessage.entity));
}
} }
} }
} }
@ -308,10 +390,12 @@ namespace Tests
public void ActivateComponent() public void ActivateComponent()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateComponentEngine());
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
worldBuilder.AddEngine(new ActivateComponentEngine());
worldBuilder.AddEngine(new CheckHasMockComponentEngine());
worldBuilder.AddEngine(new DoActivateCheckEngine(entity));
MockComponent mockComponent; MockComponent mockComponent;
mockComponent.myInt = 3; mockComponent.myInt = 3;
mockComponent.myString = "hello"; mockComponent.myString = "hello";
@ -327,7 +411,8 @@ namespace Tests
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01);
world.Update(0.01);
} }
struct DeactivateComponentMessage : IMessage struct DeactivateComponentMessage : IMessage
@ -336,7 +421,7 @@ namespace Tests
public Guid componentID; public Guid componentID;
} }
[Reads(typeof(DeactivateComponentMessage), typeof(MockComponent))] [Reads(typeof(DeactivateComponentMessage))]
[Writes(typeof(MockComponent))] [Writes(typeof(MockComponent))]
class DeactivateComponentEngine : Engine class DeactivateComponentEngine : Engine
{ {
@ -345,7 +430,36 @@ namespace Tests
foreach (var deactivateComponentMessage in ReadMessages<DeactivateComponentMessage>()) foreach (var deactivateComponentMessage in ReadMessages<DeactivateComponentMessage>())
{ {
DeactivateComponent(deactivateComponentMessage.componentID); DeactivateComponent(deactivateComponentMessage.componentID);
Assert.IsFalse(HasComponent<MockComponent>(deactivateComponentMessage.entity)); }
}
}
[Reads(typeof(DeactivateComponentMessage))]
[Writes(typeof(CheckHasMockComponentMessage))]
class DoDeactivateCheckEngine : Engine
{
private Entity entity;
public DoDeactivateCheckEngine(Entity entity)
{
this.entity = entity;
}
public override void Update(double dt)
{
if (SomeMessage<DeactivateComponentMessage>())
{
CheckHasMockComponentMessage checkHasMockComponentMessage;
checkHasMockComponentMessage.entity = entity;
checkHasMockComponentMessage.shouldHaveComponent = true;
EmitMessage(checkHasMockComponentMessage);
}
else
{
CheckHasMockComponentMessage checkHasMockComponentMessage;
checkHasMockComponentMessage.entity = entity;
checkHasMockComponentMessage.shouldHaveComponent = false;
EmitMessage(checkHasMockComponentMessage);
} }
} }
} }
@ -354,10 +468,12 @@ namespace Tests
public void DeactivateComponent() public void DeactivateComponent()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new DeactivateComponentEngine());
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
worldBuilder.AddEngine(new DeactivateComponentEngine());
worldBuilder.AddEngine(new CheckHasMockComponentEngine());
worldBuilder.AddEngine(new DoDeactivateCheckEngine(entity));
MockComponent mockComponent; MockComponent mockComponent;
mockComponent.myInt = 3; mockComponent.myInt = 3;
mockComponent.myString = "hello"; mockComponent.myString = "hello";

View File

@ -23,7 +23,6 @@ namespace Tests
static List<MockMessage> resultMessages; static List<MockMessage> resultMessages;
[Reads(typeof(MockComponent))]
public class ReadComponentsTestEngine : Engine public class ReadComponentsTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -32,7 +31,6 @@ namespace Tests
} }
} }
[Reads(typeof(MockComponent))]
public class ReadComponentTestEngine : Engine public class ReadComponentTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -118,7 +116,6 @@ namespace Tests
Assert.Throws<InvalidOperationException>(() => world.Update(0.01f)); Assert.Throws<InvalidOperationException>(() => world.Update(0.01f));
} }
[Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))] [Writes(typeof(MockComponent))]
public class UpdateComponentTestEngine : Engine public class UpdateComponentTestEngine : Engine
{ {
@ -156,7 +153,6 @@ namespace Tests
Assert.AreEqual("blaze it", resultComponent.myString); Assert.AreEqual("blaze it", resultComponent.myString);
} }
[Reads(typeof(MockComponent))]
public class UndeclaredUpdateComponentTestEngine : Engine public class UndeclaredUpdateComponentTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -243,6 +239,7 @@ namespace Tests
} }
static IEnumerable<MockMessage> emptyReadMessagesResult; static IEnumerable<MockMessage> emptyReadMessagesResult;
[Reads(typeof(MockMessage))] [Reads(typeof(MockMessage))]
class ReadMessagesWhenNoneExistEngine : Engine class ReadMessagesWhenNoneExistEngine : Engine
{ {
@ -274,7 +271,7 @@ namespace Tests
var world = worldBuilder.Build(); var world = worldBuilder.Build();
var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f)); var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to emit undeclared Message MockMessage")); Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to write undeclared Message MockMessage"));
} }
static bool someTest; static bool someTest;
@ -334,7 +331,6 @@ namespace Tests
Assert.Throws<IllegalReadException>(() => world.Update(0.01f)); Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
} }
[Reads(typeof(MockComponent))]
class SomeComponentTestEngine : Engine class SomeComponentTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -356,32 +352,9 @@ namespace Tests
world.Update(0.01); world.Update(0.01);
} }
class UndeclaredSomeComponentEngine : Engine
{
public override void Update(double dt)
{
SomeComponent<MockComponent>();
}
}
[Test]
public void UndeclaredSomeComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new UndeclaredSomeComponentEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, new MockComponent());
var world = worldBuilder.Build();
Assert.Throws<IllegalReadException>(() => world.Update(0.01));
}
static ValueTuple<Guid, MockComponent> pairA; static ValueTuple<Guid, MockComponent> pairA;
static ValueTuple<Guid, MockComponent> pairB; static ValueTuple<Guid, MockComponent> pairB;
[Reads(typeof(MockComponent))]
class SameValueComponentReadEngine : Engine class SameValueComponentReadEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -421,7 +394,6 @@ namespace Tests
static IEnumerable<ValueTuple<Guid, MockComponent>> emptyComponentReadResult; static IEnumerable<ValueTuple<Guid, MockComponent>> emptyComponentReadResult;
[Reads(typeof(MockComponent))]
class ReadEmptyMockComponentsEngine : Engine class ReadEmptyMockComponentsEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -444,7 +416,6 @@ namespace Tests
struct DestroyerComponent : IComponent { } struct DestroyerComponent : IComponent { }
[Reads(typeof(DestroyerComponent))]
class DestroyerEngine : Engine class DestroyerEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -460,7 +431,6 @@ namespace Tests
static IEnumerable<ValueTuple<Guid, MockComponent>> results; static IEnumerable<ValueTuple<Guid, MockComponent>> results;
[Reads(typeof(MockComponent))]
class ReaderEngine : Engine class ReaderEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -498,7 +468,6 @@ namespace Tests
Assert.That(results, Does.Not.Contain((componentBID, mockComponent))); Assert.That(results, Does.Not.Contain((componentBID, mockComponent)));
} }
[Reads(typeof(DestroyerComponent), typeof(MockComponent))]
[Writes(typeof(MockComponent))] [Writes(typeof(MockComponent))]
class DestroyAndAddComponentEngine : Engine class DestroyAndAddComponentEngine : Engine
{ {
@ -534,7 +503,6 @@ namespace Tests
static Entity entityFromComponentIDResult; static Entity entityFromComponentIDResult;
[Reads(typeof(MockComponent))]
class GetEntityFromComponentIDEngine : Engine class GetEntityFromComponentIDEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -565,7 +533,6 @@ namespace Tests
static MockComponent mockComponentByIDResult; static MockComponent mockComponentByIDResult;
[Reads(typeof(MockComponent))]
class GetComponentByIDEngine : Engine class GetComponentByIDEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -595,7 +562,6 @@ namespace Tests
struct OtherComponent : IComponent { } struct OtherComponent : IComponent { }
[Reads(typeof(MockComponent), typeof(OtherComponent))]
class GetComponentByIDWithTypeMismatchEngine : Engine class GetComponentByIDWithTypeMismatchEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -626,7 +592,6 @@ namespace Tests
struct EntityIDComponent : IComponent { public Guid entityID; } struct EntityIDComponent : IComponent { public Guid entityID; }
static bool hasEntity; static bool hasEntity;
[Reads(typeof(EntityIDComponent))]
class HasEntityTestEngine : Engine class HasEntityTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -663,5 +628,19 @@ namespace Tests
Assert.IsFalse(hasEntity); Assert.IsFalse(hasEntity);
} }
[Writes(typeof(MockComponent), typeof(MockMessage))]
class EngineThatWritesComponentAndMessage : Engine
{
public override void Update(double dt) { }
}
[Test]
public void EngineWritesComponentAndMessage()
{
var worldBuilder = new WorldBuilder();
Assert.Throws<ComponentAndMessageWriteException>(() => worldBuilder.AddEngine(new EngineThatWritesComponentAndMessage()));
}
} }
} }

View File

@ -216,7 +216,7 @@ namespace Tests
struct CMessage : IMessage { } struct CMessage : IMessage { }
struct DMessage : IMessage { } struct DMessage : IMessage { }
[Writes(typeof(AComponent), typeof(AMessage))] [Writes(typeof(AMessage))]
class AEngine : Engine class AEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -225,7 +225,7 @@ namespace Tests
} }
} }
[Writes(typeof(BComponent), typeof(BMessage))] [Writes(typeof(BMessage))]
class BEngine : Engine class BEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)