component removes are treated as priority writes

pull/5/head
Evan Hemsley 2019-12-23 18:01:49 -08:00
parent 51a248156e
commit 7885c2e0f4
8 changed files with 101 additions and 42 deletions

View File

@ -69,22 +69,35 @@ namespace Encompass
} }
public bool Set<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent public bool Set<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
if (Lookup<TComponent>().Set(entity, component, priority))
{ {
ComponentBitSet.Set<TComponent>(entity); ComponentBitSet.Set<TComponent>(entity);
return Lookup<TComponent>().Set(entity, component, priority); return true;
}
return false;
} }
public void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent public bool Remove<TComponent>(Entity entity, int priority) where TComponent : struct, IComponent
{
if (Lookup<TComponent>().Remove(entity, priority))
{ {
ComponentBitSet.RemoveComponent<TComponent>(entity); ComponentBitSet.RemoveComponent<TComponent>(entity);
Lookup<TComponent>().Remove(entity); return true;
}
return false;
}
public void ForceRemove<TComponent>(Entity entity) where TComponent : struct, IComponent
{
Lookup<TComponent>().ForceRemove(entity);
} }
public void Remove(Entity entity) public void Remove(Entity entity)
{ {
foreach (var entry in Stores.Values) foreach (var entry in Stores.Values)
{ {
entry.Remove(entity); entry.ForceRemove(entity);
} }
ComponentBitSet.RemoveEntity(entity); ComponentBitSet.RemoveEntity(entity);
} }

View File

@ -8,7 +8,8 @@ namespace Encompass
public abstract int Count { get; } public abstract int Count { get; }
public abstract IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped(); public abstract IEnumerable<(Entity, Type, IComponent)> AllInterfaceTyped();
public abstract bool Has(Entity entity); public abstract bool Has(Entity entity);
public abstract void Remove(Entity entity); public abstract bool Remove(Entity entity, int priority);
public abstract void ForceRemove(Entity entity);
public abstract void Clear(); public abstract void Clear();
} }
@ -40,6 +41,25 @@ namespace Encompass
return false; return false;
} }
public override bool Remove(Entity entity, int priority)
{
if (!priorities.ContainsKey(entity) || priority < priorities[entity])
{
priorities[entity] = priority;
store.Remove(entity);
priorities.Remove(entity);
return true;
}
return false;
}
public override void ForceRemove(Entity entity)
{
store.Remove(entity);
priorities.Remove(entity);
}
public override bool Has(Entity entity) public override bool Has(Entity entity)
{ {
return store.ContainsKey(entity); return store.ContainsKey(entity);
@ -66,11 +86,5 @@ namespace Encompass
yield return (kvp.Key, typeof(TComponent), (IComponent)kvp.Value); yield return (kvp.Key, typeof(TComponent), (IComponent)kvp.Value);
} }
} }
public override void Remove(Entity entity)
{
store.Remove(entity);
priorities.Remove(entity);
}
} }
} }

View File

@ -92,10 +92,24 @@ namespace Encompass
entitiesMarkedForRemoval.Clear(); entitiesMarkedForRemoval.Clear();
} }
public void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent public bool RemovePending<TComponent>(Entity entity, int priority) where TComponent : struct, IComponent
{
if (componentUpdateManager.RemovePending<TComponent>(entity, priority))
{ {
componentUpdateManager.Remove<TComponent>(entity);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity); drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity);
return true;
}
return false;
}
public bool Remove<TComponent>(Entity entity, int priority) where TComponent : struct, IComponent
{
if (componentUpdateManager.Remove<TComponent>(entity, priority))
{
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity);
return true;
}
return false;
} }
} }
} }

View File

