Search Unity

Can't iterate through DynamicBuffer since Unity 2020.3.16f1

Discussion in 'NetCode for ECS' started by DAKL, Nov 22, 2021.

  1. DAKL

    DAKL

    Joined:
    Sep 7, 2016
    Posts:
    3
    I am using Entities 0.11.2-preview.1 but I have tried it with 0.17.0-preview.42 as well.

    The following code works until 2020.3.15f2:

    Code (CSharp):
    1. static void SetAsPrefab(EntityManager entityManager, Entity entity)
    2. {
    3.     entityManager.AddComponentData(entity, new Prefab());
    4.     if (entityManager.HasComponent<Child>(entity))
    5.     {
    6.         var children = entityManager.GetBuffer<Child>(entity);
    7.         foreach (var child in children)
    8.         {
    9.             SetAsPrefab(entityManager, child.Value);
    10.         }
    11.     }
    12. }
    As soon as I upgrade to 2020.3.16f1 or later versions, this breaks with
    an ObjectDisposedException: (The NativeArray has been disposed, it is not allowed to access it.) when iterating.

    The only workaround I found to get it working again is the following:

    Code (CSharp):
    1. static void SetAsPrefab(EntityManager entityManager, Entity entity)
    2. {
    3.     entityManager.AddComponentData(entity, new Prefab());
    4.     if (entityManager.HasComponent<Child>(entity))
    5.     {
    6.         using var nativeArray = entityManager.GetBuffer<Child>(entity).AsNativeArray();
    7.         foreach (var child in nativeArray.ToArray())
    8.         {
    9.             SetAsPrefab(entityManager, child.Value);
    10.         }
    11.     }
    12. }
    So I guess it's a bug right?
    In my opinion it should work with the original code and I can't find anything in the documentation which would tell me how else I am supposed to access the child entities.
     
    Last edited: Nov 23, 2021
  2. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    I didn't knew you actually ever could iterate buffers. I've always used
    for(int i = 0 (...))
     
  3. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    What does SetAsPrefab look like? Does it make any structural changes? A structural change would invalidate your buffer.
     
  4. DAKL

    DAKL

    Joined:
    Sep 7, 2016
    Posts:
    3
    SetAsPrefab is the whole method I posted. It adds ComponentData "Prefab" recursively.
     
  5. timjohansson

    timjohansson

    Unity Technologies

    Joined:
    Jul 13, 2016
    Posts:
    473
    SetAsPrefab is calling EntityManager.AddComponentData - which is a structural change - while iterating over the buffer. Any structural change will invalidate the buffer so I would not expect it to work. I am surprised it did work in 20.3.15, the error you are getting has been the expected behavior for a long time.
     
    DAKL likes this.
  6. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    Ah my mistake I see now that it is recursive.

    Try something like this:
    Code (CSharp):
    1. static void SetAsPrefab(EntityManager entityManager, Entity entity)
    2. {
    3.     var commandBuffer = new EntityCommandBuffer(Allocator.TempJob);
    4.  
    5.     SetAsPrefabRecursive(entityManager, entity, commandBuffer);
    6.  
    7.     commandBuffer.Playback(entityManager);
    8.  
    9.     commandBuffer.Dispose();
    10. }
    11.  
    12. static void SetAsPrefabRecursive(EntityManager entityManager, Entity entity, EntityCommandBuffer commandBuffer)
    13. {
    14.     commandBuffer.AddComponent(entity, new Prefab());
    15.     if (entityManager.HasComponent<Child>(entity))
    16.     {
    17.         var children = entityManager.GetBuffer<Child>(entity);
    18.         foreach (var child in children)
    19.         {
    20.             SetAsPrefabRecursive(entityManager, child.Value, commandBuffer);
    21.         }
    22.     }
    23. }
     
  7. DAKL

    DAKL

    Joined:
    Sep 7, 2016
    Posts:
    3
    Thanks for the clarification. Now I see the problem as well and I am wondering why the Exception didn't occur in earlier versions of Unity.
    We were using this code since June 2020 and it happens on every startup of the game. It never was a problem until upgrading to 2020.3.16f1. So there were many versions of Unity with which this code worked.

    So I guess my solution is the best I could do here right?