remove component messages

pull/5/head
Evan Hemsley 2019-12-05 19:55:17 -08:00
parent d4c6b9416d
commit 005405512e
15 changed files with 105 additions and 146 deletions

View File

@ -21,7 +21,7 @@ namespace Encompass
throw new IllegalReadTypeException("{0} must be a Component", readType.Name);
}
this.readTypes.Add(typeof(ComponentMessage<>).MakeGenericType(readType));
this.readTypes.Add(readType);
}
}
}

View File

@ -21,7 +21,7 @@ namespace Encompass
throw new IllegalReadTypeException("{0} must be a Component", readPendingType.Name);
}
this.readPendingTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(readPendingType));
this.readPendingTypes.Add(readPendingType);
}
}
}

View File

@ -21,7 +21,7 @@ namespace Encompass
throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
}
this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType));
this.writeTypes.Add(writeType);
}
}
@ -33,8 +33,8 @@ namespace Encompass
throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
}
this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType));
this.priorities.Add(writeType, priority);
writeTypes.Add(writeType);
priorities.Add(writeType, priority);
}
}
}

View File

@ -19,7 +19,7 @@ namespace Encompass
throw new IllegalWritePendingTypeException("{0} must be a Component", writePendingType.Name);
}
this.writePendingTypes.Add(typeof(PendingComponentMessage<>).MakeGenericType(writePendingType));
this.writePendingTypes.Add(writePendingType);
}
}
}

View File

@ -84,10 +84,5 @@ namespace Encompass
componentMessageManager.Remove<TComponent>(entity);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity);
}
private void Remove(Entity entity)
{
componentStore.Remove(entity);
}
}
}

View File

@ -22,6 +22,7 @@ namespace Encompass
componentStore.ClearAll();
existingComponentStore.ClearAll();
pendingComponentStore.ClearAll();
UpToDateComponentStore.ClearAll();
foreach (var dictionary in typeToEntityToPendingComponentPriority.Values)
{
@ -34,18 +35,18 @@ namespace Encompass
upToDateComponentStore = componentStore;
}
internal void AddExistingComponentMessage<TComponent>(ComponentMessage<TComponent> componentMessage) where TComponent : struct, IComponent
internal void AddExistingComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
RegisterExistingOrPendingComponentMessage(componentMessage.entity, componentMessage.component);
RegisterExistingOrPendingComponentMessage(entity, component);
existingComponentStore.Set(componentMessage.entity, componentMessage.component);
existingComponentStore.Set(entity, component);
}
internal void AddPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> pendingComponentMessage) where TComponent : struct, IComponent
internal void AddPendingComponent<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
if (pendingComponentStore.Set(pendingComponentMessage.entity, pendingComponentMessage.component, pendingComponentMessage.priority))
if (pendingComponentStore.Set(entity, component, priority))
{
RegisterExistingOrPendingComponentMessage(pendingComponentMessage.entity, pendingComponentMessage.component);
RegisterExistingOrPendingComponentMessage(entity, component);
}
}

View File

