Merge pull request #2 from thatcosmonaut/rework_component_write

Rework Component Read/Write
pull/5/head
thatcosmonaut 2019-07-17 12:12:44 -07:00 committed by GitHub
commit ac744f436b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 958 additions and 765 deletions

View File

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

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class Writes : Attribute
{
public readonly HashSet<Type> writeTypes;
public Writes(params Type[] writeTypes)
{
foreach (var writeType in writeTypes)
{
if (!writeType.GetInterfaces().Contains(typeof(IMessage)) && !writeType.GetInterfaces().Contains(typeof(IComponent)))
{
throw new IllegalWriteTypeException("{0} must be a Message or Component", writeType.Name);
}
}
this.writeTypes = new HashSet<Type>(writeTypes);
}
}
}

View File

@ -18,10 +18,6 @@ namespace Encompass
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> componentsToActivate = new HashSet<Guid>();
private readonly HashSet<Guid> componentsToDeactivate = new HashSet<Guid>();
private readonly HashSet<Guid> componentsToRemove = 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;
@ -60,7 +56,7 @@ namespace Encompass
componentIDToEntityID[componentID] = entityID; componentIDToEntityID[componentID] = entityID;
inactiveComponents.Add(componentID); inactiveComponents.Add(componentID);
MarkForActivation(componentID); Activate(componentID);
entitiesWithAddedComponents.Add(entityID); entitiesWithAddedComponents.Add(entityID);
@ -158,67 +154,35 @@ namespace Encompass
{ {
var componentIDs = entityIDToComponentIDs[entityID]; var componentIDs = entityIDToComponentIDs[entityID];
foreach (var componentID in componentIDs) for (int i = componentIDs.Count - 1; i >= 0; i--)
{ {
MarkForRemoval(componentID); Remove(componentIDs[i]);
} }
} }
internal void MarkForActivation(Guid componentID) internal void Activate(Guid componentID)
{ {
componentsToActivate.Add(componentID); if (inactiveComponents.Remove(componentID))
{
activeComponents.Add(componentID);
}
var entityID = GetEntityIDByComponentID(componentID); var entityID = GetEntityIDByComponentID(componentID);
entitiesWithAddedComponents.Add(entityID); entitiesWithAddedComponents.Add(entityID);
} }
internal void MarkForDeactivation(Guid componentID) internal void Deactivate(Guid componentID)
{ {
componentsToDeactivate.Add(componentID);
var entityID = GetEntityIDByComponentID(componentID);
entitiesWithRemovedComponents.Add(entityID);
}
internal void MarkForRemoval(Guid componentID)
{
componentsToRemove.Add(componentID);
var entityID = GetEntityIDByComponentID(componentID);
entitiesWithRemovedComponents.Add(entityID);
}
internal void ActivateMarkedComponents()
{
foreach (var componentID in componentsToActivate)
{
var component = IDToComponent[componentID];
if (inactiveComponents.Remove(componentID))
{
activeComponents.Add(componentID);
}
}
componentsToActivate.Clear();
}
internal void DeactivateMarkedComponents()
{
foreach (var componentID in componentsToDeactivate)
{
var component = IDToComponent[componentID];
if (activeComponents.Remove(componentID)) if (activeComponents.Remove(componentID))
{ {
inactiveComponents.Add(componentID); inactiveComponents.Add(componentID);
} }
var entityID = GetEntityIDByComponentID(componentID);
entitiesWithRemovedComponents.Add(entityID);
} }
componentsToDeactivate.Clear(); internal void Remove(Guid componentID)
}
public void RemoveMarkedComponents()
{
foreach (var componentID in componentsToRemove)
{ {
var component = IDToComponent[componentID]; var component = IDToComponent[componentID];
var type = componentIDToType[componentID]; var type = componentIDToType[componentID];
@ -238,9 +202,8 @@ namespace Encompass
typeToComponentIDs[type].Remove(componentID); typeToComponentIDs[type].Remove(componentID);
drawLayerManager.UnRegisterComponentWithLayer(componentID); drawLayerManager.UnRegisterComponentWithLayer(componentID);
}
componentsToRemove.Clear(); entitiesWithRemovedComponents.Add(entityID);
} }
public void RegisterDestroyedEntity(Guid entityID) public void RegisterDestroyedEntity(Guid entityID)

View File

@ -8,9 +8,8 @@ namespace Encompass
{ {
public abstract class Engine public abstract class Engine
{ {
internal readonly List<Type> mutateComponentTypes = new List<Type>(); internal readonly HashSet<Type> writeTypes = new HashSet<Type>();
internal readonly List<Type> emitMessageTypes = new List<Type>(); internal readonly HashSet<Type> readTypes = new HashSet<Type>();
internal readonly List<Type> readMessageTypes = new List<Type>();
private EntityManager entityManager; private EntityManager entityManager;
private ComponentManager componentManager; private ComponentManager componentManager;
@ -18,22 +17,16 @@ namespace Encompass
protected Engine() protected Engine()
{ {
var mutatesAttribute = GetType().GetCustomAttribute<Mutates>(false); var writesAttribute = GetType().GetCustomAttribute<Writes>(false);
if (mutatesAttribute != null) if (writesAttribute != null)
{ {
mutateComponentTypes = mutatesAttribute.mutateComponentTypes; writeTypes = writesAttribute.writeTypes;
}
var emitsAttribute = GetType().GetCustomAttribute<Emits>(false);
if (emitsAttribute != null)
{
emitMessageTypes = emitsAttribute.emitMessageTypes;
} }
var readsAttribute = GetType().GetCustomAttribute<Reads>(false); var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null) if (readsAttribute != null)
{ {
readMessageTypes = readsAttribute.readMessageTypes; readTypes = readsAttribute.readTypes;
} }
} }
@ -81,37 +74,110 @@ namespace Encompass
protected TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent protected TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent
{ {
if (componentManager.GetComponentTypeByID(componentID) == typeof(TComponent)) if (!readTypes.Contains(typeof(TComponent)))
{ {
return (TComponent)componentManager.GetComponentByID(componentID); throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
} }
else
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);
} }
return (TComponent)componentManager.GetComponentByID(componentID);
} }
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
{
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);
}
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);
}
protected void ActivateComponent(Guid componentID)
{
var type = componentManager.GetComponentTypeByID(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)
{
var type = componentManager.GetComponentTypeByID(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
{
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);
}
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return GetComponents<TComponent>(entity).First();
}
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return componentManager.EntityHasComponentOfType<TComponent>(entity.ID);
}
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 (mutateComponentTypes.Contains(typeof(TComponent))) if (!writeTypes.Contains(typeof(TComponent)))
{ {
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
componentManager.UpdateComponent(componentID, newComponent); componentManager.UpdateComponent(componentID, newComponent);
} }
else
{
throw new IllegalComponentMutationException("Engine {0} tried to mutate undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
}
}
protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
{ {
@ -120,43 +186,59 @@ namespace Encompass
protected void EmitMessage<TMessage>(TMessage message) where TMessage : struct, IMessage protected void EmitMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
{ {
if (emitMessageTypes.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);
}
messageManager.AddMessage(message); messageManager.AddMessage(message);
}
else
{
throw new IllegalMessageEmitException("Engine {0} tried to emit undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
}
} }
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
{ {
if (readMessageTypes.Contains(typeof(TMessage))) if (!readTypes.Contains(typeof(TMessage)))
{ {
return messageManager.GetMessagesByType<TMessage>(); throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
}
else
{
throw new IllegalMessageReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
}
} }
protected bool Some<TMessage>() where TMessage : struct, IMessage return messageManager.GetMessagesByType<TMessage>();
}
protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
{ {
if (readMessageTypes.Contains(typeof(TMessage))) if (!readTypes.Contains(typeof(TMessage)))
{ {
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
}
return messageManager.GetMessagesByType<TMessage>().Any(); return messageManager.GetMessagesByType<TMessage>().Any();
} }
else
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{ {
throw new IllegalMessageReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name); 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();
} }
protected void Destroy(Guid entityID) protected void Destroy(Guid entityID)
{ {
entityManager.MarkForDestroy(entityID); entityManager.MarkForDestroy(entityID);
} }
protected void RemoveComponent(Guid componentID)
{
var type = componentManager.GetComponentTypeByID(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

@ -1,59 +0,0 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using Encompass.Exceptions;
namespace Encompass.Engines
{
public abstract class Detector : Engine, IEntityTracker
{
private readonly List<Type> componentTypes = new List<Type>();
private readonly EntityTracker entityTracker = new EntityTracker();
public IEnumerable<Type> ComponentTypes { get { return componentTypes; } }
protected Detector() : base()
{
var detectsAttribute = GetType().GetCustomAttribute<Detects>(false);
if (detectsAttribute != null)
{
componentTypes = detectsAttribute.componentTypes;
}
else
{
throw new DetectorWithoutComponentTypesException("Detector {0} does not have any component types declared. Use the Detects attribute to declare component types", GetType().Name);
}
}
public override void Update(double dt)
{
foreach (var id in entityTracker.TrackedEntityIDs)
{
Detect(GetEntity(id), dt);
}
}
public abstract void Detect(Entity entity, double dt);
public bool CheckAndTrackEntity(Guid entityID)
{
var entity = GetEntity(entityID);
var shouldTrack = CheckEntity(entity);
if (shouldTrack) { entityTracker.TrackEntity(entityID); }
return shouldTrack;
}
public bool CheckAndUntrackEntity(Guid entityID)
{
var entity = GetEntity(entityID);
var shouldUntrack = !CheckEntity(entity);
if (shouldUntrack) { entityTracker.UntrackEntity(entityID); }
return shouldUntrack;
}
private bool CheckEntity(Entity entity)
{
return EntityChecker.CheckEntity(entity, componentTypes);
}
}
}

View File

@ -9,10 +9,10 @@ namespace Encompass.Engines
var readsAttribute = GetType().GetCustomAttribute<Reads>(false); var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null) if (readsAttribute != null)
{ {
readsAttribute.readMessageTypes.Add(typeof(TMessage)); readsAttribute.readTypes.Add(typeof(TMessage));
} }
readMessageTypes.Add(typeof(TMessage)); readTypes.Add(typeof(TMessage));
} }
public override void Update(double dt) public override void Update(double dt)

