Skip to content

Commit

Permalink
remove EntityCommandRecorder.CreateEntity method, see new WorldRecord…
Browse files Browse the repository at this point in the history
… type

added EntityCommandRecorder.Record(World) method
added WorldRecord type to record action on World
  • Loading branch information
Doraku committed Sep 19, 2021
1 parent f25440b commit fc7a000
Show file tree
Hide file tree
Showing 13 changed files with 259 additions and 96 deletions.
7 changes: 7 additions & 0 deletions documentation/NEXT_RELEASENOTES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
breaking changes:
remove EntityCommandRecorder.CreateEntity method, see new WorldRecord type

---

added EntityCommandRecorder.Record(World) method
added WorldRecord type to record action on World
added EntitySortedSet type (#113)
added EntityQueryBuilder.AsSortedSet methods to create EntitySortedSet
added AEntitySortedSetSystem type
Expand Down
92 changes: 67 additions & 25 deletions source/DefaultEcs.Test/Command/EntityCommandRecorderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ public NonBlittable(int id, object item)
#region Tests

[Fact]
public void CreateEntity_Should_throw_When_world_is_null()
public void Record_Should_throw_When_world_is_null()
{
using EntityCommandRecorder recorder = new(1024);

Check.ThatCode(() => recorder.CreateEntity(default)).Throws<ArgumentNullException>();
Check.ThatCode(() => recorder.Record(default(World))).Throws<ArgumentNullException>();
}

[Fact]
Expand All @@ -36,17 +36,59 @@ public void CreateEntity_Should_create_an_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

recorder.CreateEntity(world);
recorder.CreateEntity(world);
recorder.CreateEntity(world);
recorder.CreateEntity(world);
recorder.CreateEntity(world);
WorldRecord record = recorder.Record(world);

record.CreateEntity();
record.CreateEntity();
record.CreateEntity();
record.CreateEntity();
record.CreateEntity();

recorder.Execute();

Check.That(world.Count()).IsEqualTo(5);
}

[Fact]
public void Set_Should_set_blittable_component_on_world()
{
using EntityCommandRecorder recorder = new(1024);
using World world = new();

recorder.Record(world).Set<int>();

recorder.Execute();

Check.That(world.Get<int>()).IsEqualTo(0);
}

[Fact]
public void Set_Should_set_non_blittable_component_on_world()
{
using EntityCommandRecorder recorder = new(1024);
using World world = new();

recorder.Record(world).Set("kikoo");

recorder.Execute();

Check.That(world.Get<string>()).IsEqualTo("kikoo");
}

[Fact]
public void Remove_Should_remove_component_from_world()
{
using EntityCommandRecorder recorder = new(1024);
using World world = new();

world.Set(42);
recorder.Record(world).Remove<int>();

recorder.Execute();

Check.That(world.Has<string>()).IsFalse();
}

