Search Unity

Bug 1.0.0-pre.65 NetworkStreamConnectSystem throws exception due to creating entities while iterating

Discussion in 'NetCode for ECS' started by PolarTron, Mar 27, 2023.

  1. PolarTron

    PolarTron

    Joined:
    Jun 21, 2013
    Posts:
    94
    Just upgraded my packages and now connecting using NetworkStreamRequestConnect doesn't work.
    I've highlighted the issues in the code below.

    The problem is the use of networkStreamDriver.Connect, which makes structural changes, while iterating over entities.

    Code (CSharp):
    1. InvalidOperationException: System.InvalidOperationException: Structural changes are not allowed while iterating over entities. Please use EntityCommandBuffer instead.
    2. This Exception was thrown from a function compiled with Burst, which has limited exception support.
    Code (CSharp):
    1.         [BurstCompile]
    2.         public void OnUpdate(ref SystemState systemState)
    3.         {
    4.             var netDebug = SystemAPI.GetSingleton<NetDebug>();
    5.             ref var networkStreamDriver = ref SystemAPI.GetSingletonRW<NetworkStreamDriver>().ValueRW;
    6.             m_ConnectionStateFromEntity.Update(ref systemState);
    7.             var stateFromEntity = m_ConnectionStateFromEntity;
    8.  
    9.             foreach(var (request, ent) in SystemAPI.Query<RefRO<NetworkStreamRequestConnect>>().WithEntityAccess())
    10.             {
    11.                 var endpoint = request.ValueRO.Endpoint;
    12. ->              var connection = networkStreamDriver.Connect(systemState.EntityManager, endpoint, ent);
    13.                 if(connection == Entity.Null)
    14.                 {
    15.                     if (stateFromEntity.HasComponent(ent))
    16.                     {
    17.                         var state = stateFromEntity[ent];
    18.                         state.DisconnectReason = NetworkStreamDisconnectReason.ConnectionClose;
    19.                         state.CurrentState = ConnectionState.State.Disconnected;
    20.                         stateFromEntity[ent] = state;
    21.                     }
    22.                     systemState.EntityManager.DestroyEntity(ent);
    23.                     netDebug.DebugLog("Connect request failed.");
    24.                 }
    25.             }
    26.             systemState.EntityManager.RemoveComponent<NetworkStreamRequestConnect>(m_ConnectionRequestConnectQuery);
    27.         }
    28.     }
    Code (CSharp):
    1.    
    2. public Entity Connect(EntityManager entityManager, NetworkEndpoint endpoint, Entity ent = default)
    3.         {
    4.             //Still storing the last connecting andpoint as it passed
    5.             LastEndPoint = endpoint;
    6.  
    7.             if (ent == Entity.Null)
    8.                 ent = entityManager.CreateEntity();
    9.  
    10. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    11.             if (DriverStore.DriversCount == 0)
    12.                 throw new InvalidOperationException("Cannot connect to the server. NetworkDriver not created");
    13.             if (DriverStore.DriversCount != 1)
    14.                 throw new InvalidOperationException("Too many NetworkDriver created for the client. Only one NetworkDriver instance should exist");
    15.             var builder = new EntityQueryBuilder(Allocator.Temp).WithAll<NetworkSnapshotAck>();
    16.             if (!entityManager.CreateEntityQuery(builder).IsEmpty)
    17.                 throw new InvalidOperationException("Connection to server already initiated, only one connection allowed at a time.");
    18. #endif
    19.             endpoint = SanitizeConnectAddress(endpoint, DriverStore.FirstDriver);
    20.             var connection = DriverStore.GetNetworkDriver(NetworkDriverStore.FirstDriverId).Connect(endpoint);
    21.  ->         entityManager.AddComponentData(ent, new NetworkStreamConnection{Value = connection, DriverId = 1});
    22.             entityManager.AddComponentData(ent, new NetworkSnapshotAck());
    23.             entityManager.AddComponentData(ent, new CommandTarget());
    24.             entityManager.AddBuffer<IncomingRpcDataStreamBuffer>(ent);
    25.             entityManager.AddBuffer<OutgoingCommandDataStreamBuffer>(ent);
    26.             entityManager.AddBuffer<IncomingSnapshotDataStreamBuffer>(ent);
    27.             entityManager.AddBuffer<LinkedEntityGroup>(ent).Add(new LinkedEntityGroup{Value = ent});
    28.             return ent;
    29.         }
     
    BackgroundMover likes this.
  2. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    900
    Thanks for reporting. We will address that. Can you please open a case for it?
     
  3. DmitryMatveev

    DmitryMatveev

    Joined:
    May 9, 2013
    Posts:
    4
  4. NikiWalker

    NikiWalker

    Unity Technologies

    Joined:
    May 18, 2021
    Posts:
    316
    Thanks for the case report, digging into it now.
     
  5. NikiWalker

    NikiWalker

    Unity Technologies

    Joined:
    May 18, 2021
    Posts:
    316
    The fix for this has landed, and will be in an upcoming patch release after 1.0.8. As a workaround, replace your NetworkStreamRequestConnect with a direct Connect call:
    Code (CSharp):
    1.     using var driverQuery = client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite<NetworkStreamDriver>());
    2.     driverQuery.GetSingletonRW<NetworkStreamDriver>().ValueRW.Connect(client.EntityManager, endpoint);
     
    PolarTron, DmitryMatveev and Kmsxkuse like this.