View File

@ -1,69 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass namespace Encompass
{ {
public struct Entity public struct Entity
{ {
public readonly Guid id; public readonly Guid ID;
private readonly ComponentManager componentManager; internal Entity(Guid id)
internal Entity(Guid id, ComponentManager componentManager)
{ {
this.id = id; this.ID = id;
this.componentManager = componentManager;
}
public Guid AddComponent<TComponent>(TComponent component) where TComponent : struct, IComponent
{
return componentManager.AddComponent(id, component);
}
public Guid AddDrawComponent<TComponent>(TComponent component, int layer = 0) where TComponent : struct, IComponent
{
return componentManager.AddDrawComponent(id, component, layer);
}
public IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetComponentsByEntityAndType<TComponent>(id);
}
public ValueTuple<Guid, TComponent> GetComponent<TComponent>() where TComponent : struct, IComponent
{
return GetComponents<TComponent>().First();
}
public bool HasComponent<TComponent>() where TComponent : struct, IComponent
{
return componentManager.EntityHasComponentOfType<TComponent>(id);
}
internal bool HasComponent(Type type)
{
return componentManager.EntityHasComponentOfType(id, type);
}
public void ActivateComponent(Guid componentID)
{
componentManager.MarkForActivation(componentID);
}
public void DeactivateComponent(Guid componentID)
{
componentManager.MarkForDeactivation(componentID);
}
public void RemoveComponent(Guid componentID)
{
componentManager.MarkForRemoval(componentID);
}
internal void RemoveAllComponents()
{
componentManager.RemoveAllComponentsFromEntity(id);
} }
} }
} }

View File