@ -15,8 +15,12 @@ namespace Encompass
{
internal Guid ID;
internal readonly HashSet<Type> readTypes = new HashSet<Type>();
internal readonly HashSet<Type> readPendingTypes = new HashSet<Type>();
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
internal readonly HashSet<Type> writeTypes = new HashSet<Type>();
internal readonly HashSet<Type> writePendingTypes = new HashSet<Type>();
internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
/// <summary>
@ -47,13 +51,12 @@ namespace Encompass
var activatesAttribute = GetType().GetCustomAttribute<WritesPending>(false);
if (activatesAttribute != null)
{
sendTypes.UnionWith(activatesAttribute.writePendingTypes);
writePendingTypes = activatesAttribute.writePendingTypes;
}
var writesAttributes = GetType().GetCustomAttributes<Writes>(false);
foreach (var writesAttribute in writesAttributes)
foreach (var writesAttribute in GetType().GetCustomAttributes<Writes>(false))
{
sendTypes.UnionWith(writesAttribute.writeTypes);
writeTypes.UnionWith(writesAttribute.writeTypes);
writePriorities = new Dictionary<Type, int>[2] { writePriorities, writesAttribute.priorities }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
}
@ -66,13 +69,13 @@ namespace Encompass
var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null)
{
receiveTypes.UnionWith(readsAttribute.readTypes);
readTypes = readsAttribute.readTypes;
}
var readsPendingAttribute = GetType().GetCustomAttribute<ReadsPending>(false);
if (readsPendingAttribute != null)
{
receiveTypes.UnionWith(readsPendingAttribute.readPendingTypes);
readPendingTypes = readsPendingAttribute.readPendingTypes;
}
}
@ -179,8 +182,8 @@ namespace Encompass
private IEnumerable<(Entity, TComponent)> ReadComponentsHelper<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadExistingAndPendingComponentsByType<TComponent>();
@ -222,8 +225,8 @@ namespace Encompass
private (Entity, TComponent) ReadComponentHelper<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
return componentMessageManager.ReadFirstExistingOrPendingComponentByType<TComponent>();
@ -264,8 +267,8 @@ namespace Encompass
/// </summary>
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
return componentMessageManager.SomeExistingOrPendingComponent<TComponent>();
@ -286,8 +289,8 @@ namespace Encompass
private TComponent GetComponentHelper<TComponent>(Entity entity) where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead)
{
if (componentMessageManager.HasPendingComponent<TComponent>(entity))
@ -339,8 +342,8 @@ namespace Encompass
/// </exception>
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
if (pendingRead && existingRead)
{
@ -368,11 +371,8 @@ namespace Encompass
/// </exception>
protected bool HasComponent(Entity entity, Type type)
{
var pending = typeof(PendingComponentMessage<>).MakeGenericType(type);
var existing = typeof(ComponentMessage<>).MakeGenericType(type);
var pendingRead = receiveTypes.Contains(pending);
var existingRead = receiveTypes.Contains(existing);
var pendingRead = readPendingTypes.Contains(type);
var existingRead = readTypes.Contains(type);
if (pendingRead && existingRead)
{
@ -402,27 +402,23 @@ namespace Encompass
{
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0;
if (!sendTypes.Contains(typeof(ComponentWriteMessage<TComponent>)))
if (!writeTypes.Contains(typeof(TComponent)))
{
throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
if (sendTypes.Contains(typeof(PendingComponentMessage<TComponent>)))
if (writePendingTypes.Contains(typeof(TComponent)))
{
PendingComponentMessage<TComponent> newComponentMessage;
newComponentMessage.entity = entity;
newComponentMessage.component = component;
newComponentMessage.priority = priority;
SendPendingComponentMessage(newComponentMessage);
AddPendingComponent(entity, component, priority);
}
else
{
componentMessageManager.UpdateComponent<TComponent>(entity, component, priority);
componentMessageManager.UpdateComponent(entity, component, priority);
}
if (component is IDrawableComponent drawableComponent)
{
componentManager.RegisterDrawableComponent<TComponent>(entity, component, drawableComponent.Layer);
componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer);
}
}
@ -473,14 +469,14 @@ namespace Encompass
messageManager.AddMessage(message);
}
internal void SendExistingComponentMessage<TComponent>(ComponentMessage<TComponent> message) where TComponent : struct, IComponent
internal void AddExistingComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{
componentMessageManager.AddExistingComponentMessage(message);
componentMessageManager.AddExistingComponent(entity, component);
}
internal void SendPendingComponentMessage<TComponent>(PendingComponentMessage<TComponent> message) where TComponent : struct, IComponent
internal void AddPendingComponent<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
componentMessageManager.AddPendingComponentMessage(message);
componentMessageManager.AddPendingComponent<TComponent>(entity, component, priority);
}
/// <summary>

View File

@ -0,0 +1,18 @@
namespace Encompass
{
internal class ComponentEmitter<TComponent> : Engine where TComponent : struct, IComponent
{
public ComponentEmitter()
{
sendTypes.Add(typeof(TComponent));
}
public override void Update(double dt)
{
foreach (var (component, entity) in InternalRead<TComponent>())
{
AddExistingComponent(entity, component);
}
}
}
}

View File

@ -1,22 +0,0 @@
namespace Encompass
{
internal class ComponentMessageEmitter<TComponent> : Engine where TComponent : struct, IComponent
{
public ComponentMessageEmitter() : base()
{
sendTypes.Add(typeof(ComponentMessage<TComponent>));
}
public override void Update(double dt)
{
foreach (var (component, entity) in InternalRead<TComponent>())
{
ComponentMessage<TComponent> componentMessage;
componentMessage.entity = entity;
componentMessage.component = component;
SendMessage(componentMessage);
SendExistingComponentMessage(componentMessage);
}
}
}
}

View File

@ -1,8 +0,0 @@
namespace Encompass
{
internal struct ComponentMessage<TComponent> : IMessage where TComponent : struct, IComponent
{
public Entity entity;
public TComponent component;
}
}

View File

@ -1,10 +0,0 @@
using System;
namespace Encompass
{
internal struct ComponentWriteMessage<TComponent> : IMessage where TComponent : struct, IComponent
{
public Guid componentID;
public TComponent component;
}
}

View File

@ -1,9 +0,0 @@
namespace Encompass
{
internal struct PendingComponentMessage<TComponent> : IMessage where TComponent : struct, IComponent
{
public Entity entity;
public TComponent component;
public int priority;
}
}

View File

@ -89,7 +89,7 @@ namespace Encompass
internal void RegisterComponent(Type componentType)
{
registeredComponentTypes.Add(componentType);
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentEmitter<>).MakeGenericType(componentType)));
}
/// <summary>
@ -115,37 +115,31 @@ namespace Encompass
messageManager.RegisterMessageType(messageType);
}
foreach (var writePendingType in engine.writePendingTypes.Intersect(engine.readPendingTypes))
{
throw new EngineSelfCycleException("Engine {0} both writes and reads pending Component {1}", engine.GetType().Name, writePendingType.Name);
}
foreach (var messageType in messageReceiveTypes.Intersect(messageSendTypes))
{
if ((messageType.IsGenericType && messageType.GetGenericTypeDefinition() == typeof(PendingComponentMessage<>)))
{
var componentType = messageType.GetGenericArguments().Single();
throw new EngineSelfCycleException("Engine {0} both activates and reads pending Component {1}", engine.GetType().Name, componentType.Name);
}
throw new EngineSelfCycleException("Engine {0} both receives and sends Message {1}", engine.GetType().Name, messageType.Name);
}
if (messageSendTypes.Any())
if (messageSendTypes.Count > 0 || engine.writePendingTypes.Count > 0)
{
senders.Add(engine);
}
foreach (var receiveType in engine.receiveTypes)
foreach (var readTypes in engine.readTypes)
{
if (receiveType.IsGenericType)
if (!registeredComponentTypes.Contains(readTypes))
{
var genericTypeDefinition = receiveType.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(PendingComponentMessage<>))
{
var componentType = receiveType.GetGenericArguments().Single();
if (!registeredComponentTypes.Contains(componentType))
{
RegisterComponent(componentType);
}
}
RegisterComponent(readTypes);
}
}
foreach (var receiveType in engine.receiveTypes.Union(engine.readPendingTypes).Union(engine.readTypes))
{
if (!typeToReaders.ContainsKey(receiveType))
{
typeToReaders.Add(receiveType, new HashSet<Engine>());
@ -188,7 +182,7 @@ namespace Encompass
{
foreach (var senderEngine in senders)
{
foreach (var messageType in senderEngine.sendTypes)
foreach (var messageType in senderEngine.sendTypes.Union(senderEngine.writePendingTypes))
{
if (typeToReaders.ContainsKey(messageType))
{
@ -247,16 +241,12 @@ namespace Encompass
var defaultWritePriorityAttribute = engine.GetType().GetCustomAttribute<DefaultWritePriority>(false);
var writeTypes = engine.sendTypes.Where((type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ComponentWriteMessage<>));
foreach (var writeType in writeTypes)
foreach (var writeType in engine.writeTypes)
{
var componentType = writeType.GetGenericArguments()[0];
int? priority = null;
if (engine.writePriorities.ContainsKey(componentType))
if (engine.writePriorities.ContainsKey(writeType))
{
priority = engine.writePriorities[componentType];
priority = engine.writePriorities[writeType];
}
else if (defaultWritePriorityAttribute != null)
{
@ -265,44 +255,44 @@ namespace Encompass
if (priority.HasValue)
{
writtenComponentTypesWithPriority.Add(componentType);
writtenComponentTypesWithPriority.Add(writeType);
if (!writePriorities.ContainsKey(componentType))
if (!writePriorities.ContainsKey(writeType))
{
writePriorities[componentType] = new HashSet<int>();
writePriorities[writeType] = new HashSet<int>();
}
if (writePriorities[componentType].Contains(priority.Value))
if (writePriorities[writeType].Contains(priority.Value))
{
duplicateWritesWithSamePriority.Add(componentType);
duplicateWritesWithSamePriority.Add(writeType);
}
else if (writtenComponentTypesWithoutPriority.Contains(componentType))
else if (writtenComponentTypesWithoutPriority.Contains(writeType))
{
duplicateWritesWithoutPriority.Add(componentType);
duplicateWritesWithoutPriority.Add(writeType);
}
else
{
writePriorities[componentType].Add(priority.Value);
writePriorities[writeType].Add(priority.Value);
}
}
else
{
if (writtenComponentTypesWithoutPriority.Contains(componentType) || writtenComponentTypesWithPriority.Contains(componentType))
if (writtenComponentTypesWithoutPriority.Contains(writeType) || writtenComponentTypesWithPriority.Contains(writeType))
{
duplicateWritesWithoutPriority.Add(componentType);
duplicateWritesWithoutPriority.Add(writeType);
}
else
{
writtenComponentTypesWithoutPriority.Add(componentType);
writtenComponentTypesWithoutPriority.Add(writeType);
}
}
if (!writeMessageToEngines.ContainsKey(componentType))
if (!writeMessageToEngines.ContainsKey(writeType))
{
writeMessageToEngines[componentType] = new List<Engine>();
writeMessageToEngines[writeType] = new List<Engine>();
}
writeMessageToEngines[componentType].Add(engine);
writeMessageToEngines[writeType].Add(engine);
}
}

View File

@ -9,7 +9,7 @@
<Company>Moonside Games</Company>
<Product>Encompass ECS</Product>
<PackageProjectUrl>https://github.com/encompass-ecs</PackageProjectUrl>
<PackageLicenseUrl/>
<PackageLicenseUrl />
<Copyright>Evan Hemsley 2019</Copyright>
<Description>Encompass is an engine-agnostic Hyper ECS framework to help you code games, or other kinds of simulations.</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@ -19,10 +19,10 @@
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath/>
<PackagePath />
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MoonTools.Core.Graph" Version="1.0.0"/>
<PackageReference Include="MoonTools.Core.Graph" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -415,6 +415,8 @@ namespace Tests
}
}
static bool hasComponentResult;
[Receives(typeof(CheckHasMockComponentMessage))]
[Reads(typeof(MockComponent))]
class CheckHasMockComponentEngine : Engine
@ -423,13 +425,13 @@ namespace Tests
{
foreach (var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
{
Assert.IsTrue(HasComponent<MockComponent>(checkHasMockComponentMessage.entity));
hasComponentResult = HasComponent<MockComponent>(checkHasMockComponentMessage.entity);
}
}
}
[Test]
public void RemoveComponent()
public void RemovedComponentShouldStillExistOnSameFrame()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
@ -451,6 +453,12 @@ namespace Tests
var world = worldBuilder.Build();
world.Update(0.01f);
hasComponentResult.Should().BeTrue();
world.Update(0.01f);
hasComponentResult.Should().BeFalse();
}
struct CheckHasMockComponentMessage : IMessage