Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/Arch.Tests/ArchetypeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,37 @@ public void Move()
That(otherArchetype.Get<Rotation>(ref newSlot).Y, Is.EqualTo(10));
}

/// <summary>
/// Checks that <see cref="Archetype.Version"/> advances whenever <see cref="Entity"/>s enter or leave,
/// including a same-count remove-then-add swap where <see cref="Archetype.EntityCount"/> ends unchanged.
/// </summary>
[Test]
public void Version()
{
var archetype = new Archetype(_group, _baseChunkSize, _baseChunkEntityCount);

var initial = archetype.Version;
archetype.Add(new Entity(1, 0), out _, out var firstSlot);
var afterFirstAdd = archetype.Version;
That(afterFirstAdd, Is.GreaterThan(initial));

archetype.Add(new Entity(2, 0), out _, out _);
var afterSecondAdd = archetype.Version;
That(afterSecondAdd, Is.GreaterThan(afterFirstAdd));

// Same-count swap: remove one and add one. EntityCount returns to 2 but Version must still advance.
var countBefore = archetype.EntityCount;
archetype.Remove(firstSlot, out _);
archetype.Add(new Entity(3, 0), out _, out _);
That(archetype.EntityCount, Is.EqualTo(countBefore));
That(archetype.Version, Is.GreaterThan(afterSecondAdd));

// Clearing a non-empty archetype advances the version too.
var beforeClear = archetype.Version;
archetype.Clear();
That(archetype.Version, Is.GreaterThan(beforeClear));
}

/// <summary>
/// Checks if a copy operation between <see cref="Archetype"/> was successful.
/// This is checked by value equality of the items and their correct order.
Expand Down
23 changes: 22 additions & 1 deletion src/Arch/Core/Archetype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,31 @@ internal Slot CurrentSlot
/// <summary>
/// The number of <see cref="Arch.Core.Entity"/>s in this <see cref="Archetype"/>.
/// </summary>
private int _entityCount;

public int EntityCount
{
get => _entityCount;
internal set
{
// Version advances on every write to the population, so an unchanged
// Version proves the entity set of this archetype is unchanged.
// Bumping in the setter makes it impossible for any add, remove, move
// or bulk restore path to change the population without advancing it.
Version++;
_entityCount = value;
}
}

/// <summary>
/// A monotonically increasing version that changes whenever <see cref="Arch.Core.Entity"/>s enter or leave this <see cref="Archetype"/>.
/// If the value is unchanged between two observations, the set of <see cref="Arch.Core.Entity"/>s in this <see cref="Archetype"/> is unchanged.
/// This makes it a cheap structural-change signal for change detection over a query, without materialising or hashing the entities themselves.
/// </summary>
public long Version
{
get;
internal set;
private set;
}

/// <summary>
Expand Down