@ -31,7 +31,7 @@ namespace Encompass
public Entity CreateEntity() public Entity CreateEntity()
{ {
var id = NextID(); var id = NextID();
var entity = new Entity(id, componentManager); var entity = new Entity(id);
IDToEntity[id] = entity; IDToEntity[id] = entity;
componentManager.RegisterEntity(id); componentManager.RegisterEntity(id);
return entity; return entity;
@ -56,8 +56,7 @@ namespace Encompass
{ {
foreach (var entityID in entitiesMarkedForDestroy) foreach (var entityID in entitiesMarkedForDestroy)
{ {
var entity = IDToEntity[entityID]; componentManager.RemoveAllComponentsFromEntity(entityID);
entity.RemoveAllComponents();
IDToEntity.Remove(entityID); IDToEntity.Remove(entityID);
entityToEntityTrackers.Remove(entityID); entityToEntityTrackers.Remove(entityID);
componentManager.RegisterDestroyedEntity(entityID); componentManager.RegisterDestroyedEntity(entityID);

View File

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

View File

@ -2,9 +2,9 @@ using System;
namespace Encompass.Exceptions namespace Encompass.Exceptions
{ {
public class EngineMutationConflictException : Exception public class EngineMessageSelfCycleException : Exception
{ {
public EngineMutationConflictException( public EngineMessageSelfCycleException(
string format, string format,
params object[] args params object[] args
) : base(string.Format(format, args)) { } ) : base(string.Format(format, args)) { }

View File

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

View File

@ -2,9 +2,9 @@ using System;
namespace Encompass.Exceptions namespace Encompass.Exceptions
{ {
public class IllegalMessageReadException : Exception public class IllegalReadException : Exception
{ {
public IllegalMessageReadException( public IllegalReadException(
string format, string format,
params object[] args params object[] args
) : base(string.Format(format, args)) { } ) : base(string.Format(format, args)) { }

View File

@ -2,9 +2,9 @@ using System;
namespace Encompass.Exceptions namespace Encompass.Exceptions
{ {
public class IllegalMessageEmitException : Exception public class IllegalReadTypeException : Exception
{ {
public IllegalMessageEmitException( public IllegalReadTypeException(
string format, string format,
params object[] args params object[] args
) : base(string.Format(format, args)) { } ) : base(string.Format(format, args)) { }

View File

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

View File

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

View File

@ -27,7 +27,7 @@ namespace Encompass
} }
protected List<T> _vertices = new List<T>(); protected List<T> _vertices = new List<T>();
protected Dictionary<T, List<T>> _neighbors = new Dictionary<T, List<T>>(); protected Dictionary<T, HashSet<T>> _neighbors = new Dictionary<T, HashSet<T>>();
public IEnumerable<T> Vertices { get { return _vertices; } } public IEnumerable<T> Vertices { get { return _vertices; } }
@ -40,7 +40,7 @@ namespace Encompass
if (!VertexExists(vertex)) if (!VertexExists(vertex))
{ {
_vertices.Add(vertex); _vertices.Add(vertex);
_neighbors.Add(vertex, new List<T>()); _neighbors.Add(vertex, new HashSet<T>());
} }
} }

View File

@ -1,13 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Linq;
namespace Encompass namespace Encompass
{ {
public abstract class Renderer public abstract class Renderer
{ {
private EntityManager entityManager; internal EntityManager entityManager;
private ComponentManager componentManager; internal ComponentManager componentManager;
internal void AssignEntityManager(EntityManager entityManager) internal void AssignEntityManager(EntityManager entityManager)
{ {
@ -48,5 +48,15 @@ namespace Encompass
{ {
return componentManager.GetActiveComponentByType<TComponent>(); return componentManager.GetActiveComponentByType<TComponent>();
} }
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return componentManager.GetComponentsByEntityAndType<TComponent>(entity.ID);
}
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
return GetComponents<TComponent>(entity).First();
}
} }
} }

View File

@ -47,8 +47,8 @@ namespace Encompass
private bool CheckEntity(Entity entity) private bool CheckEntity(Entity entity)
{ {
return EntityChecker.CheckEntity(entity, componentTypes) return EntityChecker.CheckEntity(componentManager, entity, componentTypes)
&& entity.HasComponent(DrawComponentType); && componentManager.EntityHasComponentOfType(entity.ID, DrawComponentType);
} }
} }
} }

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal static class EntityChecker
{
public static bool CheckEntity(ComponentManager componentManager, Entity entity, IEnumerable<Type> componentTypes)
{
return componentTypes.All((componentType) => componentManager.EntityHasComponentOfType(entity.ID, componentType));
}
}
}

View File

@ -35,10 +35,6 @@ 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

@ -17,8 +17,8 @@ namespace Encompass
private readonly DrawLayerManager drawLayerManager; private readonly DrawLayerManager drawLayerManager;
private readonly RenderManager renderManager; private readonly RenderManager renderManager;
private readonly Dictionary<Type, HashSet<Engine>> messageTypeToEmitters = new Dictionary<Type, HashSet<Engine>>(); private readonly Dictionary<Type, HashSet<Engine>> typeToEmitters = new Dictionary<Type, HashSet<Engine>>();
private readonly Dictionary<Type, HashSet<Engine>> messageTypeToReaders = new Dictionary<Type, HashSet<Engine>>(); private readonly Dictionary<Type, HashSet<Engine>> typeToReaders = new Dictionary<Type, HashSet<Engine>>();
public WorldBuilder() public WorldBuilder()
{ {
@ -41,6 +41,21 @@ namespace Encompass
messageManager.AddMessage(message); messageManager.AddMessage(message);
} }
public Guid AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
return componentManager.AddComponent(entity.ID, component);
}
public Guid AddDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent
{
return componentManager.AddDrawComponent(entity.ID, component, layer);
}
public void DeactivateComponent(Guid componentID)
{
componentManager.Deactivate(componentID);
}
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
{ {
engine.AssignEntityManager(entityManager); engine.AssignEntityManager(entityManager);
@ -55,41 +70,61 @@ namespace Encompass
entityManager.RegisterEntityTracker(engine as IEntityTracker); entityManager.RegisterEntityTracker(engine as IEntityTracker);
} }
foreach (var emitMessageType in engine.emitMessageTypes) foreach (var writeType in engine.writeTypes)
{ {
if (!messageTypeToEmitters.ContainsKey(emitMessageType)) if (!typeToEmitters.ContainsKey(writeType))
{ {
messageTypeToEmitters.Add(emitMessageType, new HashSet<Engine>()); typeToEmitters.Add(writeType, new HashSet<Engine>());
} }
messageTypeToEmitters[emitMessageType].Add(engine); typeToEmitters[writeType].Add(engine);
if (messageTypeToReaders.ContainsKey(emitMessageType)) if (typeToReaders.ContainsKey(writeType))
{ {
foreach (var reader in messageTypeToReaders[emitMessageType]) 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); engineGraph.AddEdge(engine, reader);
} }
} }
} }
foreach (var readMessageType in engine.readMessageTypes)
{
if (!messageTypeToReaders.ContainsKey(readMessageType))
{
messageTypeToReaders.Add(readMessageType, new HashSet<Engine>());
} }
messageTypeToReaders[readMessageType].Add(engine); foreach (var readType in engine.readTypes)
if (messageTypeToEmitters.ContainsKey(readMessageType))
{ {
foreach (var emitter in messageTypeToEmitters[readMessageType]) 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); engineGraph.AddEdge(emitter, engine);
} }
} }
} }
}
return engine; return engine;
} }
@ -140,41 +175,44 @@ namespace Encompass
foreach (var engine in engines) foreach (var engine in engines)
{ {
var mutateAttribute = engine.GetType().GetCustomAttribute<Mutates>(false); var writeAttribute = engine.GetType().GetCustomAttribute<Writes>(false);
if (mutateAttribute != null) if (writeAttribute != null)
{ {
foreach (var mutateComponentType in engine.GetType().GetCustomAttribute<Mutates>(false).mutateComponentTypes) foreach (var writeType in writeAttribute.writeTypes)
{ {
if (mutatedComponentTypes.Contains(mutateComponentType)) if (writeType.GetInterfaces().Contains(typeof(IComponent))) // if our write type is a component
{ {
duplicateMutations.Add(mutateComponentType); if (mutatedComponentTypes.Contains(writeType))
{
duplicateMutations.Add(writeType);
} }
else else
{ {
mutatedComponentTypes.Add(mutateComponentType); mutatedComponentTypes.Add(writeType);
} }
if (!componentToEngines.ContainsKey(mutateComponentType)) if (!componentToEngines.ContainsKey(writeType))
{ {
componentToEngines[mutateComponentType] = new List<Engine>(); componentToEngines[writeType] = new List<Engine>();
} }
componentToEngines[mutateComponentType].Add(engine); componentToEngines[writeType].Add(engine);
}
} }
} }
} }
if (duplicateMutations.Count > 0) if (duplicateMutations.Count > 0)
{ {
var errorString = "Multiple Engines mutate the same Component: "; var errorString = "Multiple Engines write the same Component: ";
foreach (var componentType in duplicateMutations) foreach (var componentType in duplicateMutations)
{ {
errorString += "\n" + errorString += "\n" +
componentType.Name + " mutated by: " + componentType.Name + " written by: " +
string.Join(", ", componentToEngines[componentType].Select((engine) => engine.GetType().Name)); string.Join(", ", componentToEngines[componentType].Select((engine) => engine.GetType().Name));
} }
throw new EngineMutationConflictException(errorString); throw new EngineWriteConflictException(errorString);
} }
var engineOrder = new List<Engine>(); var engineOrder = new List<Engine>();
@ -191,10 +229,6 @@ namespace Encompass
renderManager renderManager
); );
componentManager.ActivateMarkedComponents();
componentManager.DeactivateMarkedComponents();
componentManager.RemoveMarkedComponents();
entityManager.CheckEntitiesWithAddedComponents(); entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents(); entityManager.CheckEntitiesWithRemovedComponents();

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class Emits : Attribute
{
public readonly List<Type> emitMessageTypes;
public Emits(params Type[] emitMessageTypes)
{
this.emitMessageTypes = new List<Type>(emitMessageTypes);
}
}
}

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
[System.AttributeUsage(System.AttributeTargets.Class)]
public class Mutates : System.Attribute
{
public readonly List<Type> mutateComponentTypes;
public Mutates(params Type[] mutateComponentTypes)
{
this.mutateComponentTypes = new List<Type>(mutateComponentTypes);
}
}
}

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
[System.AttributeUsage(System.AttributeTargets.Class)]
public class Reads : System.Attribute
{
public readonly List<Type> readMessageTypes;
public Reads(params Type[] readMessageTypes)
{
this.readMessageTypes = new List<Type>(readMessageTypes);
}
}
}

