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); 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); 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); 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); throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
} }
this.writeTypes.Add(typeof(ComponentWriteMessage<>).MakeGenericType(writeType)); writeTypes.Add(writeType);
this.priorities.Add(writeType, priority); priorities.Add(writeType, priority);
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Encompass
throw new IllegalWritePendingTypeException("{0} must be a Component", writePendingType.Name); 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); componentMessageManager.Remove<TComponent>(entity);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity); drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity);
} }
private void Remove(Entity entity)
{
componentStore.Remove(entity);
}
} }
} }

View File

@ -22,6 +22,7 @@ namespace Encompass
componentStore.ClearAll(); componentStore.ClearAll();
existingComponentStore.ClearAll(); existingComponentStore.ClearAll();
pendingComponentStore.ClearAll(); pendingComponentStore.ClearAll();
UpToDateComponentStore.ClearAll();
foreach (var dictionary in typeToEntityToPendingComponentPriority.Values) foreach (var dictionary in typeToEntityToPendingComponentPriority.Values)
{ {
@ -34,18 +35,18 @@ namespace Encompass
upToDateComponentStore = componentStore; 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 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> sendTypes = new HashSet<Type>();
internal readonly HashSet<Type> receiveTypes = 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>(); internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
/// <summary> /// <summary>
@ -47,13 +51,12 @@ namespace Encompass
var activatesAttribute = GetType().GetCustomAttribute<WritesPending>(false); var activatesAttribute = GetType().GetCustomAttribute<WritesPending>(false);
if (activatesAttribute != null) if (activatesAttribute != null)
{ {
sendTypes.UnionWith(activatesAttribute.writePendingTypes); writePendingTypes = activatesAttribute.writePendingTypes;
} }
var writesAttributes = GetType().GetCustomAttributes<Writes>(false); foreach (var writesAttribute in GetType().GetCustomAttributes<Writes>(false))
foreach (var writesAttribute in writesAttributes)
{ {
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); 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); var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null) if (readsAttribute != null)
{ {
receiveTypes.UnionWith(readsAttribute.readTypes); readTypes = readsAttribute.readTypes;
} }
var readsPendingAttribute = GetType().GetCustomAttribute<ReadsPending>(false); var readsPendingAttribute = GetType().GetCustomAttribute<ReadsPending>(false);
if (readsPendingAttribute != null) 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 private IEnumerable<(Entity, TComponent)> ReadComponentsHelper<TComponent>() where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead) if (existingRead && pendingRead)
{ {
return componentMessageManager.ReadExistingAndPendingComponentsByType<TComponent>(); return componentMessageManager.ReadExistingAndPendingComponentsByType<TComponent>();
@ -222,8 +225,8 @@ namespace Encompass
private (Entity, TComponent) ReadComponentHelper<TComponent>() where TComponent : struct, IComponent private (Entity, TComponent) ReadComponentHelper<TComponent>() where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead) if (existingRead && pendingRead)
{ {
return componentMessageManager.ReadFirstExistingOrPendingComponentByType<TComponent>(); return componentMessageManager.ReadFirstExistingOrPendingComponentByType<TComponent>();
@ -264,8 +267,8 @@ namespace Encompass
/// </summary> /// </summary>
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead) if (existingRead && pendingRead)
{ {
return componentMessageManager.SomeExistingOrPendingComponent<TComponent>(); return componentMessageManager.SomeExistingOrPendingComponent<TComponent>();
@ -286,8 +289,8 @@ namespace Encompass
private TComponent GetComponentHelper<TComponent>(Entity entity) where TComponent : struct, IComponent private TComponent GetComponentHelper<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (existingRead && pendingRead) if (existingRead && pendingRead)
{ {
if (componentMessageManager.HasPendingComponent<TComponent>(entity)) if (componentMessageManager.HasPendingComponent<TComponent>(entity))
@ -339,8 +342,8 @@ namespace Encompass
/// </exception> /// </exception>
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>)); var pendingRead = readPendingTypes.Contains(typeof(TComponent));
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>)); var existingRead = readTypes.Contains(typeof(TComponent));
if (pendingRead && existingRead) if (pendingRead && existingRead)
{ {
@ -368,11 +371,8 @@ namespace Encompass
/// </exception> /// </exception>
protected bool HasComponent(Entity entity, Type type) protected bool HasComponent(Entity entity, Type type)
{ {
var pending = typeof(PendingComponentMessage<>).MakeGenericType(type); var pendingRead = readPendingTypes.Contains(type);
var existing = typeof(ComponentMessage<>).MakeGenericType(type); var existingRead = readTypes.Contains(type);
var pendingRead = receiveTypes.Contains(pending);
var existingRead = receiveTypes.Contains(existing);
if (pendingRead && existingRead) if (pendingRead && existingRead)
{ {
@ -402,27 +402,23 @@ namespace Encompass
{ {
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0; 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); 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; AddPendingComponent(entity, component, priority);
newComponentMessage.entity = entity;
newComponentMessage.component = component;
newComponentMessage.priority = priority;
SendPendingComponentMessage(newComponentMessage);
} }
else else
{ {
componentMessageManager.UpdateComponent<TComponent>(entity, component, priority); componentMessageManager.UpdateComponent(entity, component, priority);
} }
if (component is IDrawableComponent drawableComponent) 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); 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> /// <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) internal void RegisterComponent(Type componentType)
{ {
registeredComponentTypes.Add(componentType); registeredComponentTypes.Add(componentType);
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType))); AddEngine((Engine)Activator.CreateInstance(typeof(ComponentEmitter<>).MakeGenericType(componentType)));
} }
/// <summary> /// <summary>
@ -115,37 +115,31 @@ namespace Encompass
messageManager.RegisterMessageType(messageType); 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)) 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); 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); 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(); RegisterComponent(readTypes);
if (genericTypeDefinition == typeof(ComponentMessage<>) || genericTypeDefinition == typeof(PendingComponentMessage<>))
{
var componentType = receiveType.GetGenericArguments().Single();
if (!registeredComponentTypes.Contains(componentType))
{
RegisterComponent(componentType);
}
}
} }
}
foreach (var receiveType in engine.receiveTypes.Union(engine.readPendingTypes).Union(engine.readTypes))
{
if (!typeToReaders.ContainsKey(receiveType)) if (!typeToReaders.ContainsKey(receiveType))
{ {
typeToReaders.Add(receiveType, new HashSet<Engine>()); typeToReaders.Add(receiveType, new HashSet<Engine>());
@ -188,7 +182,7 @@ namespace Encompass
{ {
foreach (var senderEngine in senders) foreach (var senderEngine in senders)
{ {
foreach (var messageType in senderEngine.sendTypes) foreach (var messageType in senderEngine.sendTypes.Union(senderEngine.writePendingTypes))
{ {
if (typeToReaders.ContainsKey(messageType)) if (typeToReaders.ContainsKey(messageType))
{ {
@ -247,16 +241,12 @@ namespace Encompass
var defaultWritePriorityAttribute = engine.GetType().GetCustomAttribute<DefaultWritePriority>(false); var defaultWritePriorityAttribute = engine.GetType().GetCustomAttribute<DefaultWritePriority>(false);
var writeTypes = engine.sendTypes.Where((type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ComponentWriteMessage<>)); foreach (var writeType in engine.writeTypes)
foreach (var writeType in writeTypes)
{ {
var componentType = writeType.GetGenericArguments()[0];
int? priority = null; 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) else if (defaultWritePriorityAttribute != null)
{ {
@ -265,44 +255,44 @@ namespace Encompass
if (priority.HasValue) 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 else
{ {
writePriorities[componentType].Add(priority.Value); writePriorities[writeType].Add(priority.Value);
} }
} }
else else
{ {
if (writtenComponentTypesWithoutPriority.Contains(componentType) || writtenComponentTypesWithPriority.Contains(componentType)) if (writtenComponentTypesWithoutPriority.Contains(writeType) || writtenComponentTypesWithPriority.Contains(writeType))
{ {
duplicateWritesWithoutPriority.Add(componentType); duplicateWritesWithoutPriority.Add(writeType);
} }
else 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> <Company>Moonside Games</Company>
<Product>Encompass ECS</Product> <Product>Encompass ECS</Product>
<PackageProjectUrl>https://github.com/encompass-ecs</PackageProjectUrl> <PackageProjectUrl>https://github.com/encompass-ecs</PackageProjectUrl>
<PackageLicenseUrl/> <PackageLicenseUrl />
<Copyright>Evan Hemsley 2019</Copyright> <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> <Description>Encompass is an engine-agnostic Hyper ECS framework to help you code games, or other kinds of simulations.</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@ -19,10 +19,10 @@
<ItemGroup> <ItemGroup>
<None Include="..\LICENSE"> <None Include="..\LICENSE">
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath/> <PackagePath />
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MoonTools.Core.Graph" Version="1.0.0"/> <PackageReference Include="MoonTools.Core.Graph" Version="1.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

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