Search Unity

Resolved ArgumentException: The entity does not exist from EntityCommandBuffer

Discussion in 'Entity Component System' started by Lieene-Guo, Sep 30, 2020.

  1. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    System A decides to Destroy Entity e, System B decides to add a component to Entity e. They all happen in the same frame and using the same EntityCommandBufferSystem.
    Then I got this Exception.

    It looks like a common case in games. Target killed will some other one is trying to save him, well not so common when these two things happen in the same frame, but It happens.

    Should This be considered an Exception?
    Will this exception block further Commands in the buffer?
     
  2. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Tried add component in EndSimulationEntityCommandBufferSystem
    And destroy Entity in BeginPresentationEntityCommandBufferSystem
    Still got this exception.
     
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    EntityCommandBuffer only handles this case if commands in the same EntityCommandBuffer (do not confuse with the ECB system), in this case, there is no error as playback resolve this. But for different ECBSystems or different ECB's from different or even from the same system, does not resolve this.
    For example - no error on adding Dead after destroy as commands in the same EntityCommandBuffer and Playback resolve this as structural changes in progress while buffer playbacks and not "closed" yet (by ProcessTrackedChanges which is applied real changes in the end of playback)
    Code (CSharp):
    1.             var b1 = new EntityCommandBuffer(Allocator.TempJob);
    2.             var e  = b1.CreateEntity();
    3.             b1.AddComponent<InBuild>(e);
    4.             b1.DestroyEntity(e);
    5.             b1.AddComponent<Dead>(e);
    6.             b1.Playback(World.DefaultGameObjectInjectionWorld.EntityManager);
    7.             b1.Dispose();
    Error as another buffer already destroyed entity and in this case b1 Playback actually destroyed entity AND triggered structural change end and b2 Playback handles this with error like if you just trying to add component to not existed entity:
    Code (CSharp):
    1.             var b1 = new EntityCommandBuffer(Allocator.TempJob);
    2.             var b2 = new EntityCommandBuffer(Allocator.TempJob);
    3.          
    4.             e = b2.CreateEntity();
    5.             b2.AddComponent<InBuild>(e);
    6.             b1.DestroyEntity(e);
    7.  
    8.             b1.Playback(World.DefaultGameObjectInjectionWorld.EntityManager);
    9.             b2.Playback(World.DefaultGameObjectInjectionWorld.EntityManager);
    10.          
    11.             b1.Dispose();
    12.             b2.Dispose();
     
    Last edited: Sep 30, 2020
  4. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    I understand that.
    By using a second ECBS I am just hoping an extra sync point could solve the problem.
    As EndSimulationEntityCommandBufferSystem should run before BeginPresentationEntityCommandBufferSystem
    So It the component should be added to the Entity.
    And then the Entity gets destroyed. But It is weirdly not working...
     
  5. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Also, As I read though ECB source code. It could just report a warning and go on with the next command.
    But on the contrary, It through an expectation and stop working.
    These kinds of cases could be common. ECB playback should not be stoped by that.
     
  6. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    It will probably even hard crash without safety checks.
    Checking each entry for validity in a system that is made to handle millions of entities is far to expensive.

    My first take would be to instead create events in some form and let a separate system resolve these events. Then it can handle this conflict explicitly. We don't use ECS for most game logic yet and I'm interested in how things like this should be structured too.

    Your solution with the different sync points should work. Is the system filling the ECB maybe running after EndSimulationEntityCommandBufferSystem as this would postpone the AddComponent until the next frame?
     
    Lieene-Guo likes this.
  7. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    I understand that too.
    But in a real-life functional fully-featured game. you just can not expect every system "that" perfectly aligned with the others.
    I can pass the used ECB from the first system to my second system. It's fine. and the exception would be gone.
    But It's not how game coding goes.
    It's just not an exception.
     
  8. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    It's not a could crash or not case, I can not think of a case that is worse than throwing an exception at this condition.
    It returns false if the entity already has the component.
    but throw exception if the entity dose not exist.
    I could be just a return false.....

    It is a bug IMO...


    Code (CSharp):
    1.                 case ECBCommand.AddComponent:
    2.                 {
    3.                     var cmd = (EntityComponentCommand*)header;
    4.                     var componentType = ComponentType.FromTypeIndex(cmd->ComponentTypeIndex);
    5.                     var entity = SelectEntity(cmd->Header.Entity, playbackState);
    6.                     mgr->EntityComponentStore->AddComponentWithValidation(entity, componentType);
    7.                     if (cmd->ComponentSize != 0)
    8.                         mgr->EntityComponentStore->SetComponentDataRawEntityHasComponent(entity, cmd->ComponentTypeIndex, cmd + 1,
    9.                             cmd->ComponentSize);
    10.                 }


    Code (CSharp):
    1.         public bool AddComponentWithValidation(Entity entity, ComponentType componentType)
    2.         {
    3.             if (HasComponent(entity, componentType))
    4.                 return false;
    5.  
    6.             AssertCanAddComponent(entity, componentType);
    7.             AddComponent(entity, componentType);
    8.  
    9.             return true;
    10.         }

    Code (CSharp):
    1.         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
    2.         public void AssertCanAddComponent(Entity entity, ComponentType componentType)
    3.         {
    4.             if (!Exists(entity))
    5.                 throw new InvalidOperationException("The entity does not exist");
    6.  
    7.             AssertCanAddComponent(GetArchetype(entity), componentType);
    8.         }
     
    Last edited: Sep 30, 2020
  9. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Use 2 ECBS solves the problem. I have an event system that delayed the add component command to the next frame. that's why it is not working on the first try.
    Looks like I need a late ECBS dedicated to Entity Destructions.
    But still why not just return false and keep on with the commands.
     
    toomasio and manpower13 like this.
  10. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    Sorry for reviving an older thread but I'm having a heck of a time tracking down whatever it is I'm doing to cause this. Any tips on how to debug this? My project is pretty large with a lot of entities being created and destroyed, I can't easily narrow down which specific piece of code is causing the error from nothing but this delightful message "The entity does not exist". Can the error message at least tell me what component is attempting to be added/accessed?

    Maybe I'm just missing something obvious but how can I narrow it down if I have no way to check if the entity exists from the command buffer?
     
  11. Klusimo

    Klusimo

    Joined:
    May 7, 2019
    Posts:
    76
    Try to deactivate codes until you reach it. If you are worried, you can copy your project as separate one for testing this.
     
  12. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    ECB debugging not so simple for now, but Unity working on improving this right now. Until that you should disable burst if you want to catch this specific error. Then it will point you to place where it was thrown (Or put breakpoints manually to
    AssertEntityHasComponent
    and
    AssertCanAddComponent
    in
    EntityComponentStoreDebug
    , then on a breakpoint you can trace back which command was executed on which component and which system recorded that command, but not directly, every ECB command has SystemID number which is the same as m_SystemID of SystemBase assigned on internal system initialization just get systems list through Evaluate Expression and find your system by this SystemID.
     
    BigRookGames and Sarkahn like this.