View File

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

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal static class EntityChecker
{
public static bool CheckEntity(Entity entity, IEnumerable<Type> componentTypes)
{
return componentTypes.All((componentType) => entity.HasComponent(componentType));
}
}
}

377
test/ComponentTest.cs Normal file
View File

@ -0,0 +1,377 @@
using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System.Collections.Generic;
using System;
using System.Linq;
namespace Tests
{
public class ComponentTests
{
struct MockComponent : IComponent
{
public string myString;
public int myInt;
}
struct EntityMessage : IMessage
{
public Entity entity;
}
static IEnumerable<(Guid, MockComponent)> gottenMockComponentIDPairs = Enumerable.Empty<(Guid, MockComponent)>();
static (Guid, MockComponent) gottenMockComponentIDPair;
[Reads(typeof(EntityMessage), typeof(MockComponent))]
class GetMockComponentsEngine : Engine
{
public override void Update(double dt)
{
gottenMockComponentIDPairs = Enumerable.Empty<(Guid, MockComponent)>();
foreach (var entityMessage in ReadMessages<EntityMessage>())
{
gottenMockComponentIDPairs = GetComponents<MockComponent>(entityMessage.entity);
}
}
}
[Reads(typeof(EntityMessage), typeof(MockComponent))]
class GetMockComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var entityMessage in ReadMessages<EntityMessage>())
{
gottenMockComponentIDPair = GetComponent<MockComponent>(entityMessage.entity);
}
}
}
struct AddComponentTestMessage : IMessage
{
public Entity entity;
public MockComponent mockComponent;
}
[Reads(typeof(AddComponentTestMessage), typeof(MockComponent))]
class AddComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var addComponentTestMessage in ReadMessages<AddComponentTestMessage>())
{
Assert.IsTrue(HasComponent<MockComponent>(addComponentTestMessage.entity));
Assert.That(GetComponent<MockComponent>(addComponentTestMessage.entity).Item2, Is.EqualTo(addComponentTestMessage.mockComponent));
}
}
}
[Test]
public void AddComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AddComponentEngine());
worldBuilder.AddEngine(new GetMockComponentEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.AddComponent(entity, mockComponent);
AddComponentTestMessage addComponentTestMessage;
addComponentTestMessage.entity = entity;
addComponentTestMessage.mockComponent = mockComponent;
worldBuilder.EmitMessage(addComponentTestMessage);
var world = worldBuilder.Build();
world.Update(0.01);
}
[Test]
public void GetComponents()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new GetMockComponentsEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponentA;
mockComponentA.myInt = 3;
mockComponentA.myString = "hello";
MockComponent mockComponentB;
mockComponentB.myInt = 5;
mockComponentB.myString = "wassup";
MockComponent mockComponentC;
mockComponentC.myInt = 1;
mockComponentC.myString = "howdy";
var componentAID = worldBuilder.AddComponent(entity, mockComponentA);
var componentBID = worldBuilder.AddComponent(entity, mockComponentB);
var componentCID = worldBuilder.AddComponent(entity, mockComponentC);
EntityMessage entityMessage;
entityMessage.entity = entity;
worldBuilder.EmitMessage(entityMessage);
var world = worldBuilder.Build();
world.Update(0.01);
gottenMockComponentIDPairs.Should().Contain((componentAID, mockComponentA));
gottenMockComponentIDPairs.Should().Contain((componentBID, mockComponentB));
gottenMockComponentIDPairs.Should().Contain((componentCID, mockComponentC));
}
[Test]
public void GetComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new GetMockComponentEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = worldBuilder.AddComponent<MockComponent>(entity, mockComponent);
EntityMessage entityMessage;
entityMessage.entity = entity;
worldBuilder.EmitMessage(entityMessage);
var world = worldBuilder.Build();
world.Update(0.01);
Assert.AreEqual((componentID, mockComponent), gottenMockComponentIDPair);
}
struct HasComponentTestMessage : IMessage
{
public Entity entity;
}
[Reads(typeof(HasComponentTestMessage), typeof(MockComponent))]
class HasComponentTestEngine : Engine
{
public override void Update(double dt)
{
foreach (var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
{
Assert.IsTrue(HasComponent<MockComponent>(hasComponentTestEngine.entity));
}
}
}
[Test]
public void HasComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new HasComponentTestEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.AddComponent(entity, mockComponent);
HasComponentTestMessage hasComponentTestMessage;
hasComponentTestMessage.entity = entity;
worldBuilder.EmitMessage(hasComponentTestMessage);
var world = worldBuilder.Build();
world.Update(0.01);
}
struct HasComponentWhenInactiveTestMessage : IMessage
{
public Entity entity;
}
[Reads(typeof(HasComponentWhenInactiveTestMessage))]
class HasComponentWhenInactiveTestEngine : Engine
{
public override void Update(double dt)
{
foreach (var hasComponentTestEngine in ReadMessages<HasComponentWhenInactiveTestMessage>())
{
Assert.IsFalse(HasComponent<MockComponent>(hasComponentTestEngine.entity));
}
}
}
[Test]
public void HasComponentWhenInactive()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new HasComponentWhenInactiveTestEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = worldBuilder.AddComponent(entity, mockComponent);
HasComponentWhenInactiveTestMessage testMessage;
testMessage.entity = entity;
worldBuilder.EmitMessage(testMessage);
worldBuilder.DeactivateComponent(componentID);
var world = worldBuilder.Build();
world.Update(0.01f);
}
struct RemoveComponentTestMessage : IMessage
{
public Entity entity;
public Guid componentID;
}
[Reads(typeof(RemoveComponentTestMessage), typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class RemoveComponentTestEngine : Engine
{
public override void Update(double dt)
{
foreach (var removeComponentMessage in ReadMessages<RemoveComponentTestMessage>())
{
RemoveComponent(removeComponentMessage.componentID);
Assert.IsFalse(HasComponent<MockComponent>(removeComponentMessage.entity));
Assert.IsEmpty(GetComponents<MockComponent>(removeComponentMessage.entity));
}
}
}
[Test]
public void RemoveComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new RemoveComponentTestEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = worldBuilder.AddComponent(entity, mockComponent);
RemoveComponentTestMessage removeComponentMessage;
removeComponentMessage.entity = entity;
removeComponentMessage.componentID = componentID;
worldBuilder.EmitMessage(removeComponentMessage);
var world = worldBuilder.Build();
world.Update(0.01f);
}
struct ActivateComponentMessage : IMessage
{
public Entity entity;
public Guid componentID;
}
[Reads(typeof(ActivateComponentMessage))]
[Writes(typeof(MockComponent))]
class ActivateComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var activateComponentMessage in ReadMessages<ActivateComponentMessage>())
{
ActivateComponent(activateComponentMessage.componentID);
Assert.IsTrue(HasComponent<MockComponent>(activateComponentMessage.entity));
}
}
}
[Test]
public void ActivateComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateComponentEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = worldBuilder.AddComponent(entity, mockComponent);
worldBuilder.DeactivateComponent(componentID);
ActivateComponentMessage activateMessage;
activateMessage.entity = entity;
activateMessage.componentID = componentID;
worldBuilder.EmitMessage(activateMessage);
var world = worldBuilder.Build();
world.Update(0.01f);
}
struct DeactivateComponentMessage : IMessage
{
public Entity entity;
public Guid componentID;
}
[Reads(typeof(DeactivateComponentMessage))]
[Writes(typeof(MockComponent))]
class DeactivateComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var deactivateComponentMessage in ReadMessages<DeactivateComponentMessage>())
{
DeactivateComponent(deactivateComponentMessage.componentID);
Assert.IsFalse(HasComponent<MockComponent>(deactivateComponentMessage.entity));
}
}
}
[Test]
public void DeactivateComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new DeactivateComponentEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = worldBuilder.AddComponent(entity, mockComponent);
DeactivateComponentMessage deactivateComponentMessage;
deactivateComponentMessage.entity = entity;
deactivateComponentMessage.componentID = componentID;
worldBuilder.EmitMessage(deactivateComponentMessage);
var world = worldBuilder.Build();
world.Update(0.01);
}
}
}