@ -1,6 +1,4 @@
using Encompass.Collections; using System;
using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -8,7 +6,6 @@ namespace Encompass
{ {
internal class ComponentUpdateManager internal class ComponentUpdateManager
{ {
private readonly ComponentStore existingAndPendingComponentStore;
private readonly ComponentStore existingComponentStore; private readonly ComponentStore existingComponentStore;
private readonly ComponentStore pendingComponentStore; private readonly ComponentStore pendingComponentStore;
private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToPendingComponentPriority = new Dictionary<Type, Dictionary<Entity, int>>(128); private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToPendingComponentPriority = new Dictionary<Type, Dictionary<Entity, int>>(128);
@ -18,7 +15,6 @@ namespace Encompass
public ComponentUpdateManager(Dictionary<Type, int> typeToIndex) public ComponentUpdateManager(Dictionary<Type, int> typeToIndex)
{ {
existingAndPendingComponentStore = new ComponentStore(typeToIndex);
existingComponentStore = new ComponentStore(typeToIndex); existingComponentStore = new ComponentStore(typeToIndex);
pendingComponentStore = new ComponentStore(typeToIndex); pendingComponentStore = new ComponentStore(typeToIndex);
UpToDateComponentStore = new ComponentStore(typeToIndex); UpToDateComponentStore = new ComponentStore(typeToIndex);
@ -27,7 +23,6 @@ namespace Encompass
public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
{ {
existingAndPendingComponentStore.RegisterComponentType<TComponent>();
existingComponentStore.RegisterComponentType<TComponent>(); existingComponentStore.RegisterComponentType<TComponent>();
pendingComponentStore.RegisterComponentType<TComponent>(); pendingComponentStore.RegisterComponentType<TComponent>();
UpToDateComponentStore.RegisterComponentType<TComponent>(); UpToDateComponentStore.RegisterComponentType<TComponent>();
@ -35,7 +30,6 @@ namespace Encompass
public void FinishRegistering() public void FinishRegistering()
{ {
existingAndPendingComponentStore.FinishRegistering();
existingComponentStore.FinishRegistering(); existingComponentStore.FinishRegistering();
pendingComponentStore.FinishRegistering(); pendingComponentStore.FinishRegistering();
UpToDateComponentStore.FinishRegistering(); UpToDateComponentStore.FinishRegistering();
@ -43,7 +37,6 @@ namespace Encompass
internal void Clear() internal void Clear()
{ {
existingAndPendingComponentStore.ClearAll();
existingComponentStore.ClearAll(); existingComponentStore.ClearAll();
pendingComponentStore.ClearAll(); pendingComponentStore.ClearAll();
UpToDateComponentStore.ClearAll(); UpToDateComponentStore.ClearAll();
@ -77,9 +70,19 @@ namespace Encompass
return false; return false;
} }
internal bool RemovePending<TComponent>(Entity entity, int priority) where TComponent : struct, IComponent
{
UpToDateComponentStore.Remove<TComponent>(entity, priority);
return pendingComponentStore.Remove<TComponent>(entity, priority);
}
internal bool Remove<TComponent>(Entity entity, int priority) where TComponent : struct, IComponent
{
return UpToDateComponentStore.Remove<TComponent>(entity, priority);
}
private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{ {
existingAndPendingComponentStore.Set(entity, component);
UpToDateComponentStore.Set(entity, component); UpToDateComponentStore.Set(entity, component);
} }
@ -92,7 +95,7 @@ namespace Encompass
internal IEnumerable<(TComponent, Entity)> ReadExistingAndPendingComponentsByType<TComponent>() where TComponent : struct, IComponent internal IEnumerable<(TComponent, Entity)> ReadExistingAndPendingComponentsByType<TComponent>() where TComponent : struct, IComponent
{ {
return existingAndPendingComponentStore.All<TComponent>(); return UpToDateComponentStore.All<TComponent>();
} }
internal IEnumerable<(TComponent, Entity)> ReadExistingComponentsByType<TComponent>() where TComponent : struct, IComponent internal IEnumerable<(TComponent, Entity)> ReadExistingComponentsByType<TComponent>() where TComponent : struct, IComponent
@ -135,7 +138,7 @@ namespace Encompass
internal bool SomeExistingOrPendingComponent<TComponent>() where TComponent : struct, IComponent internal bool SomeExistingOrPendingComponent<TComponent>() where TComponent : struct, IComponent
{ {
return existingAndPendingComponentStore.Any<TComponent>(); return UpToDateComponentStore.Any<TComponent>();
} }
internal bool SomeExistingComponent<TComponent>() where TComponent : struct, IComponent internal bool SomeExistingComponent<TComponent>() where TComponent : struct, IComponent
@ -164,12 +167,12 @@ namespace Encompass
internal bool HasExistingOrPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent internal bool HasExistingOrPendingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
return existingAndPendingComponentStore.Has<TComponent>(entity); return UpToDateComponentStore.Has<TComponent>(entity);
} }
internal bool HasExistingOrPendingComponent(Entity entity, Type type) internal bool HasExistingOrPendingComponent(Entity entity, Type type)
{ {
return existingAndPendingComponentStore.Has(type, entity); return UpToDateComponentStore.Has(type, entity);
} }
internal bool HasExistingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent internal bool HasExistingComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
@ -192,11 +195,6 @@ namespace Encompass
return pendingComponentStore.Has(type, entity); return pendingComponentStore.Has(type, entity);
} }
internal void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent
{
UpToDateComponentStore.Remove<TComponent>(entity);
}
internal ComponentBitSet PendingBits { get { return pendingComponentStore.ComponentBitSet; } } internal ComponentBitSet PendingBits { get { return pendingComponentStore.ComponentBitSet; } }
internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } } internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } }
} }

View File

