Search Unity

Resolved "ClientRpc - Don't know how to serialize String[]"

Discussion in 'Netcode for GameObjects' started by GuirieSanchez, Nov 15, 2022.

  1. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    upload_2022-11-15_13-47-9.png

    I'm trying to pass an array instead of a single parameter to a [ClientRpc], but to no avail. Passing in parameters have worked fine, but apparently a Network Serializer is needed if you want to pass in an array of parameters (in my case, strings).

    here's my (non-working) code:
    Code (CSharp):
    1. //this function calls the "Client Rpc just fine"
    2. [ServerRpc(RequireOwnership = false)]
    3.         private void ProvideName_ServerRpc(int index, string name)
    4.         {
    5.                 string[] playersName = new string[numberOfPlayers];
    6.  
    7.                 for (int i = 0; i < playersName.Length; i++)
    8.                 {
    9.                     playersName[i] = playerNameArray[i];
    10.                 }
    11.  
    12.                 UpdatePlayersName_ClientRpc(playersName);
    13.         }
    and this one is the one that throws the error:
    Code (CSharp):
    1. [ClientRpc]
    2.         private void UpdatePlayersName_ClientRpc(string[] playersName)
    3.         {
    4.             for (int i = 0; i < playersName.Length; i++) //this line is referenced in the error
    5.             {
    6.                 playerNameArray[i] = playersName[i];
    7.             }
    8.  
    9.             DoneUpdatingPlayersName_ServerRpc();
    10.         }

    I was having a look at the documentation (here), where it says that one could implement an interface to define custom serializable types.

    I tried to use these kinds of structure:

    upload_2022-11-15_13-51-4.png

    but with no success: There was no definition for the NetworkSerializer. When I tried to solve the problem by pressing "Alt - Enter", it created a public class

    Code (CSharp):
    1. public class NetworkSerializer
    2.     {
    3.         internal void Serialize(ref string[] playersName)
    4.         {
    5.             throw new NotImplementedException();
    6.         }
    7.     }
    for the error to disappear, but apparently does nothing.
    Also, it says that the
    serializer
    does not contain definitions for "length", "IsReading", etc., rendering this piece of code to be completely useless.

    Probably not a big deal for an experienced programmer, but for me (who I have no idea about serializers) this is driving me nuts. All I want is to pass in an array of strings, nothing crazy. I would appreciate any detailed explanation, or even more so, a working example.

    Thank you.
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,975
    Implementing INetworkSerializable is the way to go for any managed type.

    You probably didn't add using Netcode in that script:
    Code (CSharp):
    1. using Unity.Netcode;
    This compiles for me:
    Code (CSharp):
    1. using Unity.Netcode;
    2.  
    3. public struct NetworkStringArray : INetworkSerializable
    4. {
    5.     public string[] Array;
    6.  
    7.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    8.     {
    9.         var length = 0;
    10.         if (!serializer.IsReader)
    11.             length = Array.Length;
    12.  
    13.         serializer.SerializeValue(ref length);
    14.  
    15.         if (serializer.IsReader)
    16.             Array = new string[length];
    17.  
    18.         for (var n = 0; n < length; ++n)
    19.             serializer.SerializeValue(ref Array[n]);
    20.     }
    21. }
     
  3. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    I am using it. It's needed for communicating RPCs, isn't it.

    This is the docs's version:
    upload_2022-11-15_14-53-36.png

    ---
    upload_2022-11-15_14-55-29.png

    ---
    upload_2022-11-15_14-55-42.png

    As you can see, the docs' version is not compiling. Your version however does:

    upload_2022-11-15_14-56-13.png

    I noticed that you use a BufferSerializer Type (which I don't know the difference between them), instead of a NetworkSerializer from the docs.
     
  4. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Now that I have to use structs to pass in the array, could you tell me how could I do so? I have no clue how to assign the values.
    here I was using this:

    Code (CSharp):
    1. [ServerRpc(RequireOwnership = false)]
    2.         private void ProvideName_ServerRpc(int index, string name)
    3.         {
    4.                 string[] playersName = new string[numberOfPlayers];
    5.                 for (int i = 0; i < playersName.Length; i++)
    6.                 {
    7.                     playersName[i] = playerNameArray[i];
    8.                 }
    9.                 UpdatePlayersName_ClientRpc(playersName);
    10.         }
    to set up the string array, but now I'm supposed to do this in the struct and I have no idea how to proceed.
     
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,975
    GuirieSanchez likes this.
  6. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Thank you, I didn't know I was looking at an outdated version.


    Could anyone give a hand on this? I would highly appreciate it
     
  7. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Ok, I think I managed. I'll leave it here for anyone who may be struggling with this:

    I assign the values to the array, as one would do normally:
    Code (CSharp):
    1.  
    2. string[] playersName = new string[numberOfPlayers];
    3.  
    4.                 for (int i = 0; i < playersName.Length; i++)
    5.                 {
    6.                     playersName[i] = playerNameArray[i];
    7.                 }
    8.  


    Then, I used CodeSmile's version of the INetworkSerializable:
    Code (CSharp):
    1.  
    2. public struct NetworkStringArrayStruct: INetworkSerializable
    3.     {
    4.         public string[] Array;
    5.  
    6.         public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    7.         {
    8.             var length = 0;
    9.             if (!serializer.IsReader)
    10.                 length = Array.Length;
    11.  
    12.             serializer.SerializeValue(ref length);
    13.  
    14.             if (serializer.IsReader)
    15.                 Array = new string[length];
    16.  
    17.             for (var n = 0; n < length; ++n)
    18.                 serializer.SerializeValue(ref Array[n]);
    19.         }
    20.     }
    21.  

    Now, in order to serialize the struct with your custom array, I did this:
    Code (CSharp):
    1.  
    2. UpdatePlayersName_ClientRpc(new NetworkStringArrayStruct { Array = playersName });
    3.  

    This way, you're providing some values on the struct to be read and written.


    Then, on the other function, for instance, you would use that referenced array like this:
    Code (CSharp):
    1.  
    2. [ClientRpc]
    3. private void UpdatePlayersName_ClientRpc(NetworkStringArrayStruct structArray)
    4.         {
    5.             for (int i = 0; i < structArray.Array.Length; i++)
    6.             {
    7.                 playerNameArray[i] = structArray.Array[i];
    8.             }
    9.  
    10.             // Do whatever, in my case:
    11.             playersReady += 1;
    12.             DoneUpdatingPlayersName_ServerRpc();
    13.         }
    14.  
     
    Last edited: Nov 15, 2022