[Fact]
public void Disable_Should_disable_recorded_entity()
{
Expand All @@ -69,7 +111,7 @@ public void Disable_Should_disable_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Disable();

recorder.Execute();
Expand Down Expand Up @@ -100,7 +142,7 @@ public void Enable_Should_enable_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Disable();
record.Enable();

Expand Down Expand Up @@ -131,7 +173,7 @@ public void Set_Should_set_blittable_component_on_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Set(true);

recorder.Execute();
Expand Down Expand Up @@ -163,7 +205,7 @@ public void Set_Should_set_reference_component_on_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Set(o);

recorder.Execute();
Expand Down Expand Up @@ -196,7 +238,7 @@ public void Set_Should_set_non_blittable_component_on_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Set(new NonBlittable(42, o));

recorder.Execute();
Expand Down Expand Up @@ -228,7 +270,7 @@ public void DisableT_Should_disable_component_of_type_T_on_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Set(true);
record.Disable<bool>();

Expand Down Expand Up @@ -261,7 +303,7 @@ public void EnableT_Should_enable_component_of_type_T_on_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Set(true);
record.Disable<bool>();
record.Enable<bool>();
Expand Down Expand Up @@ -294,7 +336,7 @@ public void Remove_Should_remove_component_on_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Set(true);
record.Remove<bool>();

Expand Down Expand Up @@ -332,7 +374,7 @@ public void NotifyChanged_Should_change_component_on_created_entity()
using World world = new();
using IDisposable changed = world.SubscribeComponentChanged((in Entity e, in bool _, in bool _) => result = e);

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Set(true);
record.NotifyChanged<bool>();

Expand Down Expand Up @@ -363,7 +405,7 @@ public void Dispose_Should_dispose_created_entity()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.Dispose();

recorder.Execute();
Expand Down Expand Up @@ -398,7 +440,7 @@ public void SetSameAs_Should_set_same_as_on_created_entity()
Entity reference = world.CreateEntity();
reference.Set(true);

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.SetSameAs<bool>(recorder.Record(reference));

recorder.Execute();
Expand All @@ -416,7 +458,7 @@ public void SetSameAsWorld_Should_set_same_as_on_created_entity()

world.Set(true);

EntityRecord record = recorder.CreateEntity(world);
EntityRecord record = recorder.Record(world).CreateEntity();
record.SetSameAsWorld<bool>();

recorder.Execute();
Expand All @@ -430,7 +472,7 @@ public void Should_work_in_multithread()
using EntityCommandRecorder recorder = new(8, int.MaxValue);
using World world = new();

Enumerable.Range(0, 100000).AsParallel().ForAll(_ => recorder.CreateEntity(world));
Enumerable.Range(0, 100000).AsParallel().ForAll(_ => recorder.Record(world).CreateEntity());

recorder.Execute();

Expand Down Expand Up @@ -465,12 +507,12 @@ public void Shoud_throw_When_no_more_space()
Check.That(recorder.Capacity).IsEqualTo(8);
Check.That(recorder.MaxCapacity).IsEqualTo(16);

recorder.CreateEntity(world);
recorder.Record(world).CreateEntity();

Check.That(recorder.Size).IsEqualTo(9);
Check.That(recorder.Capacity).IsEqualTo(recorder.MaxCapacity);

Check.ThatCode(() => recorder.CreateEntity(world)).Throws<InvalidOperationException>();
Check.ThatCode(() => recorder.Record(world).CreateEntity()).Throws<InvalidOperationException>();
}

[Fact]
Expand All @@ -479,7 +521,7 @@ public void Execute_Should_clear_recorded_command_after_executing()
using EntityCommandRecorder recorder = new();
using World world = new();

recorder.CreateEntity(world);
recorder.Record(world).CreateEntity();

Check.That(recorder.Size).IsNotZero();

Expand All @@ -500,7 +542,7 @@ public void Clear_Should_clear_recorded_command()
using EntityCommandRecorder recorder = new(5, 10);
using World world = new();

recorder.CreateEntity(world);
recorder.Record(world).CreateEntity();

Check.That(recorder.Size).IsNotZero();

Expand All @@ -519,7 +561,7 @@ public void CopyTo_Should_throw_When_cloner_is_null()
using EntityCommandRecorder recorder = new(1024);
using World world = new();

Check.ThatCode(() => recorder.CreateEntity(world).CopyTo(world, default)).Throws<ArgumentNullException>();
Check.ThatCode(() => recorder.Record(world).CreateEntity().CopyTo(world, default)).Throws<ArgumentNullException>();
}

[Fact]
Expand Down
3 changes: 2 additions & 1 deletion source/DefaultEcs/AoTHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public static void RegisterUnmanagedComponent<T>()
T value;

UnmanagedComponentCommand<T>.WriteComponent(default, (byte*)&value, default);
UnmanagedComponentCommand<T>.SetComponent(entity, default, (byte*)&value);
UnmanagedComponentCommand<T>.SetWorldComponent(world, default, (byte*)&value);
UnmanagedComponentCommand<T>.SetEntityComponent(entity, default, (byte*)&value);
}
}
}
Expand Down
83 changes: 34 additions & 49 deletions source/DefaultEcs/Command/EntityCommandRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,6 @@ public EntityCommandRecorder()

#region Methods

private void WriteCommand<T>(int offset, in T command)
where T : unmanaged
{
_lockObject?.EnterReadLock();
try
{
fixed (byte* memory = _memory)
{
*(T*)(memory + offset) = command;
}
}
finally
{
_lockObject?.ExitReadLock();
}
}

private int ReserveNextCommand(int commandSize)
{
static void Throw() => throw new InvalidOperationException("CommandBuffer is full.");
Expand Down Expand Up @@ -139,72 +122,74 @@ private int ReserveNextCommand(int commandSize)
return commandOffset;
}

