diff --git a/src/World.Deferred.cs b/src/World.Deferred.cs index 8384f14..22b7564 100644 --- a/src/World.Deferred.cs +++ b/src/World.Deferred.cs @@ -6,6 +6,7 @@ namespace TinyEcs; public sealed partial class World { private readonly ConcurrentQueue _operations = new(); + private readonly ConcurrentDictionary _createdEntities = new(); private readonly ConcurrentDictionary> _deferredSets = new(); private WorldState _worldState = new () { State = WorldStateTypes.Normal, Locks = 0 }; private readonly object _worldStateLock = new object(); @@ -144,6 +145,19 @@ private void UnsetDeferred(EcsID entity, EcsID id) } } + private void CreateDeferred(EcsID entity) + { + var cmd = new DeferredOp() + { + Op = DeferredOpTypes.CreateEntity, + Entity = entity, + }; + + _operations.Enqueue(cmd); + + _createdEntities[entity] = entity; + } + private void DeleteDeferred(EcsID entity) { var cmd = new DeferredOp() @@ -160,6 +174,10 @@ private void DeleteDeferred(EcsID entity) } } + private bool ExistsDeferred(EcsID entity) + => entity.IsPair ? _createdEntities.ContainsKey(entity.First) && + _createdEntities.ContainsKey(entity.Second) : _createdEntities.ContainsKey(entity); + private bool HasDeferred(EcsID entity) where T : struct => _deferredSets.TryGetValue(entity, out var dict) && dict.ContainsKey(Component().ID); @@ -196,6 +214,9 @@ private void Merge() { switch (op.Op) { + case DeferredOpTypes.CreateEntity: + var ent = Entity(op.Entity); + break; case DeferredOpTypes.DestroyEntity: Delete(op.Entity); break; @@ -231,6 +252,7 @@ private void Merge() } _deferredSets.Clear(); + _createdEntities.Clear(); } enum WorldStateTypes @@ -256,6 +278,7 @@ struct DeferredOp enum DeferredOpTypes : byte { + CreateEntity, DestroyEntity, SetComponent, UnsetComponent, diff --git a/src/World.cs b/src/World.cs index 7658821..184215c 100644 --- a/src/World.cs +++ b/src/World.cs @@ -146,6 +146,14 @@ internal EntityView NewEmpty(ulong id = 0) { lock (_newEntLock) { + if (IsDeferred) + { + if (id == 0) + id = ++_entities.MaxID; + CreateDeferred(id); + return new EntityView(this, id); + } + ref var record = ref ( id > 0 ? ref _entities.Add(id, default!) : ref _entities.CreateNew(out id) ); @@ -203,6 +211,9 @@ public void Delete(EcsID entity) public bool Exists(EcsID entity) { + if (IsDeferred && ExistsDeferred(entity)) + return true; + if (entity.IsPair) { return _entities.Contains(entity.First) && _entities.Contains(entity.Second); diff --git a/tests/Deferred.cs b/tests/Deferred.cs index 76e3825..daf3dcc 100644 --- a/tests/Deferred.cs +++ b/tests/Deferred.cs @@ -2,6 +2,8 @@ namespace TinyEcs.Tests { public class DeferredTest { + struct JustForTest { } + [Fact] public void Deferred_CheckWorldState() { @@ -12,31 +14,28 @@ public void Deferred_CheckWorldState() Assert.False(ctx.World.IsDeferred); } - [Theory] - [InlineData(1_000)] - [InlineData(1_000_00)] - [InlineData(1_000_000)] - public void Deferred_NewEntity(int amount) + [Fact] + public void Deferred_NewEntity() { using var ctx = new Context(); var count = ctx.World.EntityCount; - var expected = count + amount * 2; - - var t0 = new Thread(() => { for (var i = 0; i < amount; ++i) ctx.World.Entity(); }) - { IsBackground = true }; - var t1 = new Thread(() => { for (var i = 0; i < amount; ++i) ctx.World.Entity(); }) - { IsBackground = true }; ctx.World.BeginDeferred(); - t0.Start(); - t1.Start(); + var e0 = ctx.World.Entity("An entity"); + var e1 = ctx.World.Entity(); + var e2 = ctx.World.Entity(); - t0.Join(); - t1.Join(); + Assert.True(e0.Exists()); + Assert.True(e1.Exists()); + Assert.True(e2.Exists()); ctx.World.EndDeferred(); - Assert.Equal(expected, ctx.World.EntityCount); + Assert.True(e0.Exists()); + Assert.True(e1.Exists()); + Assert.True(e2.Exists()); + + Assert.Equal(count + 3, ctx.World.EntityCount); } [Fact]