@ -96,7 +96,7 @@ namespace Encompass
if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity)) if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entity))
{ {
var layer = typeToEntityToLayer[typeof(TComponent)][entity]; var layer = typeToEntityToLayer[typeof(TComponent)][entity];
layerIndexToComponentStore[layer].Remove<TComponent>(entity); layerIndexToComponentStore[layer].ForceRemove<TComponent>(entity);
} }
typeToEntityToLayer[typeof(TComponent)].Remove(entity); typeToEntityToLayer[typeof(TComponent)].Remove(entity);
} }

View File

@ -561,12 +561,23 @@ namespace Encompass
/// Note that the Engine must Read the Component type that is being removed. /// Note that the Engine must Read the Component type that is being removed.
/// If a Component with the specified type does not exist on the Entity, returns false and does not mutate the Entity. /// If a Component with the specified type does not exist on the Entity, returns false and does not mutate the Entity.
/// </summary> /// </summary>
protected bool RemoveComponent<TComponent>(Entity entity) where TComponent : struct, IComponent protected void RemoveComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (!HasComponent<TComponent>(entity)) { return false; } var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : defaultWritePriority;
componentManager.Remove<TComponent>(entity); if (!writeTypes.Contains(typeof(TComponent)))
return true; {
throw new IllegalWriteException("Engine {0} tried to remove undeclared Component {1}. Declare with Writes attribute.", GetType().Name, typeof(TComponent).Name);
}
if (writePendingTypes.Contains(typeof(TComponent)))
{
componentManager.RemovePending<TComponent>(entity, priority);
}
else
{
componentManager.Remove<TComponent>(entity, priority);
}
} }
/// <summary> /// <summary>

View File

@ -369,6 +369,7 @@ namespace Tests
[Reads(typeof(MockComponent))] [Reads(typeof(MockComponent))]
[Receives(typeof(RemoveComponentTestMessage))] [Receives(typeof(RemoveComponentTestMessage))]
[Writes(typeof(MockComponent))]
class RemoveComponentTestEngine : Engine class RemoveComponentTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)

View File

@ -593,7 +593,8 @@ namespace Tests
Assert.That(results, Does.Not.Contain((mockComponent, entity))); Assert.That(results, Does.Not.Contain((mockComponent, entity)));
} }
[Reads(typeof(DestroyerComponent), typeof(MockComponent))] [Reads(typeof(DestroyerComponent))]
[Writes(typeof(MockComponent))]
class DestroyAndAddComponentEngine : Engine class DestroyAndAddComponentEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -665,6 +666,7 @@ namespace Tests
} }
[Reads(typeof(MockComponent))] [Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class DelayedMessageEngine : Engine class DelayedMessageEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -711,6 +713,7 @@ namespace Tests
} }
[Reads(typeof(MockComponent))] [Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class DelayedMessageIgnoringTimeDilationEngine : Engine class DelayedMessageIgnoringTimeDilationEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -754,7 +757,7 @@ namespace Tests
[Receives(typeof(MockMessage))] [Receives(typeof(MockMessage))]
[WritesPending(typeof(MockComponent))] [WritesPending(typeof(MockComponent))]
[Writes(typeof(MockComponent))] [Writes(typeof(MockComponent), 1)]
class ActivateComponentEngine : Engine class ActivateComponentEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -768,6 +771,7 @@ namespace Tests
} }
[ReadsPending(typeof(MockComponent))] [ReadsPending(typeof(MockComponent))]
[Writes(typeof(MockComponent), 0)]
class RemoveComponentEngine : Engine class RemoveComponentEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -780,17 +784,20 @@ namespace Tests
} }
[Test] [Test]
public void EngineAddAndRemoveComponentSameFrame() public void EngineAddAndRemoveComponentSameFrameWithRemovePriority()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateComponentEngine()); worldBuilder.AddEngine(new ActivateComponentEngine());
worldBuilder.AddEngine(new RemoveComponentEngine()); worldBuilder.AddEngine(new RemoveComponentEngine());
worldBuilder.AddEngine(new ReadComponentsTestEngine());
worldBuilder.SendMessage(new MockMessage { }); worldBuilder.SendMessage(new MockMessage { });
var world = worldBuilder.Build(); var world = worldBuilder.Build();
Assert.DoesNotThrow(() => world.Update(0.01)); Assert.DoesNotThrow(() => world.Update(0.01));
world.Update(0.01); // update again for the read
resultComponents.Should().BeEmpty();
} }
struct DestroyComponentMessage : IMessage { public Entity entity; } struct DestroyComponentMessage : IMessage { public Entity entity; }
@ -926,6 +933,7 @@ namespace Tests
} }
[Reads(typeof(MockComponent))] [Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class RemoveComponentByTypeEngine : Engine class RemoveComponentByTypeEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)