Search Unity

Question Multiplayer Player Data - First Principals

Discussion in 'Multiplayer' started by chris-tdc, Feb 6, 2021.

  1. chris-tdc

    chris-tdc

    Joined:
    Dec 11, 2020
    Posts:
    19
    Unity 2020.2 with Mirror
    My first Unity project and I have been wrestling with this for weeks. Here's the simple objective: I want multiple people to join the game, enter their name and then one of the people clicks a Start button and the game starts. I want to store the name of each person and which person clicked Start.

    I created a custom NetworkManager named NetworkManagerMultiPlayerScript and a simple class, let's say named PlayerData, with properties for the Name and StartedGame values. My NetworkManagerMultiPlayerScript has a List of PlayerData objects named AllPlayerData.

    I made my NetworkManagerMultiPlayerScript a Singleton by adding the following code:
    Code (csharp):
    1.  
    2. private static NetworkManagerMultiPlayerScript instance;
    3.         public static NetworkManagerMultiPlayerScript Instance
    4.         {
    5.             get
    6.             {
    7.                 if (instance == null)
    8.                 {
    9.                     instance = FindObjectOfType<NetworkManagerMultiPlayerScript>();
    10.                 }
    11.                 return instance;
    12.             }
    13.         }
    14.  
    In my NetworkManagerMultiPlayerScript.OnServerAddPlayer function I instantiated my PlayerData object and then called

    NetworkServer.AddPlayerForConnection(conn, PlayerData.gameObject);
    to associate the player's connection with the instance and spawn it into all clients. So far I get that.
    So now I have several instances of PlayerData; One on the Server and one at each connected Client, but which do I add into the List<PlayerData> in NetworkManagerMultiPlayerScript? Currently my PlayerData class declares a variable to hold the NetworkManagerMultiPlayerScript singleton instance as follows:
    Code (csharp):
    1.  
    2. private NetworkManagerMultiPlayerScript networkManager;
    3.  
    and then I set the value and tried to add the current PlayerData class to my list as follows:
    Code (CSharp):
    1.  
    2. public override void OnStartClient()
    3.         {
    4.             networkManager = NetworkManagerMultiPlayerScript.Instance;
    5.             networkManager.AllPlayerData.Add(this);
    6.             ...
    7.         }
    8.  
    To my surprise if I then immediately debug the networkManager.AllPlayerData.Count I get 0!

    I then thought that perhaps I should add the PlayerData class on the Server so I created a Command as follows passing 'this' as an argument:
    Code (CSharp):
    1.         [Command]
    2.         private void CmdAddRoomPlayer(NetworkRoomPlayerData playerData)
    3.         {
    4.             NetworkManagerMultiPlayerScript.Instance.AllPlayerData.Add(playerData);
    5.         }
    6.  
    As you can see I found I could non longer use my networkManager variable which I set earlier, I had to use NetworkManagerMultiPlayerScript.Instance. Is this because this is running on the server, not the client where the networkManager is stored?

    Now I find that while I appear to have added 'this' PlayerData to my AllPlayerData list on the server, the AllPlayerData list on my client (networkManager) doesn't have this and I want to iterate over the list to display player data. I can't appear to add SyncVar to my AllPlayerData list on my NetworkManagerMultiPlayerScript, hoping this would keep the AllPlayerData list updated on the clients.

    What I really struggle with conceptually is having multiple versions of everything on the server and on all the clients and where to get/set values? I'm sure it's not too complex once you have a firm mental model of the structure and solid processes for state management, but I fail to grasp it currently!

    Any advise, pointers or suggestions really welcome.
     
  2. bindon

    bindon

    Joined:
    May 25, 2017
    Posts:
    20
    That detailed questions like this get no response at all ... not even a "you need to re-write this question in the following form" or a "you need to refer to this guidelines page for how to write questions so that they will be answered" is a big discouragement for me to post here.

    Where should I post questions to have them actually get answered?
     
  3. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    I'll chime in but my knowledge of Mirror isn't extensive. I wrote a simple multiplayer menu set up but it's thrown together to allow me to focus on the in-game code. Not to mention it was the first thing I wrote in Mirror, it works but probably not best practice.

    I'm a bit confused by your approach, you may be doing more legwork than you actually need and could be letting the NetworkManager handle things for you.

    You don't need to call AddPlayerForConnection yourself. I override the OnClientStart method with this to spawn each player:
    Code (CSharp):
    1.     public override void OnClientConnect(NetworkConnection conn)
    2.     {
    3.         NetworkClient.Ready();
    4.         NetworkClient.AddPlayer();
    5.  
    6.     }
    If you want each client to have a list of the players use GameObject.FindObjectsOfType<PlayerData>(); I don't think that list should necessary live on the NetworkManager but it's your call.

    When a player adds their name have a [Command] back to the server to update it, and that value will be [SyncVar]'ed to all clients.