View File

@ -1,123 +0,0 @@
using NUnit.Framework;
using FluentAssertions;
using Encompass;
using Encompass.Engines;
using Encompass.Exceptions;
using System;
using System.Collections.Generic;
namespace Tests
{
class DetectorTest
{
class NoComponentTypesDetector : Detector
{
public override void Detect(Entity entity, double dt) { }
}
[Test]
public void DetectorWithNoComponentTypes()
{
var worldBuilder = new WorldBuilder();
Action addEngine = () => worldBuilder.AddEngine(new NoComponentTypesDetector());
addEngine.Should().Throw<DetectorWithoutComponentTypesException>();
}
struct AComponent : IComponent { }
struct BComponent : IComponent { }
struct CComponent : IComponent { }
static List<Entity> trackedEntities = new List<Entity>();
[Detects(typeof(AComponent), typeof(BComponent))]
class TestDetector : Detector
{
public override void Detect(Entity entity, double dt)
{
trackedEntities.Add(entity);
}
}
[Test]
public void CheckAndTrackEntities()
{
var worldBuilder = new WorldBuilder();
var detector = worldBuilder.AddEngine(new TestDetector());
var entityToTrack = worldBuilder.CreateEntity();
entityToTrack.AddComponent(new AComponent());
entityToTrack.AddComponent(new BComponent());
var entityNotToTrack = worldBuilder.CreateEntity();
entityNotToTrack.AddComponent(new AComponent());
entityNotToTrack.AddComponent(new CComponent());
var entityWithDeactivatedComponents = worldBuilder.CreateEntity();
var aComponent = entityWithDeactivatedComponents.AddComponent(new AComponent());
entityWithDeactivatedComponents.AddComponent(new BComponent());
entityWithDeactivatedComponents.DeactivateComponent(aComponent);
var entityWithOneDeactivatedComponent = worldBuilder.CreateEntity();
var inactiveComponent = entityWithOneDeactivatedComponent.AddComponent(new AComponent());
entityWithOneDeactivatedComponent.AddComponent(new AComponent());
entityWithOneDeactivatedComponent.AddComponent(new BComponent());
entityWithOneDeactivatedComponent.DeactivateComponent(inactiveComponent);
var world = worldBuilder.Build();
world.Update(0.01);
trackedEntities.Should().Contain(entityToTrack);
trackedEntities.Should().NotContain(entityNotToTrack);
trackedEntities.Should().NotContain(entityWithDeactivatedComponents);
trackedEntities.Should().Contain(entityWithOneDeactivatedComponent);
}
[Test]
public void EntityUntrackedWhenComponentRemoved()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new TestDetector());
var entityToUntrack = worldBuilder.CreateEntity();
entityToUntrack.AddComponent(new AComponent());
var bComponent = entityToUntrack.AddComponent(new BComponent());
var world = worldBuilder.Build();
// have to update twice because we are updating from outside the world
entityToUntrack.RemoveComponent(bComponent);
world.Update(0.01);
trackedEntities.Clear();
world.Update(0.01);
trackedEntities.Should().NotContain(entityToUntrack);
}
[Test]
public void DetectCalledPerTrackedEntityOnWorldUpdat()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new TestDetector());
var entityOne = worldBuilder.CreateEntity();
entityOne.AddComponent(new AComponent());
entityOne.AddComponent(new BComponent());
var entityTwo = worldBuilder.CreateEntity();
entityTwo.AddComponent(new AComponent());
entityTwo.AddComponent(new BComponent());
trackedEntities.Clear();
var world = worldBuilder.Build();
world.Update(0.01);
trackedEntities.Should().Contain(entityOne);
trackedEntities.Should().Contain(entityTwo);
}
}
}

View File

