diff --git a/src/Arch.Tests/ArchetypeTest.cs b/src/Arch.Tests/ArchetypeTest.cs index bfea697b..8cf2bee5 100644 --- a/src/Arch.Tests/ArchetypeTest.cs +++ b/src/Arch.Tests/ArchetypeTest.cs @@ -213,6 +213,37 @@ public void Move() That(otherArchetype.Get(ref newSlot).Y, Is.EqualTo(10)); } + /// + /// Checks that advances whenever s enter or leave, + /// including a same-count remove-then-add swap where ends unchanged. + /// + [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)); + } + /// /// Checks if a copy operation between was successful. /// This is checked by value equality of the items and their correct order. diff --git a/src/Arch/Core/Archetype.cs b/src/Arch/Core/Archetype.cs index a06537a3..a81281eb 100644 --- a/src/Arch/Core/Archetype.cs +++ b/src/Arch/Core/Archetype.cs @@ -381,10 +381,31 @@ internal Slot CurrentSlot /// /// The number of s in this . /// + 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; + } + } + + /// + /// A monotonically increasing version that changes whenever s enter or leave this . + /// If the value is unchanged between two observations, the set of s in this is unchanged. + /// This makes it a cheap structural-change signal for change detection over a query, without materialising or hashing the entities themselves. + /// + public long Version { get; - internal set; + private set; } ///