internal void WriteCommand<T>(in T command) where T : unmanaged => WriteCommand(ReserveNextCommand(sizeof(T)), command);
internal int WriteCommand<T>(in T command) where T : unmanaged
{
int offset = ReserveNextCommand(sizeof(T));

internal void WriteSetCommand<T>(int entityOffset, in T component)
_lockObject?.EnterReadLock();
try
{
fixed (byte* memory = _memory)
{
*(T*)(memory + offset) = command;
}
}
finally
{
_lockObject?.ExitReadLock();
}

return offset;
}

internal int WriteComponentCommand<TCommand, TComponent>(in TCommand command, in TComponent component) where TCommand : unmanaged
{
int offset = ReserveNextCommand(sizeof(EntityOffsetComponentCommand) + ComponentCommands.ComponentCommand<T>.SizeOfT);
int offset = ReserveNextCommand(sizeof(TCommand) + ComponentCommands.ComponentCommand<TComponent>.SizeOfT);

_lockObject?.EnterReadLock();
try
{
fixed (byte* memory = _memory)
{
*(EntityOffsetComponentCommand*)(memory + offset) = new EntityOffsetComponentCommand(CommandType.Set, ComponentCommands.ComponentCommand<T>.Index, entityOffset);
ComponentCommands.ComponentCommand<T>.WriteComponent(_objects, memory + offset + sizeof(EntityOffsetComponentCommand), component);
*(TCommand*)(memory + offset) = command;
ComponentCommands.ComponentCommand<TComponent>.WriteComponent(_objects, memory + offset + sizeof(TCommand), component);
}
}
finally
{
_lockObject?.ExitReadLock();
}

return offset;
}

internal void WriteCloneCommand(int sourceOffset, int targetOffset, ComponentCloner cloner)
{
int clonerIndex;
lock (_objects)
{
clonerIndex = _objects.Count;
clonerIndex = _objects.Count;
_objects.Add(cloner);
}

WriteCommand(new CloneCommand(sourceOffset, targetOffset, clonerIndex));
}

/// <summary>
/// Gives an <see cref="EntityRecord"/> to record action on the given <see cref="Entity"/>.
/// This command takes 9 bytes.
/// Gives an <see cref="WorldRecord"/> to record action on the given <see cref="World"/>.
/// </summary>
/// <param name="entity">The <see cref="EntityRecord"/> used to record action on the given <see cref="Entity"/>.</param>
/// <returns>The <see cref="EntityRecord"/> used to record actions on the given <see cref="Entity"/>.</returns>
/// <exception cref="InvalidOperationException">Command buffer is full.</exception>
public EntityRecord Record(in Entity entity)
{
int offset = ReserveNextCommand(sizeof(EntityCommand));

WriteCommand(offset, new EntityCommand(CommandType.Entity, entity));

return new EntityRecord(this, offset + sizeof(CommandType));
}
/// <param name="world">The <see cref="World"/> to record action for.</param>
/// <returns>The <see cref="WorldRecord"/> used to record actions on the given <see cref="World"/>.</returns>
public WorldRecord Record(World world) => new(this, world ?? throw new ArgumentNullException(nameof(world)));

/// <summary>
/// Records the creation of an <see cref="Entity"/> on a <see cref="World"/> and returns an <see cref="EntityRecord"/> to record action on it.
/// Gives an <see cref="EntityRecord"/> to record action on the given <see cref="Entity"/>.
/// This command takes 9 bytes.
/// </summary>
/// <param name="world">The <see cref="World"/> on which the entity need to be created.</param>
/// <returns>The <see cref="EntityRecord"/> used to record actions on the later created <see cref="Entity"/>.</returns>
/// <param name="entity">The <see cref="Entity"/> to record action for.</param>
/// <returns>The <see cref="EntityRecord"/> used to record actions on the given <see cref="Entity"/>.</returns>
/// <exception cref="InvalidOperationException">Command buffer is full.</exception>
public EntityRecord CreateEntity(World world)
{
if (world is null) throw new ArgumentNullException(nameof(world));

int offset = ReserveNextCommand(sizeof(EntityCommand));

WriteCommand(offset, new EntityCommand(CommandType.CreateEntity, new Entity(world.WorldId)));

return new EntityRecord(this, offset + sizeof(CommandType));
}
public EntityRecord Record(in Entity entity) => new(this, WriteCommand(new EntityCommand(CommandType.Entity, entity)) + sizeof(CommandType));

/// <summary>
/// Executes all recorded commands and clears those commands.
Expand Down
Loading

0 comments on commit fc7a000

Please sign in to comment.