@ -10,6 +10,12 @@ using Encompass.Exceptions;
namespace Tests namespace Tests
{ {
struct MockComponent : IComponent
{
public int myInt;
public string myString;
}
public class EngineTest public class EngineTest
{ {
static List<ValueTuple<Guid, MockComponent>> resultComponents; static List<ValueTuple<Guid, MockComponent>> resultComponents;
@ -17,6 +23,7 @@ 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)
@ -25,6 +32,7 @@ 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)
@ -49,11 +57,11 @@ namespace Tests
mockComponentB.myInt = 1; mockComponentB.myInt = 1;
mockComponentB.myString = "howdy"; mockComponentB.myString = "howdy";
var componentAID = entity.AddComponent(mockComponent); var componentAID = worldBuilder.AddComponent(entity, mockComponent);
var componentBID = entity.AddComponent(mockComponentB); var componentBID = worldBuilder.AddComponent(entity, mockComponentB);
var inactiveComponentAID = entity.AddComponent(mockComponent); var inactiveComponentAID = worldBuilder.AddComponent(entity, mockComponent);
entity.DeactivateComponent(inactiveComponentAID); worldBuilder.DeactivateComponent(inactiveComponentAID);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -77,7 +85,7 @@ namespace Tests
mockComponent.myInt = 0; mockComponent.myInt = 0;
mockComponent.myString = "hello"; mockComponent.myString = "hello";
entity.AddComponent(mockComponent); worldBuilder.AddComponent(entity, mockComponent);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -102,15 +110,16 @@ namespace Tests
mockComponentB.myInt = 1; mockComponentB.myInt = 1;
mockComponentB.myString = "howdy"; mockComponentB.myString = "howdy";
entity.AddComponent(mockComponent); worldBuilder.AddComponent(entity, mockComponent);
entity.AddComponent(mockComponentB); worldBuilder.AddComponent(entity, mockComponentB);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
Assert.Throws<InvalidOperationException>(() => world.Update(0.01f)); Assert.Throws<InvalidOperationException>(() => world.Update(0.01f));
} }
[Mutates(typeof(MockComponent))] [Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
public class UpdateComponentTestEngine : Engine public class UpdateComponentTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -137,7 +146,7 @@ namespace Tests
mockComponent.myInt = 0; mockComponent.myInt = 0;
mockComponent.myString = "hello"; mockComponent.myString = "hello";
entity.AddComponent(mockComponent); worldBuilder.AddComponent(entity, mockComponent);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -147,6 +156,7 @@ 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)
@ -173,12 +183,12 @@ namespace Tests
mockComponent.myInt = 0; mockComponent.myInt = 0;
mockComponent.myString = "hello"; mockComponent.myString = "hello";
entity.AddComponent(mockComponent); worldBuilder.AddComponent(entity, mockComponent);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
var ex = Assert.Throws<IllegalComponentMutationException>(() => world.Update(0.01f)); var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to mutate undeclared Component MockComponent")); Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to write undeclared Component MockComponent"));
} }
struct MockMessage : IMessage struct MockMessage : IMessage
@ -186,7 +196,7 @@ namespace Tests
public string myString; public string myString;
} }
[Emits(typeof(MockMessage))] [Writes(typeof(MockMessage))]
public class MessageEmitEngine : Engine public class MessageEmitEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -263,13 +273,13 @@ namespace Tests
var world = worldBuilder.Build(); var world = worldBuilder.Build();
var ex = Assert.Throws<IllegalMessageEmitException>(() => 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 emit undeclared Message MockMessage"));
} }
static bool someTest; static bool someTest;
[Emits(typeof(MockMessage))] [Writes(typeof(MockMessage))]
class EmitMockMessageEngine : Engine class EmitMockMessageEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -282,20 +292,20 @@ namespace Tests
} }
[Reads(typeof(MockMessage))] [Reads(typeof(MockMessage))]
class SomeTestEngine : Engine class SomeMessageTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
{ {
someTest = this.Some<MockMessage>(); someTest = this.SomeMessage<MockMessage>();
} }
} }
[Test] [Test]
public void Some() public void SomeMessage()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new EmitMockMessageEngine()); worldBuilder.AddEngine(new EmitMockMessageEngine());
worldBuilder.AddEngine(new SomeTestEngine()); worldBuilder.AddEngine(new SomeMessageTestEngine());
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -304,29 +314,74 @@ namespace Tests
Assert.That(someTest, Is.True); Assert.That(someTest, Is.True);
} }
class UndeclaredSomeEngine : Engine class UndeclaredSomeMessageEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
{ {
someTest = this.Some<MockMessage>(); someTest = this.SomeMessage<MockMessage>();
} }
} }
[Test] [Test]
public void IllegalSome() public void UndeclaredSomeMessage()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new EmitMockMessageEngine()); worldBuilder.AddEngine(new EmitMockMessageEngine());
worldBuilder.AddEngine(new UndeclaredSomeEngine()); worldBuilder.AddEngine(new UndeclaredSomeMessageEngine());
var world = worldBuilder.Build(); var world = worldBuilder.Build();
Assert.Throws<IllegalMessageReadException>(() => world.Update(0.01f)); Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
}
[Reads(typeof(MockComponent))]
class SomeComponentTestEngine : Engine
{
public override void Update(double dt)
{
Assert.IsTrue(SomeComponent<MockComponent>());
}
}
[Test]
public void SomeComponent()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, new MockComponent());
var world = worldBuilder.Build();
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)
@ -354,8 +409,8 @@ namespace Tests
componentB.myString = "hello"; componentB.myString = "hello";
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(componentA); worldBuilder.AddComponent(entity, componentA);
entity.AddComponent(componentB); worldBuilder.AddComponent(entity, componentB);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);
@ -366,6 +421,7 @@ 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)
@ -388,6 +444,7 @@ 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)
@ -402,6 +459,8 @@ 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)
@ -425,11 +484,11 @@ namespace Tests
mockComponent.myInt = 2; mockComponent.myInt = 2;
mockComponent.myString = "blah"; mockComponent.myString = "blah";
entity.AddComponent(destroyerComponent); worldBuilder.AddComponent(entity, destroyerComponent);
var componentID = entity.AddComponent(mockComponent); var componentID = worldBuilder.AddComponent(entity, mockComponent);
entityB.AddComponent(destroyerComponent); worldBuilder.AddComponent(entityB, destroyerComponent);
var componentBID = entityB.AddComponent(mockComponent); var componentBID = worldBuilder.AddComponent(entityB, mockComponent);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -439,6 +498,8 @@ 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))]
class DestroyAndAddComponentEngine : Engine class DestroyAndAddComponentEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -447,9 +508,9 @@ namespace Tests
{ {
var componentID = componentPair.Item1; var componentID = componentPair.Item1;
var entity = GetEntityByComponentID(componentID); var entity = GetEntityByComponentID(componentID);
var (id, _) = entity.GetComponent<MockComponent>(); var (id, _) = GetComponent<MockComponent>(entity);
entity.RemoveComponent(id); RemoveComponent(id);
Destroy(entity.id); Destroy(entity.ID);
} }
} }
} }
@ -463,8 +524,8 @@ namespace Tests
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(new DestroyerComponent()); worldBuilder.AddComponent(entity, new DestroyerComponent());
entity.AddComponent(new MockComponent()); worldBuilder.AddComponent(entity, new MockComponent());
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -472,6 +533,8 @@ 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)
@ -492,7 +555,7 @@ namespace Tests
component.myString = "howdy"; component.myString = "howdy";
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(component); worldBuilder.AddComponent(entity, component);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);
@ -501,6 +564,8 @@ 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)
@ -520,7 +585,7 @@ namespace Tests
component.myString = "howdy"; component.myString = "howdy";
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(component); worldBuilder.AddComponent(entity, component);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);
@ -530,6 +595,7 @@ 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)
@ -550,7 +616,7 @@ namespace Tests
component.myString = "howdy"; component.myString = "howdy";
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(component); worldBuilder.AddComponent(entity, component);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -559,6 +625,8 @@ 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)
@ -581,9 +649,9 @@ namespace Tests
var entityTwo = worldBuilder.CreateEntity(); var entityTwo = worldBuilder.CreateEntity();
EntityIDComponent entityIDComponent; EntityIDComponent entityIDComponent;
entityIDComponent.entityID = entityTwo.id; entityIDComponent.entityID = entityTwo.ID;
entity.AddComponent(entityIDComponent); worldBuilder.AddComponent(entity, entityIDComponent);
var world = worldBuilder.Build(); var world = worldBuilder.Build();

View File

