diff --git a/src/Arch.Tests/ArchetypeTest.cs b/src/Arch.Tests/ArchetypeTest.cs index d02ff46..bfea697 100644 --- a/src/Arch.Tests/ArchetypeTest.cs +++ b/src/Arch.Tests/ArchetypeTest.cs @@ -245,7 +245,6 @@ public void CopyTo([Values(1111,2222,3333)] int sourceAmount, [Values(1111,2222, // Copy from one chunk into other. Archetype.Copy(source, destination); - source.Clear(); var sourceCounter = sourceAmount; var destinationCounter = destinationAmount; @@ -358,7 +357,6 @@ public void CopyToShift([Values(1111,2222,3333)] int sourceAmount, [Values(1111, // Copy from one chunk into other. Archetype.Copy(source, destination); - source.Clear(); var requiredChunksForSource = Archetype.GetChunkCapacityFor(source.EntitiesPerChunk, sourceAmount); var requiredChunksForDestination = Archetype.GetChunkCapacityFor(destination.EntitiesPerChunk, sourceAmount + destinationAmount); diff --git a/src/Arch.Tests/WorldTest.cs b/src/Arch.Tests/WorldTest.cs index 0c5d347..d6d47b9 100644 --- a/src/Arch.Tests/WorldTest.cs +++ b/src/Arch.Tests/WorldTest.cs @@ -866,4 +866,69 @@ public void GeneratedAdd() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(arch, Is.EqualTo(_world.GetArchetype(entity))); } + + /// + /// Checks if the is copied correctly over a new instance. + /// + [Test] + public void Copy() + { + using var source = World.Create(); + source.Create(new Transform(), new Rotation()); + source.Create(new Transform(), new Rotation()); + source.Create(new Transform()); + source.Create(new Transform(), new Rotation()); + source.Create(new Rotation()); + source.Create(new Transform()); + + var copy = source.Copy(); + + That(copy.Size, Is.EqualTo(source.Size)); + That(copy.Capacity, Is.EqualTo(source.Capacity)); + That(copy.Archetypes.Count, Is.EqualTo(source.Archetypes.Count)); + //same amount of entities per archetype + for (var index = 0; index < copy.Archetypes.Items.Count; index++) + { + var copyArchetype = copy.Archetypes.Items[index]; + var sourceArchetypeExists = source.TryGetArchetype(copyArchetype.Signature, out var sourceArchetype); + That(sourceArchetypeExists, Is.True); + That(sourceArchetype, Is.Not.Null); + That(copyArchetype.Count, Is.EqualTo(sourceArchetype.Count)); + That(copyArchetype.EntityCount, Is.EqualTo(sourceArchetype.EntityCount)); + } + } + + /// + /// Checks if the is copied correctly over a new instance, skipping empty archetypes. + /// + [Test] + public void CopySkipping() + { + using var source = World.Create(); + source.Create(new Transform(), new Rotation()); + source.Create(new Transform(), new Rotation()); + source.Create(new Transform()); + source.Create(new Transform(), new Rotation()); + source.Create(new Transform()); + source.Create(new Transform()); + //this will cause one archetype to be empty and ignored in the copy + source.Destroy(source.Create(new Rotation())); + + var copy = source.Copy(); + + That(copy.Size, Is.EqualTo(source.Size)); + //LessThan because one archetype got skipped + That(copy.Capacity, Is.LessThan(source.Capacity)); + That(copy.Archetypes.Count, Is.LessThan(source.Archetypes.Count)); + //same amount of entities per archetype + for (var index = 0; index < copy.Archetypes.Items.Count; index++) + { + var copyArchetype = copy.Archetypes.Items[index]; + var sourceArchetypeExists = source.TryGetArchetype(copyArchetype.Signature, out var sourceArchetype); + That(sourceArchetypeExists, Is.True); + That(sourceArchetype, Is.Not.Null); + That(copyArchetype.Count, Is.EqualTo(sourceArchetype.Count)); + That(copyArchetype.EntityCount, Is.EqualTo(sourceArchetype.EntityCount)); + } + } } diff --git a/src/Arch/Core/Archetype.cs b/src/Arch/Core/Archetype.cs index 931df94..a06537a 100644 --- a/src/Arch/Core/Archetype.cs +++ b/src/Arch/Core/Archetype.cs @@ -867,7 +867,8 @@ internal static int GetNextSlots(Archetype archetype, Span slots, int amou /// /// The source . /// The destination . - internal static void Copy(Archetype source, Archetype destination) + /// Whether to clear the source Archetype and Chunk counts. + internal static void Copy(Archetype source, Archetype destination, bool clearSource = true) { // Make sure other archetype can fit additional entities from this archetype. destination.EnsureEntityCapacity(destination.EntityCount + source.EntityCount); @@ -880,19 +881,25 @@ internal static void Copy(Archetype source, Archetype destination) var amountCopied = 0; var chunkIndex = 0; + var amountLeft = sourceChunk.Count; // Loop over destination chunk and fill them with the source chunk till either the source chunk is empty or theres no more capacity - for (int destinationChunkIndex = destination.Count; destinationChunkIndex < destination.ChunkCapacity && sourceChunk.Count > 0; destinationChunkIndex++) + for (int destinationChunkIndex = destination.Count; destinationChunkIndex < destination.ChunkCapacity && amountLeft > 0; destinationChunkIndex++) { // Determine amount that can be copied into destination ref var destinationChunk = ref destination.GetChunk(destinationChunkIndex); var remainingCapacity = destinationChunk.Buffer; - var amountToCopy = Math.Min(sourceChunk.Count, remainingCapacity); + var amountToCopy = Math.Min(amountLeft, remainingCapacity); Chunk.Copy(ref sourceChunk, amountCopied, ref sourceSignature, ref destinationChunk, destinationChunk.Count, amountToCopy); // Apply copied amount to track the progress - sourceChunk.Count -= amountToCopy; + amountLeft -= amountToCopy; + if (clearSource) + { + sourceChunk.Count -= amountToCopy; + } + destinationChunk.Count += amountToCopy; amountCopied += amountToCopy; chunkIndex = destinationChunkIndex; // Track the last destination chunk we filled, important @@ -903,8 +910,11 @@ internal static void Copy(Archetype source, Archetype destination) // Update entity counts destination.EntityCount += source.EntityCount; - source.EntityCount = 0; - source.Count = 0; + if (clearSource) + { + source.EntityCount = 0; + source.Count = 0; + } } /// diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs index 5af4c0b..9fdeb4b 100644 --- a/src/Arch/Core/World.cs +++ b/src/Arch/Core/World.cs @@ -1744,4 +1744,31 @@ public Signature GetSignature(Entity entity) } } +public partial class World +{ + /// + /// Creates a deep copy of the world, copying archetypes and chunks. + /// + /// A newly created World with all data copied from the original. + public World Copy() + { + var copy = Create(); + var archetypes = Archetypes.Items; + for (var index= 0; index < archetypes.Count; index++) + { + var archetype = archetypes[index]; + if (archetype.EntityCount <= 0) + { + continue; + } + + var newArchetype = copy.GetOrCreate(archetype.Signature); + Archetype.Copy(archetype, newArchetype, false); + copy.Size += newArchetype.EntityCount; + } + + return copy; + } +} + #endregion