read new and existing component system
parent
80effc06dc
commit
1845d5f766
|
@ -19,7 +19,7 @@ namespace Encompass
|
||||||
throw new IllegalActivateTypeException("{0} must be a Component", activateType.Name);
|
throw new IllegalActivateTypeException("{0} must be a Component", activateType.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.activateTypes.Add(typeof(ComponentMessage<>).MakeGenericType(activateType));
|
this.activateTypes.Add(typeof(NewComponentMessage<>).MakeGenericType(activateType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,23 +14,15 @@ namespace Encompass
|
||||||
{
|
{
|
||||||
foreach (var readType in readTypes)
|
foreach (var readType in readTypes)
|
||||||
{
|
{
|
||||||
var isMessage = readType.GetInterfaces().Contains(typeof(IMessage));
|
|
||||||
var isComponent = readType.GetInterfaces().Contains(typeof(IComponent));
|
var isComponent = readType.GetInterfaces().Contains(typeof(IComponent));
|
||||||
|
|
||||||
if (!isMessage && !isComponent)
|
if (!isComponent)
|
||||||
{
|
{
|
||||||
throw new IllegalReadTypeException("{0} must be a Message or Component", readType.Name);
|
throw new IllegalReadTypeException("{0} must be a Component", readType.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isComponent)
|
|
||||||
{
|
|
||||||
this.readTypes.Add(typeof(ComponentMessage<>).MakeGenericType(readType));
|
this.readTypes.Add(typeof(ComponentMessage<>).MakeGenericType(readType));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
this.readTypes.Add(readType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Encompass.Exceptions;
|
||||||
|
|
||||||
|
namespace Encompass
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class ReadsNew : Attribute
|
||||||
|
{
|
||||||
|
public readonly HashSet<Type> newComponentReadTypes = new HashSet<Type>();
|
||||||
|
|
||||||
|
public ReadsNew(params Type[] readTypes)
|
||||||
|
{
|
||||||
|
foreach (var readType in readTypes)
|
||||||
|
{
|
||||||
|
var isComponent = readType.GetInterfaces().Contains(typeof(IComponent));
|
||||||
|
|
||||||
|
if (!isComponent)
|
||||||
|
{
|
||||||
|
throw new IllegalReadTypeException("{0} must be a Component", readType.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newComponentReadTypes.Add(typeof(ComponentMessage<>).MakeGenericType(readType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Encompass.Exceptions;
|
||||||
|
|
||||||
|
namespace Encompass
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class Receives : Attribute
|
||||||
|
{
|
||||||
|
public readonly HashSet<Type> receiveTypes;
|
||||||
|
|
||||||
|
public Receives(params Type[] receiveTypes)
|
||||||
|
{
|
||||||
|
foreach (var receiveType in receiveTypes)
|
||||||
|
{
|
||||||
|
var isMessage = receiveType.GetInterfaces().Contains(typeof(IMessage));
|
||||||
|
if (!isMessage)
|
||||||
|
{
|
||||||
|
throw new IllegalSendTypeException("{0} must be a Message", receiveType.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.receiveTypes = new HashSet<Type>(receiveTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ namespace Encompass
|
||||||
var isMessage = sendType.GetInterfaces().Contains(typeof(IMessage));
|
var isMessage = sendType.GetInterfaces().Contains(typeof(IMessage));
|
||||||
if (!isMessage)
|
if (!isMessage)
|
||||||
{
|
{
|
||||||
throw new IllegalWriteTypeException("{0} must be a Message", sendType.Name);
|
throw new IllegalSendTypeException("{0} must be a Message", sendType.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,7 @@ namespace Encompass
|
||||||
public abstract class Engine
|
public abstract class Engine
|
||||||
{
|
{
|
||||||
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
||||||
internal readonly HashSet<Type> readTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
|
||||||
internal readonly HashSet<Type> activateTypes = new HashSet<Type>();
|
|
||||||
internal readonly HashSet<Type> updateTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> updateTypes = new HashSet<Type>();
|
||||||
|
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
@ -19,22 +18,34 @@ namespace Encompass
|
||||||
|
|
||||||
protected Engine()
|
protected Engine()
|
||||||
{
|
{
|
||||||
var writesAttribute = GetType().GetCustomAttribute<Sends>(false);
|
var sendsAttribute = GetType().GetCustomAttribute<Sends>(false);
|
||||||
if (writesAttribute != null)
|
if (sendsAttribute != null)
|
||||||
{
|
{
|
||||||
sendTypes = writesAttribute.sendTypes;
|
sendTypes = sendsAttribute.sendTypes;
|
||||||
}
|
|
||||||
|
|
||||||
var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
|
|
||||||
if (readsAttribute != null)
|
|
||||||
{
|
|
||||||
readTypes = readsAttribute.readTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var activatesAttribute = GetType().GetCustomAttribute<Activates>(false);
|
var activatesAttribute = GetType().GetCustomAttribute<Activates>(false);
|
||||||
if (activatesAttribute != null)
|
if (activatesAttribute != null)
|
||||||
{
|
{
|
||||||
activateTypes = activatesAttribute.activateTypes;
|
sendTypes.UnionWith(activatesAttribute.activateTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
var receivesAttribute = GetType().GetCustomAttribute<Receives>(false);
|
||||||
|
if (receivesAttribute != null)
|
||||||
|
{
|
||||||
|
receiveTypes = receivesAttribute.receiveTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
|
||||||
|
if (readsAttribute != null)
|
||||||
|
{
|
||||||
|
receiveTypes.UnionWith(readsAttribute.readTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
var readsNewAttribute = GetType().GetCustomAttribute<ReadsNew>(false);
|
||||||
|
if (readsNewAttribute != null)
|
||||||
|
{
|
||||||
|
receiveTypes.UnionWith(readsNewAttribute.newComponentReadTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatesAttribute = GetType().GetCustomAttribute<Updates>(false);
|
var updatesAttribute = GetType().GetCustomAttribute<Updates>(false);
|
||||||
|
@ -107,14 +118,14 @@ namespace Encompass
|
||||||
|
|
||||||
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 (!activateTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
if (!sendTypes.Contains(typeof(NewComponentMessage<TComponent>)))
|
||||||
{
|
{
|
||||||
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
var componentID = componentManager.AddComponent(entity.ID, component);
|
var componentID = componentManager.AddComponent(entity.ID, component);
|
||||||
|
|
||||||
ComponentMessage<TComponent> componentMessage;
|
NewComponentMessage<TComponent> componentMessage;
|
||||||
componentMessage.entity = entity;
|
componentMessage.entity = entity;
|
||||||
componentMessage.componentID = componentID;
|
componentMessage.componentID = componentID;
|
||||||
componentMessage.component = component;
|
componentMessage.component = component;
|
||||||
|
@ -125,25 +136,25 @@ namespace Encompass
|
||||||
|
|
||||||
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 (!activateTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
if (!sendTypes.Contains(typeof(NewComponentMessage<TComponent>)))
|
||||||
{
|
{
|
||||||
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
var componentID = componentManager.AddDrawComponent(entity.ID, component, layer);
|
var componentID = componentManager.AddDrawComponent(entity.ID, component, layer);
|
||||||
|
|
||||||
ComponentMessage<TComponent> componentMessage;
|
NewComponentMessage<TComponent> newComponentMessage;
|
||||||
componentMessage.entity = entity;
|
newComponentMessage.entity = entity;
|
||||||
componentMessage.componentID = componentID;
|
newComponentMessage.componentID = componentID;
|
||||||
componentMessage.component = component;
|
newComponentMessage.component = component;
|
||||||
SendMessage(componentMessage);
|
SendMessage(newComponentMessage);
|
||||||
|
|
||||||
return componentID;
|
return componentID;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ActivateComponent<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
protected void ActivateComponent<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
if (!activateTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
if (!sendTypes.Contains(typeof(NewComponentMessage<TComponent>)))
|
||||||
{
|
{
|
||||||
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
throw new IllegalActivateException("Engine {0} tried to activate undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||||
}
|
}
|
||||||
|
@ -151,13 +162,13 @@ namespace Encompass
|
||||||
var entity = GetEntity(componentManager.GetEntityIDByComponentID(componentID));
|
var entity = GetEntity(componentManager.GetEntityIDByComponentID(componentID));
|
||||||
var component = GetComponentByID<TComponent>(componentID);
|
var component = GetComponentByID<TComponent>(componentID);
|
||||||
|
|
||||||
ComponentMessage<TComponent> componentMessage;
|
NewComponentMessage<TComponent> newComponentMessage;
|
||||||
componentMessage.entity = entity;
|
newComponentMessage.entity = entity;
|
||||||
componentMessage.componentID = componentID;
|
newComponentMessage.componentID = componentID;
|
||||||
componentMessage.component = component;
|
newComponentMessage.component = component;
|
||||||
SendMessage(componentMessage);
|
SendMessage(newComponentMessage);
|
||||||
|
|
||||||
componentManager.Activate(componentID);
|
componentManager.Activate(componentID); // TODO: actually delay this to end of frame, make sure to update after activate
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void DeactivateComponent(Guid componentID)
|
protected void DeactivateComponent(Guid componentID)
|
||||||
|
@ -165,9 +176,9 @@ namespace Encompass
|
||||||
componentManager.MarkForDeactivation(componentID);
|
componentManager.MarkForDeactivation(componentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
|
private IEnumerable<ValueTuple<Guid, TComponent>> ExistingComponentsOnEntity<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
if (!readTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
if (!receiveTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
||||||
{
|
{
|
||||||
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||||
}
|
}
|
||||||
|
@ -175,6 +186,26 @@ namespace Encompass
|
||||||
return ReadMessages<ComponentMessage<TComponent>>().Where((message) => message.entity == entity).Select((message) => (message.componentID, message.component));
|
return ReadMessages<ComponentMessage<TComponent>>().Where((message) => message.entity == entity).Select((message) => (message.componentID, message.component));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<ValueTuple<Guid, TComponent>> NewComponentsOnEntity<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||||
|
{
|
||||||
|
if (!receiveTypes.Contains(typeof(NewComponentMessage<TComponent>)))
|
||||||
|
{
|
||||||
|
throw new IllegalReadException("Engine {0} tried to read undeclared new Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadMessages<NewComponentMessage<TComponent>>().Where((message) => message.entity == entity).Select((message) => (message.componentID, message.component));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponentsIncludingNew<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||||
|
{
|
||||||
|
return ExistingComponentsOnEntity<TComponent>(entity).Union(NewComponentsOnEntity<TComponent>(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IEnumerable<ValueTuple<Guid, TComponent>> GetComponents<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||||
|
{
|
||||||
|
return ExistingComponentsOnEntity<TComponent>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
|
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
return GetComponents<TComponent>(entity).First();
|
return GetComponents<TComponent>(entity).First();
|
||||||
|
@ -182,19 +213,14 @@ 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(ComponentMessage<TComponent>)))
|
return GetComponents<TComponent>(entity).Any();
|
||||||
{
|
|
||||||
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReadMessages<ComponentMessage<TComponent>>().Where((message) => message.entity == entity).Any();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent
|
internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
if (!updateTypes.Contains(typeof(TComponent)))
|
if (!updateTypes.Contains(typeof(TComponent)))
|
||||||
{
|
{
|
||||||
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
|
throw new IllegalSendException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentManager.AddUpdateComponentOperation(componentID, newComponent);
|
componentManager.AddUpdateComponentOperation(componentID, newComponent);
|
||||||
|
@ -209,7 +235,7 @@ namespace Encompass
|
||||||
{
|
{
|
||||||
if (!sendTypes.Contains(typeof(TMessage)))
|
if (!sendTypes.Contains(typeof(TMessage)))
|
||||||
{
|
{
|
||||||
throw new IllegalWriteException("Engine {0} tried to write undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
|
throw new IllegalSendException("Engine {0} tried to write undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
messageManager.AddMessage(message);
|
messageManager.AddMessage(message);
|
||||||
|
@ -217,7 +243,7 @@ namespace Encompass
|
||||||
|
|
||||||
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
|
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
if (!readTypes.Contains(typeof(TMessage)))
|
if (!receiveTypes.Contains(typeof(TMessage)))
|
||||||
{
|
{
|
||||||
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
|
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
|
||||||
}
|
}
|
||||||
|
@ -230,9 +256,9 @@ namespace Encompass
|
||||||
return ReadMessages<TMessage>().Single();
|
return ReadMessages<TMessage>().Single();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<(Guid, TComponent)> ReadComponents<TComponent>() where TComponent : struct, IComponent
|
protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
if (!readTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
if (!receiveTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
||||||
{
|
{
|
||||||
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
|
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
|
||||||
}
|
}
|
||||||
|
@ -240,14 +266,14 @@ namespace Encompass
|
||||||
return ReadMessages<ComponentMessage<TComponent>>().Select((message) => (message.componentID, message.component));
|
return ReadMessages<ComponentMessage<TComponent>>().Select((message) => (message.componentID, message.component));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected (Guid, TComponent) ReadComponent<TComponent>() where TComponent : struct, IComponent
|
protected ValueTuple<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
return ReadComponents<TComponent>().Single();
|
return ReadComponents<TComponent>().Single();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
|
protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
if (!readTypes.Contains(typeof(TMessage)))
|
if (!receiveTypes.Contains(typeof(TMessage)))
|
||||||
{
|
{
|
||||||
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
|
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
|
||||||
}
|
}
|
||||||
|
@ -257,7 +283,7 @@ namespace Encompass
|
||||||
|
|
||||||
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
|
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
if (!readTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
if (!receiveTypes.Contains(typeof(ComponentMessage<TComponent>)))
|
||||||
{
|
{
|
||||||
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,6 @@ namespace Encompass.Engines
|
||||||
{
|
{
|
||||||
public ComponentMessageEmitter() : base()
|
public ComponentMessageEmitter() : base()
|
||||||
{
|
{
|
||||||
var writesAttribute = GetType().GetCustomAttribute<Sends>(false);
|
|
||||||
if (writesAttribute != null)
|
|
||||||
{
|
|
||||||
writesAttribute.sendTypes.Add(typeof(ComponentMessage<TComponent>));
|
|
||||||
}
|
|
||||||
|
|
||||||
sendTypes.Add(typeof(ComponentMessage<TComponent>));
|
sendTypes.Add(typeof(ComponentMessage<TComponent>));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Encompass.Engines
|
||||||
|
{
|
||||||
|
internal class NewComponentMessageEmitter<TComponent> : Engine where TComponent : struct, IComponent
|
||||||
|
{
|
||||||
|
public NewComponentMessageEmitter() : base()
|
||||||
|
{
|
||||||
|
sendTypes.Add(typeof(NewComponentMessage<TComponent>));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(double dt)
|
||||||
|
{
|
||||||
|
foreach (var (entity, componentID, component) in ReadComponentsFromWorld<TComponent>())
|
||||||
|
{
|
||||||
|
NewComponentMessage<TComponent> newComponentMessage;
|
||||||
|
newComponentMessage.entity = entity;
|
||||||
|
newComponentMessage.componentID = componentID;
|
||||||
|
newComponentMessage.component = component;
|
||||||
|
SendMessage(newComponentMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ namespace Encompass.Engines
|
||||||
readsAttribute.readTypes.Add(typeof(TMessage));
|
readsAttribute.readTypes.Add(typeof(TMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
readTypes.Add(typeof(TMessage));
|
receiveTypes.Add(typeof(TMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
|
|
@ -2,9 +2,9 @@ using System;
|
||||||
|
|
||||||
namespace Encompass.Exceptions
|
namespace Encompass.Exceptions
|
||||||
{
|
{
|
||||||
public class IllegalWriteException : Exception
|
public class IllegalSendException : Exception
|
||||||
{
|
{
|
||||||
public IllegalWriteException(
|
public IllegalSendException(
|
||||||
string format,
|
string format,
|
||||||
params object[] args
|
params object[] args
|
||||||
) : base(string.Format(format, args)) { }
|
) : base(string.Format(format, args)) { }
|
|
@ -2,9 +2,9 @@ using System;
|
||||||
|
|
||||||
namespace Encompass.Exceptions
|
namespace Encompass.Exceptions
|
||||||
{
|
{
|
||||||
public class IllegalWriteTypeException : Exception
|
public class IllegalSendTypeException : Exception
|
||||||
{
|
{
|
||||||
public IllegalWriteTypeException(
|
public IllegalSendTypeException(
|
||||||
string format,
|
string format,
|
||||||
params object[] args
|
params object[] args
|
||||||
) : base(string.Format(format, args)) { }
|
) : base(string.Format(format, args)) { }
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Encompass
|
||||||
|
{
|
||||||
|
public struct NewComponentMessage<TComponent> : IMessage where TComponent : struct, IComponent
|
||||||
|
{
|
||||||
|
public Entity entity;
|
||||||
|
public Guid componentID;
|
||||||
|
public TComponent component;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ namespace Encompass
|
||||||
private readonly HashSet<Engine> senders = new HashSet<Engine>();
|
private readonly HashSet<Engine> senders = new HashSet<Engine>();
|
||||||
|
|
||||||
private readonly HashSet<Type> registeredComponentTypes = new HashSet<Type>();
|
private readonly HashSet<Type> registeredComponentTypes = new HashSet<Type>();
|
||||||
|
private readonly HashSet<Type> registeredNewComponentTypes = new HashSet<Type>();
|
||||||
|
|
||||||
public WorldBuilder()
|
public WorldBuilder()
|
||||||
{
|
{
|
||||||
|
@ -66,6 +67,12 @@ namespace Encompass
|
||||||
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
|
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void RegisterNewComponentEmitter(Type componentType)
|
||||||
|
{
|
||||||
|
registeredNewComponentTypes.Add(componentType);
|
||||||
|
AddEngine((Engine)Activator.CreateInstance(typeof(NewComponentMessageEmitter<>).MakeGenericType(componentType)));
|
||||||
|
}
|
||||||
|
|
||||||
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
|
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
|
||||||
{
|
{
|
||||||
engine.AssignEntityManager(entityManager);
|
engine.AssignEntityManager(entityManager);
|
||||||
|
@ -75,15 +82,10 @@ namespace Encompass
|
||||||
engines.Add(engine);
|
engines.Add(engine);
|
||||||
engineGraph.AddVertex(engine);
|
engineGraph.AddVertex(engine);
|
||||||
|
|
||||||
foreach (var activateType in engine.activateTypes)
|
var messageReceiveTypes = engine.receiveTypes;
|
||||||
{
|
|
||||||
engine.sendTypes.Add(activateType);
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageReadTypes = engine.readTypes;
|
|
||||||
var messageSendTypes = engine.sendTypes;
|
var messageSendTypes = engine.sendTypes;
|
||||||
|
|
||||||
foreach (var messageType in messageReadTypes.Intersect(messageSendTypes))
|
foreach (var messageType in messageReceiveTypes.Intersect(messageSendTypes))
|
||||||
{
|
{
|
||||||
// ComponentMessages can safely self-cycle
|
// ComponentMessages can safely self-cycle
|
||||||
// this does introduce a gotcha though: if you AddComponent and then HasComponent or GetComponent you will receive a false negative
|
// this does introduce a gotcha though: if you AddComponent and then HasComponent or GetComponent you will receive a false negative
|
||||||
|
@ -99,23 +101,43 @@ namespace Encompass
|
||||||
senders.Add(engine);
|
senders.Add(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var readType in engine.readTypes)
|
foreach (var receiveType in engine.receiveTypes)
|
||||||
{
|
{
|
||||||
if (readType.IsGenericType && readType.GetGenericTypeDefinition() == typeof(ComponentMessage<>))
|
if (receiveType.IsGenericType)
|
||||||
{
|
{
|
||||||
var componentType = readType.GetGenericArguments().Single();
|
var genericTypeDefinition = receiveType.GetGenericTypeDefinition();
|
||||||
|
if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(NewComponentMessage<>))
|
||||||
|
{
|
||||||
|
var componentType = receiveType.GetGenericArguments().Single();
|
||||||
if (!registeredComponentTypes.Contains(componentType))
|
if (!registeredComponentTypes.Contains(componentType))
|
||||||
{
|
{
|
||||||
RegisterComponent(componentType);
|
RegisterComponent(componentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!typeToReaders.ContainsKey(readType))
|
|
||||||
{
|
|
||||||
typeToReaders.Add(readType, new HashSet<Engine>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typeToReaders[readType].Add(engine);
|
if (!typeToReaders.ContainsKey(receiveType))
|
||||||
|
{
|
||||||
|
typeToReaders.Add(receiveType, new HashSet<Engine>());
|
||||||
|
}
|
||||||
|
|
||||||
|
typeToReaders[receiveType].Add(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var sendType in engine.sendTypes)
|
||||||
|
{
|
||||||
|
if (sendType.IsGenericType)
|
||||||
|
{
|
||||||
|
var genericTypeDefinition = sendType.GetGenericTypeDefinition();
|
||||||
|
if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(NewComponentMessage<>))
|
||||||
|
{
|
||||||
|
var componentType = sendType.GetGenericArguments().Single();
|
||||||
|
if (!registeredNewComponentTypes.Contains(componentType))
|
||||||
|
{
|
||||||
|
RegisterNewComponentEmitter(componentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return engine;
|
return engine;
|
||||||
|
|
|
@ -24,7 +24,8 @@ 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))]
|
[Receives(typeof(EntityMessage))]
|
||||||
|
[Reads(typeof(MockComponent))]
|
||||||
class GetMockComponentEngine : Engine
|
class GetMockComponentEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -42,7 +43,8 @@ namespace Tests
|
||||||
public MockComponent mockComponent;
|
public MockComponent mockComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(AddComponentTestMessage), typeof(MockComponent))]
|
[Receives(typeof(AddComponentTestMessage))]
|
||||||
|
[Reads(typeof(MockComponent))]
|
||||||
class AddComponentTestEngine : Engine
|
class AddComponentTestEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -109,7 +111,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Activates(typeof(MockComponent))]
|
[Activates(typeof(MockComponent))]
|
||||||
[Reads(typeof(AddMockComponentMessage))]
|
[Receives(typeof(AddMockComponentMessage))]
|
||||||
class AddMockComponentEngine : Engine
|
class AddMockComponentEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -152,7 +154,8 @@ namespace Tests
|
||||||
world.Update(0.01);
|
world.Update(0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(EntityMessage), typeof(MockComponent))]
|
[Receives(typeof(EntityMessage))]
|
||||||
|
[Reads(typeof(MockComponent))]
|
||||||
class GetMockComponentsEngine : Engine
|
class GetMockComponentsEngine : Engine
|
||||||
{
|
{
|
||||||
private Entity entity;
|
private Entity entity;
|
||||||
|
@ -264,7 +267,8 @@ namespace Tests
|
||||||
public Entity entity;
|
public Entity entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(HasComponentTestMessage), typeof(MockComponent))]
|
[Receives(typeof(HasComponentTestMessage))]
|
||||||
|
[Reads(typeof(MockComponent))]
|
||||||
class HasComponentTestEngine : Engine
|
class HasComponentTestEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -304,7 +308,8 @@ namespace Tests
|
||||||
public Entity entity;
|
public Entity entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(HasComponentWhenInactiveTestMessage), typeof(MockComponent))]
|
[Receives(typeof(HasComponentWhenInactiveTestMessage))]
|
||||||
|
[Reads(typeof(MockComponent))]
|
||||||
class HasComponentWhenInactiveTestEngine : Engine
|
class HasComponentWhenInactiveTestEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -346,7 +351,7 @@ namespace Tests
|
||||||
public Guid componentID;
|
public Guid componentID;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(RemoveComponentTestMessage))]
|
[Receives(typeof(RemoveComponentTestMessage))]
|
||||||
class RemoveComponentTestEngine : Engine
|
class RemoveComponentTestEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -358,7 +363,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(RemoveComponentTestMessage))]
|
[Receives(typeof(RemoveComponentTestMessage))]
|
||||||
[Sends(typeof(CheckHasMockComponentMessage))]
|
[Sends(typeof(CheckHasMockComponentMessage))]
|
||||||
class DoRemoveCheckEngine : Engine
|
class DoRemoveCheckEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -421,7 +426,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Activates(typeof(MockComponent))]
|
[Activates(typeof(MockComponent))]
|
||||||
[Reads(typeof(ActivateComponentMessage))]
|
[Receives(typeof(ActivateComponentMessage))]
|
||||||
class ActivateComponentEngine : Engine
|
class ActivateComponentEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -439,7 +444,7 @@ namespace Tests
|
||||||
public bool shouldHaveComponent;
|
public bool shouldHaveComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(ActivateComponentMessage))]
|
[Receives(typeof(ActivateComponentMessage))]
|
||||||
[Sends(typeof(CheckHasMockComponentMessage))]
|
[Sends(typeof(CheckHasMockComponentMessage))]
|
||||||
class DoActivateCheckEngine : Engine
|
class DoActivateCheckEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -462,7 +467,8 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(CheckHasMockComponentMessage), typeof(MockComponent))]
|
[Receives(typeof(CheckHasMockComponentMessage))]
|
||||||
|
[Reads(typeof(MockComponent))]
|
||||||
class CheckHasMockComponentEngine : Engine
|
class CheckHasMockComponentEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -508,7 +514,7 @@ namespace Tests
|
||||||
public Guid componentID;
|
public Guid componentID;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(DeactivateComponentMessage))]
|
[Receives(typeof(DeactivateComponentMessage))]
|
||||||
class DeactivateComponentEngine : Engine
|
class DeactivateComponentEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -520,7 +526,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(DeactivateComponentMessage))]
|
[Receives(typeof(DeactivateComponentMessage))]
|
||||||
[Sends(typeof(CheckHasMockComponentMessage))]
|
[Sends(typeof(CheckHasMockComponentMessage))]
|
||||||
class DoDeactivateCheckEngine : Engine
|
class DoDeactivateCheckEngine : Engine
|
||||||
{
|
{
|
||||||
|
|
|
@ -191,7 +191,7 @@ namespace Tests
|
||||||
|
|
||||||
var world = worldBuilder.Build();
|
var world = worldBuilder.Build();
|
||||||
|
|
||||||
var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
|
var ex = Assert.Throws<IllegalSendException>(() => world.Update(0.01f));
|
||||||
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to write undeclared Component MockComponent"));
|
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to write undeclared Component MockComponent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(MockMessage))]
|
[Receives(typeof(MockMessage))]
|
||||||
public class MessageReadEngine : Engine
|
public class MessageReadEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -248,7 +248,7 @@ namespace Tests
|
||||||
|
|
||||||
static IEnumerable<MockMessage> emptyReadMessagesResult;
|
static IEnumerable<MockMessage> emptyReadMessagesResult;
|
||||||
|
|
||||||
[Reads(typeof(MockMessage))]
|
[Receives(typeof(MockMessage))]
|
||||||
class ReadMessagesWhenNoneExistEngine : Engine
|
class ReadMessagesWhenNoneExistEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -278,7 +278,7 @@ namespace Tests
|
||||||
|
|
||||||
var world = worldBuilder.Build();
|
var world = worldBuilder.Build();
|
||||||
|
|
||||||
var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
|
var ex = Assert.Throws<IllegalSendException>(() => world.Update(0.01f));
|
||||||
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to write undeclared Message MockMessage"));
|
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to write undeclared Message MockMessage"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(MockMessage))]
|
[Receives(typeof(MockMessage))]
|
||||||
class SomeMessageTestEngine : Engine
|
class SomeMessageTestEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
@ -666,7 +666,7 @@ namespace Tests
|
||||||
public MockComponent mockComponent;
|
public MockComponent mockComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(MockComponentUpdateMessage))]
|
[Receives(typeof(MockComponentUpdateMessage))]
|
||||||
[Updates(typeof(MockComponent))]
|
[Updates(typeof(MockComponent))]
|
||||||
class RepeatUpdateEngine : Engine
|
class RepeatUpdateEngine : Engine
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Tests
|
||||||
struct AMessage : IMessage { }
|
struct AMessage : IMessage { }
|
||||||
struct BMessage : IMessage { }
|
struct BMessage : IMessage { }
|
||||||
|
|
||||||
[Reads(typeof(AMessage))]
|
[Receives(typeof(AMessage))]
|
||||||
[Sends(typeof(BMessage))]
|
[Sends(typeof(BMessage))]
|
||||||
class AEngine : Engine
|
class AEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(BMessage))]
|
[Receives(typeof(BMessage))]
|
||||||
[Sends(typeof(AMessage))]
|
[Sends(typeof(AMessage))]
|
||||||
class BEngine : Engine
|
class BEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ namespace Tests
|
||||||
struct CMessage : IMessage { }
|
struct CMessage : IMessage { }
|
||||||
struct DMessage : IMessage { }
|
struct DMessage : IMessage { }
|
||||||
|
|
||||||
[Reads(typeof(AMessage))]
|
[Receives(typeof(AMessage))]
|
||||||
[Sends(typeof(BMessage))]
|
[Sends(typeof(BMessage))]
|
||||||
class AEngine : Engine
|
class AEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(BMessage))]
|
[Receives(typeof(BMessage))]
|
||||||
[Sends(typeof(CMessage))]
|
[Sends(typeof(CMessage))]
|
||||||
class BEngine : Engine
|
class BEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -75,7 +75,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(CMessage))]
|
[Receives(typeof(CMessage))]
|
||||||
[Sends(typeof(DMessage))]
|
[Sends(typeof(DMessage))]
|
||||||
class CEngine : Engine
|
class CEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -86,7 +86,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(DMessage))]
|
[Receives(typeof(DMessage))]
|
||||||
[Sends(typeof(AMessage))]
|
[Sends(typeof(AMessage))]
|
||||||
class DEngine : Engine
|
class DEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -141,7 +141,7 @@ namespace Tests
|
||||||
{
|
{
|
||||||
struct AMessage : IMessage { }
|
struct AMessage : IMessage { }
|
||||||
|
|
||||||
[Reads(typeof(AMessage))]
|
[Receives(typeof(AMessage))]
|
||||||
[Sends(typeof(AMessage))]
|
[Sends(typeof(AMessage))]
|
||||||
class AEngine : Engine
|
class AEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -200,7 +200,7 @@ namespace Tests
|
||||||
{
|
{
|
||||||
var worldBuilder = new WorldBuilder();
|
var worldBuilder = new WorldBuilder();
|
||||||
|
|
||||||
Assert.Throws<IllegalWriteTypeException>(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component");
|
Assert.Throws<IllegalSendTypeException>(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(AMessage), typeof(BMessage))]
|
[Receives(typeof(AMessage), typeof(BMessage))]
|
||||||
[Sends(typeof(DMessage))]
|
[Sends(typeof(DMessage))]
|
||||||
class CEngine : Engine
|
class CEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -244,7 +244,7 @@ namespace Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Reads(typeof(DMessage))]
|
[Receives(typeof(DMessage))]
|
||||||
class DEngine : Engine
|
class DEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
|
|
Loading…
Reference in New Issue