@ -32,27 +32,27 @@ namespace Tests
TestDrawComponent testDrawComponent = default(TestDrawComponent); TestDrawComponent testDrawComponent = default(TestDrawComponent);
var entityToTrack = worldBuilder.CreateEntity(); var entityToTrack = worldBuilder.CreateEntity();
entityToTrack.AddComponent(aComponent); worldBuilder.AddComponent(entityToTrack, aComponent);
entityToTrack.AddComponent(bComponent); worldBuilder.AddComponent(entityToTrack, bComponent);
entityToTrack.AddComponent(testDrawComponent); worldBuilder.AddComponent(entityToTrack, testDrawComponent);
var entityNotToTrack = worldBuilder.CreateEntity(); var entityNotToTrack = worldBuilder.CreateEntity();
entityNotToTrack.AddComponent(aComponent); worldBuilder.AddComponent(entityNotToTrack, aComponent);
entityNotToTrack.AddComponent(testDrawComponent); worldBuilder.AddComponent(entityNotToTrack, testDrawComponent);
var entityWithoutDrawComponent = worldBuilder.CreateEntity(); var entityWithoutDrawComponent = worldBuilder.CreateEntity();
entityWithoutDrawComponent.AddComponent(aComponent); worldBuilder.AddComponent(entityWithoutDrawComponent, aComponent);
entityWithoutDrawComponent.AddComponent(bComponent); worldBuilder.AddComponent(entityWithoutDrawComponent, bComponent);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);
Console.WriteLine(renderer.IsTracking(entityNotToTrack.id)); Console.WriteLine(renderer.IsTracking(entityNotToTrack.ID));
Assert.IsTrue(renderer.IsTracking(entityToTrack.id)); Assert.IsTrue(renderer.IsTracking(entityToTrack.ID));
Assert.IsFalse(renderer.IsTracking(entityNotToTrack.id)); Assert.IsFalse(renderer.IsTracking(entityNotToTrack.ID));
Assert.IsFalse(renderer.IsTracking(entityWithoutDrawComponent.id)); Assert.IsFalse(renderer.IsTracking(entityWithoutDrawComponent.ID));
} }
static bool called = false; static bool called = false;
@ -75,19 +75,17 @@ namespace Tests
TestDrawComponent testDrawComponent = default(TestDrawComponent); TestDrawComponent testDrawComponent = default(TestDrawComponent);
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(aComponent); worldBuilder.AddComponent(entity, aComponent);
entity.AddComponent(bComponent); worldBuilder.AddComponent(entity, bComponent);
var testDrawComponentID = entity.AddDrawComponent(testDrawComponent, 1); var testDrawComponentID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 1);
worldBuilder.DeactivateComponent(testDrawComponentID);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);
entity.DeactivateComponent(testDrawComponentID); Assert.IsFalse(renderer.IsTracking(entity.ID));
world.Update(0.01f);
Assert.IsFalse(renderer.IsTracking(entity.id));
world.Draw(); world.Draw();
@ -101,7 +99,7 @@ namespace Tests
{ {
public override void Render(Entity entity) public override void Render(Entity entity)
{ {
resultComponents = entity.GetComponents<TestDrawComponent>(); resultComponents = GetComponents<TestDrawComponent>(entity);
calledOnDraw = true; calledOnDraw = true;
} }
} }
@ -117,16 +115,16 @@ namespace Tests
TestDrawComponent testDrawComponent; TestDrawComponent testDrawComponent;
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(aComponent); worldBuilder.AddComponent(entity, aComponent);
entity.AddComponent(cComponent); worldBuilder.AddComponent(entity, cComponent);
var testDrawComponentID = entity.AddDrawComponent(testDrawComponent, 2); var testDrawComponentID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 2);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);
world.Draw(); world.Draw();
Assert.IsTrue(renderer.IsTracking(entity.id)); Assert.IsTrue(renderer.IsTracking(entity.ID));
Assert.IsTrue(calledOnDraw); Assert.IsTrue(calledOnDraw);
resultComponents.Should().Contain(new ValueTuple<Guid, TestDrawComponent>(testDrawComponentID, testDrawComponent)); resultComponents.Should().Contain(new ValueTuple<Guid, TestDrawComponent>(testDrawComponentID, testDrawComponent));
} }

View File

@ -1,172 +0,0 @@
using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System.Collections.Generic;
using System;
namespace Tests
{
struct MockComponent : IComponent
{
public string myString;
public int myInt;
}
public class EntityTest
{
[Test]
public void AddComponent()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
Assert.IsTrue(entity.HasComponent<MockComponent>());
Assert.That(entity.GetComponent<MockComponent>().Item2, Is.EqualTo(mockComponent));
}
[Test]
public void GetComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponentA;
mockComponentA.myInt = 3;
mockComponentA.myString = "hello";
MockComponent mockComponentB;
mockComponentB.myInt = 5;
mockComponentB.myString = "wassup";
MockComponent mockComponentC;
mockComponentC.myInt = 1;
mockComponentC.myString = "howdy";
var componentAID = entity.AddComponent(mockComponentA);
var componentBID = entity.AddComponent(mockComponentB);
var componentCID = entity.AddComponent(mockComponentC);
var world = worldBuilder.Build();
var components = entity.GetComponents<MockComponent>();
components.Should().Contain(new ValueTuple<Guid, MockComponent>(componentAID, mockComponentA));
components.Should().Contain(new ValueTuple<Guid, MockComponent>(componentBID, mockComponentB));
components.Should().Contain(new ValueTuple<Guid, MockComponent>(componentCID, mockComponentC));
}
[Test]
public void GetComponent()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = entity.AddComponent<MockComponent>(mockComponent);
var world = worldBuilder.Build();
Assert.AreEqual(new ValueTuple<Guid, MockComponent>(componentID, mockComponent), entity.GetComponent<MockComponent>());
}
[Test]
public void HasComponent()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
Assert.IsTrue(entity.HasComponent<MockComponent>());
}
[Test]
public void HasComponentWhenInactive()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
entity.DeactivateComponent(componentID);
world.Update(0.01f);
Assert.IsFalse(entity.HasComponent<MockComponent>());
}
[Test]
public void RemoveComponent()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
entity.RemoveComponent(componentID);
world.Update(0.01f);
Assert.IsFalse(entity.HasComponent<MockComponent>());
Assert.IsEmpty(entity.GetComponents<MockComponent>());
}
[Test]
public void ReactivateComponent()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
var componentID = entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
entity.DeactivateComponent(componentID);
world.Update(0.01f);
Assert.IsFalse(entity.HasComponent<MockComponent>());
Assert.IsEmpty(entity.GetComponents<MockComponent>());
entity.ActivateComponent(componentID);
world.Update(0.01f);
Assert.IsTrue(entity.HasComponent<MockComponent>());
Assert.IsNotEmpty(entity.GetComponents<MockComponent>());
}
}
}

View File

@ -32,7 +32,7 @@ namespace Tests
AComponent aComponent; AComponent aComponent;
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
var componentID = entity.AddComponent(aComponent); var componentID = worldBuilder.AddComponent(entity, aComponent);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
@ -52,8 +52,8 @@ namespace Tests
AComponent aComponentTwo; AComponent aComponentTwo;
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
var componentID = entity.AddComponent(aComponent); var componentID = worldBuilder.AddComponent(entity, aComponent);
var componentTwoID = entity.AddComponent(aComponentTwo); var componentTwoID = worldBuilder.AddComponent(entity, aComponentTwo);
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01f);

View File

