Search Unity

What causes an entity version to change

Discussion in 'Entity Component System' started by Mogthew, Nov 14, 2020.

  1. Mogthew

    Mogthew

    Joined:
    Jan 30, 2013
    Posts:
    18
    Hey peeps, I'm seeing an issue in my game at the moment where an entities version is changing unexpectedly. I was under the impression that only the destruction of an entity would cause the a version change.

    What else causes it? Does it happen when an entity changes archetypes? If so, that kinda means references to entities that use the 'tagging' system are basically pointless...

    EDIT: I've just tested thrashing 10k entities around by removing and adding both tags and component data and the entity maintains stability.
     
    Last edited: Nov 14, 2020
  2. Mogthew

    Mogthew

    Joined:
    Jan 30, 2013
    Posts:
    18
    For anyone else reading this, the issue was being caused by calling EntityManager.SetArchetype().

    It seems that adding/removing elements from an entity does not cause entity references to become stale(wrong version), but resetting the archetype does, even if both operations have the exact same outcome.

    Is this expected behaviour?
     
  3. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    EntityManager.SetArchetype or any other structural changes invalidating the entity ID is not the expected behaviour. We do have a ton of tests that validate that it's not for SetArchetype and other structural changes

    Can you write some specific code where this happens & post it here.
     
  4. Mogthew

    Mogthew

    Joined:
    Jan 30, 2013
    Posts:
    18
    Here's the minimum repro.

    Note that you get 2 bugs for the price of one here too!

    Firstly, the .SetArchetype consistently throws an Assertion error:
    EDIT: This appears to be caused by setting the same archetype twice

    AssertionException: Assertion failure. Value was True
    Expected: False
    UnityEngine.Assertions.Assert.Fail (System.String message, System.String userMessage) (at <96fe0edd95c741b291e67722b91bd84f>:0)
    UnityEngine.Assertions.Assert.IsFalse (System.Boolean condition, System.String message) (at <96fe0edd95c741b291e67722b91bd84f>:0)
    UnityEngine.Assertions.Assert.IsFalse (System.Boolean condition) (at <96fe0edd95c741b291e67722b91bd84f>:0)
    Unity.Assertions.Assert.IsFalse (System.Boolean condition) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/Stubs/Unity.Assertions/Assert.cs:33)
    Unity.Entities.ChunkDataUtility.Convert (Unity.Entities.Chunk* srcChunk, System.Int32 srcIndex, Unity.Entities.Chunk* dstChunk, System.Int32 dstIndex, System.Int32 count) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ChunkDataUtility.cs:293)
    Unity.Entities.ChunkDataUtility.Clone (Unity.Entities.EntityBatchInChunk& srcBatch, Unity.Entities.Chunk* dstChunk) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ChunkDataUtility.cs:868)
    Unity.Entities.EntityComponentStore.Move (Unity.Entities.EntityBatchInChunk& srcBatch, Unity.Entities.Chunk* dstChunk) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityComponentStoreChangeArchetype.cs:391)
    Unity.Entities.EntityComponentStore.Move (Unity.Entities.EntityBatchInChunk entityBatchInChunk, Unity.Entities.EntityComponentStore+ArchetypeChunkFilter& archetypeChunkFilter) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityComponentStoreChangeArchetype.cs:362)
    Unity.Entities.EntityComponentStore.Move (Unity.Entities.Entity entity, Unity.Entities.EntityComponentStore+ArchetypeChunkFilter& archetypeChunkFilter) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityComponentStoreChangeArchetype.cs:320)
    Unity.Entities.EntityComponentStore.Move (Unity.Entities.Entity entity, Unity.Entities.Archetype* dstArchetype) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityComponentStoreChangeArchetype.cs:291)
    Unity.Entities.StructuralChange._MoveEntityArchetype (Unity.Entities.EntityComponentStore* entityComponentStore, Unity.Entities.Entity* entity, System.Void* dstArchetype) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityManagerChangeArchetype.cs:77)
    Unity.Entities.StructuralChange._mono_to_burst_MoveEntityArchetype (System.IntPtr entityComponentStore, System.IntPtr entity, System.IntPtr dstArchetype) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityManagerChangeArchetype.interop.gen.cs:372)
    Unity.Entities.StructuralChange._forward_mono_MoveEntityArchetype (Unity.Entities.EntityComponentStore* entityComponentStore, Unity.Entities.Entity* entity, System.Void* dstArchetype) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityManagerChangeArchetype.interop.gen.cs:378)
    Unity.Entities.StructuralChange.MoveEntityArchetype (Unity.Entities.EntityComponentStore* entityComponentStore, Unity.Entities.Entity* entity, System.Void* dstArchetype) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityManagerChangeArchetype.interop.gen.cs:359)
    Unity.Entities.EntityManager.SetArchetype (Unity.Entities.Entity entity, Unity.Entities.EntityArchetype archetype) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityManagerChangeArchetype.cs:989)
    EntityStalenatorSystem.OnUpdate (Unity.Jobs.JobHandle inputDeps) (at Assets/Scrips/ECS/EntityStalenatorSystem.cs:35)


    And secondly, the entity version changes (and throws an exception).

    Here's the code

    Code (CSharp):
    1.  
    2. using System;
    3. using Unity.Collections;
    4. using Unity.Entities;
    5. using Unity.Jobs;
    6.  
    7. [AlwaysUpdateSystem]
    8. public class EntityStalenatorSystem : JobComponentSystem {
    9.     private EntityArchetype archetype;
    10.     private EntityArchetype archetype2;
    11.  
    12.     protected override void OnCreate() {
    13.         for (int x = 0; x < 1000; x++)
    14.         {
    15.             archetype = EntityManager.CreateArchetype(typeof(Component1), typeof(Component2));
    16.             archetype2 = EntityManager.CreateArchetype(typeof(Component1));
    17.             EntityManager.CreateEntity(archetype);
    18.         }
    19.     }
    20.  
    21.     protected override JobHandle OnUpdate(JobHandle inputDeps) {
    22.         NativeArray<Entity> all = EntityManager.GetAllEntities();
    23.  
    24.         /*
    25.          * Pick a random entity from all entities, that has the component we're looking for
    26.          */
    27.         Entity random = all[new Random().Next(all.Length)];
    28.         while (!EntityManager.HasComponent<Component1>(random))
    29.         {
    30.             random = all[new Random().Next(all.Length)];
    31.         }
    32.  
    33.         /*
    34.          * Update it's archetype
    35.          */
    36.         EntityManager.SetArchetype(random, archetype2);
    37.  
    38.         /*
    39.          * Apply the assertion
    40.          */
    41.         return Entities.ForEach((Entity entity, ref Component1 component) =>
    42.         {
    43.             if (entity.Version != 1)
    44.             {
    45.                 throw new Exception("This is where the fun begins");
    46.             }
    47.         }).Schedule(inputDeps);
    48.     }
    49.  
    50.     private struct Component1 : IComponentData {
    51.         public int something;
    52.     }
    53.  
    54.     private struct Component2 : IComponentData {
    55.         public int somethingElse;
    56.     }
    57. }
    58.  
    P.S. Thanks for the amazing work you guys are doing in this space. It's allowing me build a game with the scale I've always wanted, but never had the tech to do :)
     
    Last edited: Nov 14, 2020
  5. Mogthew

    Mogthew

    Joined:
    Jan 30, 2013
    Posts:
    18
    There also seems to be an issue related to .RemoveAtSwapBack on the dynamic buffer. I can consistently cause it to duplicate elements, whereas a manual swap and remove at end element doesn't.

    This code in my game

    Code (CSharp):
    1.  
    2. for (int i = 0; i < entities.Length; i++)
    3. {
    4.     for (int y = i + 1; y<entities.Length;
    5.         y++)
    6.     {
    7.         if (entities[i].entity == entities[y].entity)
    8.         {
    9.             throw new Exception("Duplicate entity" + i);
    10.         }
    11.     }
    12. }
    13.  
    14. entities.RemoveAtSwapBack(x);
    15.  
    16. for (int i = 0; i < entities.Length; i++)
    17. {
    18.     for (int y = i + 1; y<entities.Length;
    19.         y++)
    20.     {
    21.         if (entities[i].entity == entities[y].entity)
    22.         {
    23.             throw new Exception("Duplicate entity" + i);
    24.         }
    25.     }
    26. }
    27.  
    Will eventually throw an exception in the second duplicate detection loop only. I'm currently trying to make a minimum repro.

    EDIT: I did post the code to reproduce the other issue before this, but the post is currently awaiting moderator approval for some reason
     
  6. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    https://forum.unity.com/threads/dynamicbuffer-removeatswapback-copy-only-byte-size-data.983163/
     
  7. Mogthew

    Mogthew

    Joined:
    Jan 30, 2013
    Posts:
    18
    Were you able to see the message I posted yet? It still says it's awaiting moderator approval -_-

    Good find! I'll just keep doing the manual swap and remove for now until that fix is released