Search Unity

Question working rpc in pre.15 now gets a nested native container error in pre.47

Discussion in 'NetCode for ECS' started by twn9009, Mar 14, 2023.

  1. twn9009

    twn9009

    Joined:
    May 2, 2019
    Posts:
    58
    Code (CSharp):
    1.  
    2. [BurstCompile]
    3. public struct TestRPC : IComponentData, IRpcCommandSerializer<TestRPC>
    4. {
    5.    
    6.    
    7.     public NativeHashMap<FixedString64Bytes, int> items;
    8.  
    9.  
    10.  
    11.  
    12.     [BurstCompile(DisableDirectCall = true)]
    13.     private static void InvokeExecute(ref RpcExecutor.Parameters parameters)
    14.     {
    15.         RpcExecutor.ExecuteCreateRequestComponent<TestRPC, TestRPC>(ref parameters);
    16.     }
    17.  
    18.     public void Serialize(ref DataStreamWriter writer, in RpcSerializerState state, in TestRPC data)
    19.     {
    20.        
    21.         writer.WriteInt(data.items.Count);
    22.  
    23.         foreach (var item in data.items)
    24.         {
    25.            
    26.             writer.WriteFixedString64(item.Key);
    27.            
    28.             writer.WriteInt(item.Value);
    29.          
    30.         }
    31.         Debug.Log("SERIALIZED TEST RPC");
    32.     }
    33.  
    34.     public void Deserialize(ref DataStreamReader reader, in RpcDeserializerState state, ref TestRPC data)
    35.     {
    36.        
    37.         int count = reader.ReadInt();
    38.         NativeHashMap<FixedString64Bytes, int> items = new NativeHashMap<FixedString64Bytes, int>(count,Allocator.Persistent);
    39.         for (int i = 0; i < count; i++)
    40.         {
    41.            
    42.             items.Add(reader.ReadFixedString64(), reader.ReadInt());
    43.         }
    44.         data.items = items;
    45.        
    46.         Debug.Log("DESERIALIZED TEST RPC");
    47.     }
    48.  
    49.     public PortableFunctionPointer<RpcExecutor.ExecuteDelegate> CompileExecute()
    50.     {
    51.        
    52.         return InvokeExecuteFunctionPointer;
    53.     }
    54.  
    55.     static readonly PortableFunctionPointer<RpcExecutor.ExecuteDelegate> InvokeExecuteFunctionPointer = new PortableFunctionPointer<RpcExecutor.ExecuteDelegate>(InvokeExecute);
    56. }
    57.  
    58.  
    59.  
    60. [UpdateInGroup(typeof(RpcCommandRequestSystemGroup))]
    61. [CreateAfter(typeof(RpcSystem))]
    62. [BurstCompile]
    63. partial struct TestRpcCommandRequestSystem : ISystem
    64. {
    65.     RpcCommandRequest<TestRPC, TestRPC> m_Request;
    66.     [BurstCompile]
    67.     struct SendRpc : IJobChunk
    68.     {
    69.         public RpcCommandRequest<TestRPC, TestRPC>.SendRpcData data;
    70.         public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
    71.         {
    72.             Assert.IsFalse(useEnabledMask);
    73.             data.Execute(chunk, unfilteredChunkIndex);
    74.         }
    75.     }
    76.     public void OnCreate(ref SystemState state)
    77.     {
    78.         m_Request.OnCreate(ref state);
    79.     }
    80.     [BurstCompile]
    81.     public void OnUpdate(ref SystemState state)
    82.     {
    83.         var sendJob = new SendRpc { data = m_Request.InitJobData(ref state) };
    84.         state.Dependency = sendJob.Schedule(m_Request.Query, state.Dependency);
    85.     }
    86.  
    87.     public void OnDestroy(ref SystemState state)
    88.     {
    89.        
    90.     }
    91. }
    the above code works fine in 1.0.0-pre.15 but when trying to send the same rpc in pre.47 i get a nested native container error when i cant see a nested container at all, i didnt think nested native containers were ever allowed so not sure why it would've worked in the previous version, any ideas on how to fix this? i can use a workaround by sending an rpc for each item in the list but it just feels wrong

    InvalidOperationException: The ComponentTypeHandle<TestRPC> SendRpc.JobData.data.actionRequestType can not be accessed. Nested native containers are illegal in jobs.
    Unity.Entities.WorldUnmanagedImpl+UnmanagedUpdate_0000152E$BurstDirectCall.Invoke (System.Void* pSystemState) (at <e9af11ee39d34420b82f09345b533528>:0)
    Unity.Entities.WorldUnmanagedImpl.UnmanagedUpdate (System.Void* pSystemState) (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/WorldUnmanaged.cs:810)
    Unity.Entities.WorldUnmanagedImpl.UpdateSystem (Unity.Entities.SystemHandle sh) (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/WorldUnmanaged.cs:895)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/ComponentSystemGroup.cs:697)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/ComponentSystemGroup.cs:708)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/ComponentSystemGroup.cs:661)
    Unity.NetCode.RpcCommandRequestSystemGroup:OnUpdate() (at ./Library/PackageCache/com.unity.netcode@1.0.0-pre.47/Runtime/Rpc/RpcCommandRequest.cs:84)
    Unity.Entities.SystemBase:Update() (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/SystemBase.cs:418)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/ComponentSystemGroup.cs:703)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/ComponentSystemGroup.cs:667)
    Unity.Entities.SystemBase:Update() (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/SystemBase.cs:418)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at ./Library/PackageCache/com.unity.entities@1.0.0-pre.47/Unity.Entities/ScriptBehaviourUpdateOrder.cs:526)
     
  2. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    900
    Using NativeContainer as a field in a component is supported but you can pass that component to a Job.
    Entities added lots of check to guarantee that this pattern cannot be used. The reason is all around the fact safety handles cannot be collected and checked correctly in all cases.

    The SendRpc.JobData.data.actionRequestType is a ComponentTypeHandle that is by per se a NativeContainer. So this become a nested container pattern.

    You can work around that by either:
    - Use UnsafeHashmap instead.
    - Try adding a [NativeDisableContainerSafetyRestriction] to the field
    Code (csharp):
    1.  
    2. [NativeDisableContainerSafetyRestriction]
    3. public NativeHashMap<FixedString64Bytes, int> items;
    4.  
    The latter will not track race conditions or access to the map at the same time the RPC job is running. So you need to use another way to track the dependency.
     
  3. twn9009

    twn9009

    Joined:
    May 2, 2019
    Posts:
    58
    thanks for the explanation, unsafehashmap did the trick
     
  4. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    900
    Another alternative is to use a FixedContainer as a field and do the "serialization/copy" to it when constructing the RPC. RPC cannot be larger than 1 MTU anyway (excluded headers etc, so more toward 1400 bytes).
    So looping over all the hash key/value works only if the key value pair fit.
    A possibility is also something like:
    Code (csharp):
    1.  
    2. unsafe
    3. {
    4.     FixedString4096Bytes data;
    5.     var streamWriter = new DataStreamWriter(data.GetUnsafePtr(), 1400);
    6.     SerializeHashmap(streamWriter);
    7.     streamWriter.Flush();
    8.     data.Length = streamWriter.Length;
    9.     var e = EntityManager.CreateEntity(
    10.         typeof(MyRPC),
    11.         typeof(SendRpcCommandRequestComponent));
    12.     EntityManager.SetComponentData(e, new RPC
    13.     {
    14.         value = data
    15.     });
    16. }
    17.  
    That is slightly sub-optimal, but do the job too
     
  5. twn9009

    twn9009

    Joined:
    May 2, 2019
    Posts:
    58
    That's so much cleaner than what I was doing thanks for the tip