Search Unity

Question [MLAPI NetworkVariable<MyClass>] how do you use classes with NetworkVariable<> ?

Discussion in 'Netcode for GameObjects' started by Qazuro, Sep 22, 2021.

  1. Qazuro

    Qazuro

    Joined:
    Aug 4, 2020
    Posts:
    9
    I'm trying to network a variable which is a class "Player" for an online quiz game the class looks like this:

    Code (CSharp):
    1.  
    2. [System.Serializable]
    3. public class Player
    4. {
    5.     public string name;
    6.     public int scoreToAdd;
    7.     public int scoreTotal;
    8.     public List<int> indexesOfGuesses;
    9.     public Commenter playerChatConnection;
    10.     public PlayerType playerType;
    11.     public Player()
    12.     {
    13.         name = "";
    14.         scoreTotal = 0;
    15.         scoreToAdd = 0;
    16.         playerChatConnection = null;
    17.         indexesOfGuesses = new List<int>();
    18.     }
    19. }
    20.  
    21. public enum PlayerType {twitchPlayer, guestPlayer};
    Commenter is just a string string and a number I use to also hook this up to a twitch chat (this part actually works and was surprisingly easy lol)

    I am declaring the networked variable like this:

    Code (CSharp):
    1.  
    2. public NetworkVariable<Player> quizPlayer = new NetworkVariable<Player>();
    and then assigning it like this:


    Code (CSharp):
    1. [ServerRpc]
    2.     public void AssignInitialValuesServerRpc(byte[] playerByte)
    3.     {
    4.         string playerString = Encoding.ASCII.GetString(playerByte);
    5.         var player = JsonUtility.FromJson<Player>(playerString);
    6.         quizPlayer.Value = player;
    7.     }
    However, The error message didn't stop when I stopped calling the server Rpc and just had a network variable all on it's own and only stopped when even the declaration of the networked variable was removed. What am I doing wrong and can you even network custom classes?
     
  2. luke-unity

    luke-unity

    Joined:
    Sep 30, 2020
    Posts:
    306
  3. Qazuro

    Qazuro

    Joined:
    Aug 4, 2020
    Posts:
    9
    I'm very sorry, I can't remember the specific message I was getting but this mostly worked, I also had to change a few other things in the class, like I to change the list indexesOfGuesses, to an array. Commenter had to be converted to a string and I'm going to use a dictionary of key: string, value: commenter or find something less spegatti. Thank you for the quick response though
     
  4. Qazuro

    Qazuro

    Joined:
    Aug 4, 2020
    Posts:
    9
    For the unlucky student that finds this a year down the line here's what I ended up doing:
    I made this new class which consolidates my other classes together and has not failed me yet in what I'm trying to do:

    Code (CSharp):
    1. [System.Serializable]
    2. public class NetPlayer : INetworkSerializable
    3. {
    4.     public string name;
    5.     public int scoreToAdd;
    6.     public int scoreTotal;
    7.     public int[] indexesOfGuesses;
    8.     public string playerChatConnectionTwitchName;
    9.     public int[] ghostPartsIndexs;
    10.     public PlayerType playerType;
    11.    
    12.     public NetPlayer()
    13.     {
    14.         name = "";
    15.         scoreTotal = 0;
    16.         scoreToAdd = 0;
    17.         playerChatConnectionTwitchName = null;
    18.         indexesOfGuesses = new int[8];
    19.         ghostPartsIndexs = new int[4];
    20.         playerType = PlayerType.guestPlayer;
    21.     }
    22.     public void NetworkSerialize(NetworkSerializer serializer)
    23.     {
    24.         serializer.Serialize(ref name);
    25.         serializer.Serialize(ref scoreToAdd);
    26.         serializer.Serialize(ref scoreTotal);
    27.         serializer.Serialize(ref indexesOfGuesses);
    28.         serializer.Serialize(ref playerChatConnectionTwitchName);
    29.         serializer.Serialize(ref playerType);
    30.         serializer.Serialize(ref ghostPartsIndexs);
    31.  
    32.     }
    33. }
    I make an instance of this class on my networked player script:

    Code (CSharp):
    1. public NetworkVariable<NetPlayer> quizPlayer = new NetworkVariable<NetPlayer>(new NetworkVariableSettings
    2.     {
    3.         WritePermission=NetworkVariablePermission.ServerOnly,ReadPermission=NetworkVariablePermission.Everyone
    4.     });
    I assign the values of the NetPlayer via ServerRpc
    I have them generated on a Manager script called NetworkMailRoom.cs which handles most network management

    Code (CSharp):
    1. [ServerRpc]
    2.     public void AssignInitialValuesServerRpc(string np)
    3.     {
    4.         NetPlayer player = JsonUtility.FromJson<NetPlayer>(np);
    5.         quizPlayer.Value = player;      
    6.         // from here, add this net player and client Id to the list and dictionary on the manager object
    7.         NetworkMailRoom.instance.AddToNetPlayersDictionaryServerRpc(NetworkManager.Singleton.LocalClientId, quizPlayer.Value);
    8.         NetworkMailRoom.instance.AddToNetPlayerListServerRpc(quizPlayer.Value);
    9.     }
    10.  
    11.  
    I make absolutely no claim that this is the most efficient way to do this. To me this code just makes sense but if anyone wants to give critique or other suggestions, I'd greatly appreciate it.
     
  5. dreamhst7

    dreamhst7

    Joined:
    Mar 27, 2014
    Posts:
    13
    It's not working for Netcode. You have to implement that method in your NetPlayer:
    Code (CSharp):
    1. public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    then you'll get an error:
    The type 'NetworkData' must be valid unmanaged type (simple numeric, 'bool', 'char', 'void', enumeration type or struct type with all fields of unmanaged types at any level of nesting) in order to use it as a type argument for 'T' parameter
     
  6. jc2

    jc2

    Joined:
    Jul 14, 2015
    Posts:
    15
    luke-unity why have u decided to ignore this?
     
  7. luke-unity

    luke-unity

    Joined:
    Sep 30, 2020
    Posts:
    306
    Ignore what? How can I help?
     
  8. jc2

    jc2

    Joined:
    Jul 14, 2015
    Posts:
    15
    hiya :) thanks for responding. im talking about the post directly above mine.

    the code above where NetworkVariable<NetPlayer> quizPlayer is created will never work as NetPlayer contains an array which is a nullable type.
     
    Last edited: Mar 12, 2022
  9. jc2

    jc2

    Joined:
    Jul 14, 2015
    Posts:
    15
    lol typical
     
  10. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    I think you'll find you are more likely to get good answers if you ask good questions. A reference to a different post that doesn't ask a question either is not that.
     
  11. luke-unity

    luke-unity

    Joined:
    Sep 30, 2020
    Posts:
    306
    Managed types are not supported in NetworkVariables. You'd either have to use NetworkList<T> to sync the arrays of the player data or replace the arrays with fixed size unmanaged types such as FixedList128<T> from the Unity.Collections package.
     
  12. Gunit_gamba

    Gunit_gamba

    Joined:
    Mar 29, 2022
    Posts:
    1
    It would be extremely useful with an example of this working on either the docs or if somebody could make a working example of getting this method to work after the new updates, there is barely any information available for newcomers for this kind of stuff, does anybody know where the golden information for getting this kind of thing to work is ? Having player data in a struct or class, and have it all sync instead of having 50 different networkvariables, thanks :)
     
    gamedevjake likes this.
  13. insoluzioni

    insoluzioni

    Joined:
    Nov 25, 2018
    Posts:
    6
    Following this solution, you'll have to change all classes to a struct, then it should work because struct is unmanaged type.

    I didn't figured out, though, how to store a list of that struct using 'NetworkList'. The way I solved the problem turned out to be a much simpler one. I just converted the List<MyClass> to a JSON string (using Newtonsoft), then assigned that string to a NetworkVariable<FixedString512Bytes>. Other players will be able to access that data unserializing it back to the same class.

    I'm not sure if this will work with MonoBehaviour exclusive properties, such as Vector2D. In that case, you'll probably have to store such information in simpler variables like a float array.