@ -14,7 +14,7 @@ namespace Tests
static Entity resultEntity; static Entity resultEntity;
[Emits(typeof(SpawnMessageA))] [Writes(typeof(SpawnMessageA))]
class MessageEmitter : Engine class MessageEmitter : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -23,12 +23,14 @@ namespace Tests
} }
} }
[Writes(typeof(TestComponent))]
class TestSpawner : Spawner<SpawnMessageA> class TestSpawner : Spawner<SpawnMessageA>
{ {
protected override void Spawn(SpawnMessageA message) protected override void Spawn(SpawnMessageA message)
{ {
resultEntity = CreateEntity(); resultEntity = CreateEntity();
resultEntity.AddComponent(new TestComponent()); AddComponent(resultEntity, new TestComponent());
Assert.Pass();
} }
} }
@ -42,14 +44,6 @@ namespace Tests
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01); world.Update(0.01);
Assert.That(resultEntity.HasComponent<TestComponent>(), Is.True);
var id = resultEntity.id;
world.Update(0.01);
Assert.That(resultEntity.id, Is.Not.EqualTo(id));
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace Tests
struct BMessage : IMessage { } struct BMessage : IMessage { }
[Reads(typeof(AMessage))] [Reads(typeof(AMessage))]
[Emits(typeof(BMessage))] [Writes(typeof(BMessage))]
class AEngine : Engine class AEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -25,7 +25,7 @@ namespace Tests
} }
[Reads(typeof(BMessage))] [Reads(typeof(BMessage))]
[Emits(typeof(AMessage))] [Writes(typeof(AMessage))]
class BEngine : Engine class BEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -54,7 +54,7 @@ namespace Tests
struct DMessage : IMessage { } struct DMessage : IMessage { }
[Reads(typeof(AMessage))] [Reads(typeof(AMessage))]
[Emits(typeof(BMessage))] [Writes(typeof(BMessage))]
class AEngine : Engine class AEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -65,7 +65,7 @@ namespace Tests
} }
[Reads(typeof(BMessage))] [Reads(typeof(BMessage))]
[Emits(typeof(CMessage))] [Writes(typeof(CMessage))]
class BEngine : Engine class BEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -76,7 +76,7 @@ namespace Tests
} }
[Reads(typeof(CMessage))] [Reads(typeof(CMessage))]
[Emits(typeof(DMessage))] [Writes(typeof(DMessage))]
class CEngine : Engine class CEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -87,7 +87,7 @@ namespace Tests
} }
[Reads(typeof(DMessage))] [Reads(typeof(DMessage))]
[Emits(typeof(AMessage))] [Writes(typeof(AMessage))]
class DEngine : Engine class DEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -114,13 +114,13 @@ namespace Tests
{ {
struct AComponent : IComponent { } struct AComponent : IComponent { }
[Mutates(typeof(AComponent))] [Writes(typeof(AComponent))]
class AEngine : Engine class AEngine : Engine
{ {
public override void Update(double dt) { } public override void Update(double dt) { }
} }
[Mutates(typeof(AComponent))] [Writes(typeof(AComponent))]
class BEngine : Engine class BEngine : Engine
{ {
public override void Update(double dt) { } public override void Update(double dt) { }
@ -133,7 +133,74 @@ namespace Tests
worldBuilder.AddEngine(new AEngine()); worldBuilder.AddEngine(new AEngine());
worldBuilder.AddEngine(new BEngine()); worldBuilder.AddEngine(new BEngine());
Assert.Throws<EngineMutationConflictException>(() => worldBuilder.Build()); Assert.Throws<EngineWriteConflictException>(() => worldBuilder.Build());
}
}
public class EngineMessageSelfCycle
{
struct AMessage : IMessage { }
[Reads(typeof(AMessage))]
[Writes(typeof(AMessage))]
class AEngine : Engine
{
public override void Update(double dt)
{
}
}
[Test]
public void ThrowsError()
{
var worldBuilder = new WorldBuilder();
Assert.Throws<EngineMessageSelfCycleException>(() => worldBuilder.AddEngine(new AEngine()), "Engine both reads and writes Message AMessage");
}
}
public class IllegalReadType
{
struct ANonMessage { }
[Reads(typeof(ANonMessage))]
class MyEngine : Engine
{
public override void Update(double dt)
{
}
}
[Test]
public void ThrowsError()
{
var worldBuilder = new WorldBuilder();
Assert.Throws<IllegalReadTypeException>(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component");
}
}
public class IllegalWriteType
{
struct ANonMessage { }
[Writes(typeof(ANonMessage))]
class MyEngine : Engine
{
public override void Update(double dt)
{
}
}
[Test]
public void ThrowsError()
{
var worldBuilder = new WorldBuilder();
Assert.Throws<IllegalWriteTypeException>(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component");
} }
} }
@ -149,8 +216,7 @@ namespace Tests
struct CMessage : IMessage { } struct CMessage : IMessage { }
struct DMessage : IMessage { } struct DMessage : IMessage { }
[Mutates(typeof(AComponent))] [Writes(typeof(AComponent), typeof(AMessage))]
[Emits(typeof(AMessage))]
class AEngine : Engine class AEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -159,8 +225,7 @@ namespace Tests
} }
} }
[Mutates(typeof(BComponent))] [Writes(typeof(BComponent), typeof(BMessage))]
[Emits(typeof(BMessage))]
class BEngine : Engine class BEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -170,7 +235,7 @@ namespace Tests
} }
[Reads(typeof(AMessage), typeof(BMessage))] [Reads(typeof(AMessage), typeof(BMessage))]
[Emits(typeof(DMessage))] [Writes(typeof(DMessage))]
class CEngine : Engine class CEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)

View File

@ -44,26 +44,26 @@ namespace Tests
TestDrawComponent testDrawComponent = default(TestDrawComponent); TestDrawComponent testDrawComponent = default(TestDrawComponent);
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
entity.AddComponent(testComponent); worldBuilder.AddComponent(entity, testComponent);
entity.AddDrawComponent(testDrawComponent, 3); worldBuilder.AddDrawComponent(entity, testDrawComponent, 3);
TestDrawComponent testDrawComponentTwo = default(TestDrawComponent); TestDrawComponent testDrawComponentTwo = default(TestDrawComponent);
var entityTwo = worldBuilder.CreateEntity(); var entityTwo = worldBuilder.CreateEntity();
entityTwo.AddComponent(testComponent); worldBuilder.AddComponent(entityTwo, testComponent);
entityTwo.AddDrawComponent(testDrawComponentTwo, 1); worldBuilder.AddDrawComponent(entityTwo, testDrawComponentTwo, 1);
TestDrawComponent testDrawComponentThree = default(TestDrawComponent); TestDrawComponent testDrawComponentThree = default(TestDrawComponent);
var entityThree = worldBuilder.CreateEntity(); var entityThree = worldBuilder.CreateEntity();
entityThree.AddComponent(testComponent); worldBuilder.AddComponent(entityThree, testComponent);
entityThree.AddDrawComponent(testDrawComponentThree, 5); worldBuilder.AddDrawComponent(entityThree, testDrawComponentThree, 5);
TestDrawComponent testDrawComponentFour = default(TestDrawComponent); TestDrawComponent testDrawComponentFour = default(TestDrawComponent);
var entityFour = worldBuilder.CreateEntity(); var entityFour = worldBuilder.CreateEntity();
entityFour.AddComponent(testComponent); worldBuilder.AddComponent(entityFour, testComponent);
entityFour.AddDrawComponent(testDrawComponentFour, -5); worldBuilder.AddDrawComponent(entityFour, testDrawComponentFour, -5);
var world = worldBuilder.Build(); var world = worldBuilder.Build();