MLAPI - GameObject name not updating to server

I’m new to networking, so I’m sure this is a basic problem that has a simple solution, but I cannot figure it out.

When I start as host a player prefab is created. I then find that player prefab and rename it. The issue I’m having is that the name change isn’t showing for my client when it connects. It should be named “MyPrefab”, but it’s still keeping its basic name “PlayerPrefab(Clone).”

The PlayerPrefab prefab has the NetworkedObject component. Here’s the inspector:

The code for starting my host and updating the player object is:

Host/Client Start and object renaming code:

    public void StartGameAsHost()
    {
        Debug.Log("Starting game as host...");
        NetworkingManager.Singleton.StartHost();
        InitializePlayerObject();

        //NetworkSceneManager.SwitchScene(SPData.SCENEGameSetup);
    }

    public void StartGameAsClient()
    {
        Debug.Log("Joining game as client...");
        NetworkingManager.Singleton.OnClientConnectedCallback += OnClientJoins;
        NetworkingManager.Singleton.StartClient();

    }

    private void InitializePlayerObject()
    {
        Player p = new Player(playerName); //Generate a player object
        GameObject g = GameObject.Find(SPData.PREFABPlayer + "(Clone)");

        try
        {
            g.name = playerName + " Player";
        }
        catch (System.Exception ex)
        {
            Debug.LogError("Player object not found.\n" + ex.Message);
        }

        PlayerPrefabManager ppm = g.GetComponent<PlayerPrefabManager>();
        ppm.Init(p);
    }

    public void OnClientJoins(ulong id)
    {
        Debug.Log($"Client joined: {id}");
        InitializePlayerObject();
    }

PlayerPrefabManager: (Basic, nothing much to it yet)

using MLAPI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerPrefabManager : NetworkedBehaviour
{
    [Header("Player Reference:")]
    public Player player;
    [Header("Component References:")]
    public PlayerUIHandler playerUIHandler;

    // Start is called before the first frame update
    void Start()
    {
     
    }

    // Update is called once per frame
    void Update()
    {
     
    }

    public void Init(Player p)
    {
        player = p;
        playerUIHandler.SetPlayerName(p.Name);
    }
}

PlayerUIHandler:

using MLAPI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerUIHandler : NetworkedBehaviour
{
    [Header("UI References")]
    public Text playerName;

    // Start is called before the first frame update
    void Start()
    {
     
    }

    // Update is called once per frame
    void Update()
    {
     
    }

    public void SetPlayerName(string n)
    {
        playerName.text = n;
    }
}

Player:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player
{
    private string playerName;
    public string Name { get => playerName; private set { } }

    public Player(string n)
    {
        playerName = n;
    }
}

As I said, I’m sure this is something simple, but I could really use some help.

I’m not sure if it’s needed, but just in case, here’s the full code of the LobbyUIHandler script that contains all of my client code. There’s a lot of debugging and sequencing stuff in there since I’m still trying to figure out how networking works.

using MLAPI;
using MLAPI.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class LobbyUIHandler : NetworkedBehaviour
{
    [Header("UI References")]
    public InputField fieldName;

    [Space(10)]
    public Button buttonHost;
    public Button buttonJoin;

    private string playerName;

    public void Start()
    {
        try
        {
            playerName = PlayerPrefs.GetString(SPData.PREFPlayerName);
        } catch (System.Exception ex)
        {
            Debug.LogError(ex.Message);
            playerName = "";
        }

        fieldName.text = playerName;
        UpdateButtonState();

        //DEBUG
        NetworkSceneManager.OnSceneSwitchStarted += OnStartSceneSwitch;
        NetworkSceneManager.OnSceneSwitched += OnSceneSwitched;
    }

    public void OnDestroy()
    {

        //DEBUG
        NetworkSceneManager.OnSceneSwitchStarted -= OnStartSceneSwitch;
    }

    public void OnNameChanged()
    {
        playerName = fieldName.text;
        PlayerPrefs.SetString(SPData.PREFPlayerName, playerName);
        UpdateButtonState();
    }

    private void UpdateButtonState()
    {
        buttonHost.interactable = !string.IsNullOrEmpty(playerName);
        buttonJoin.interactable = !string.IsNullOrEmpty(playerName);
    }

    public void StartGameAsHost()
    {
        Debug.Log("Starting game as host...");
        NetworkingManager.Singleton.StartHost();
        InitializePlayerObject();

        //NetworkSceneManager.SwitchScene(SPData.SCENEGameSetup);
    }

    public void StartGameAsClient()
    {
        Debug.Log("Joining game as client...");
        NetworkingManager.Singleton.OnClientConnectedCallback += OnClientJoins;
        NetworkingManager.Singleton.StartClient();

    }

    private void InitializePlayerObject()
    {
        Player p = new Player(playerName); //Generate a player object
        GameObject g = GameObject.Find(SPData.PREFABPlayer + "(Clone)");

        try
        {
            g.name = playerName + " Player";
        }
        catch (System.Exception ex)
        {
            Debug.LogError("Player object not found.\n" + ex.Message);
        }

        PlayerPrefabManager ppm = g.GetComponent<PlayerPrefabManager>();
        ppm.Init(p);
    }

    public void OnStartSceneSwitch(AsyncOperation operation)
    {
        //Debug.Log("The scene should have started switching now.");
    }

    public void OnSceneSwitched()
    {
        Debug.Log("The scene has finished switching.");
    }

    public void OnClientJoins(ulong id)
    {
        Debug.Log($"Client joined: {id}");
        InitializePlayerObject();
    }
}

SPData is just a class that contains persistent strings and other data like that.

There is a few things which you have to do differently with MLAPI. Right now you are instantiating a player object on everyone when a new client joins. What you should do instead is only instantiate the object on the server and then call the Spawn function on NetworkObject instead.

To synchronize the player name you will need a NetworkVariable because a regular string won’t be synchronized automatically over the network. I recommend checking out our Hello World tutorial + the tutorial building on top of it and reading up on object spawning.
Hello World: https://docs-multiplayer.unity3d.com/docs/tutorials/helloworldintro
Spawning: https://docs-multiplayer.unity3d.com/docs/mlapi-basics/object-spawning

3 Likes

Thank you for the response, but I’m still a little confused.

I can’t declare the GameObject’s name to be a NetworkedVariable, that’s built-in and I can’t modify it. I tried assigning it by a NetworkedVarString, but that didn’t work.

I’ll look at changing the spawning system, but I’d still like to figure this out. If this GameObject is a NetworkedObject, it seems like any changes made to the base GameObject should be sent to the network…

Modified Code:

LobbyUIHandler:

using MLAPI;
using MLAPI.NetworkedVar;
using MLAPI.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class LobbyUIHandler : NetworkedBehaviour
{
    [Header("UI References")]
    public InputField fieldName;

    [Space(10)]
    public Button buttonHost;
    public Button buttonJoin;

    private string playerName;

    public void Start()
    {
        try
        {
            playerName = PlayerPrefs.GetString(SPData.PREFPlayerName);
        } catch (System.Exception ex)
        {
            Debug.LogError(ex.Message);
            playerName = "";
        }

        fieldName.text = playerName;
        UpdateButtonState();

        //DEBUG
        NetworkSceneManager.OnSceneSwitchStarted += OnStartSceneSwitch;
        NetworkSceneManager.OnSceneSwitched += OnSceneSwitched;
    }

    public void OnDestroy()
    {

        //DEBUG
        NetworkSceneManager.OnSceneSwitchStarted -= OnStartSceneSwitch;
    }

    public void OnNameChanged()
    {
        playerName = fieldName.text;
        PlayerPrefs.SetString(SPData.PREFPlayerName, playerName);
        UpdateButtonState();
    }

    private void UpdateButtonState()
    {
        buttonHost.interactable = !string.IsNullOrEmpty(playerName);
        buttonJoin.interactable = !string.IsNullOrEmpty(playerName);
    }

    public void StartGameAsHost()
    {
        Debug.Log("Starting game as host...");
        NetworkingManager.Singleton.StartHost();
        InitializePlayerObject();

        //NetworkSceneManager.SwitchScene(SPData.SCENEGameSetup);
    }

    public void StartGameAsClient()
    {
        Debug.Log("Joining game as client...");
        NetworkingManager.Singleton.OnClientConnectedCallback += OnClientJoins;
        NetworkingManager.Singleton.StartClient();

    }

    private void InitializePlayerObject()
    {
        NetworkedVarString pName = new NetworkedVarString(playerName);
        Player p = new Player(pName); //Generate a player object
        GameObject g = GameObject.Find(SPData.PREFABPlayer + "(Clone)");

        try
        {
            g.name = pName.Value + " Player";
        }
        catch (System.Exception ex)
        {
            Debug.LogError("Player object not found.\n" + ex.Message);
        }

        PlayerPrefabManager ppm = g.GetComponent<PlayerPrefabManager>();
        ppm.Init(p);
    }

    public void OnStartSceneSwitch(AsyncOperation operation)
    {
        //Debug.Log("The scene should have started switching now.");
    }

    public void OnSceneSwitched()
    {
        Debug.Log("The scene has finished switching.");
    }

    public void OnClientJoins(ulong id)
    {
        Debug.Log($"Client joined: {id}");
        InitializePlayerObject();
    }
}

Player:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MLAPI.NetworkedVar;

public class Player
{
    private NetworkedVarString playerName;
    public NetworkedVarString Name { get => playerName; private set { } }

    public Player(NetworkedVarString n)
    {
        playerName = n;
    }
}

PlayerUIHandler:

using MLAPI;
using MLAPI.NetworkedVar;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerUIHandler : NetworkedBehaviour
{
    [Header("UI References")]
    public Text playerName;

    // Start is called before the first frame update
    void Start()
    {
       
    }

    // Update is called once per frame
    void Update()
    {
       
    }

    public void SetPlayerName(NetworkedVarString n)
    {
        playerName.text = n.Value;
    }
}

PlayerPrefabManager:

using MLAPI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerPrefabManager : NetworkedBehaviour
{
    [Header("Player Reference:")]
    public Player player;
    [Header("Component References:")]
    public PlayerUIHandler playerUIHandler;

    // Start is called before the first frame update
    void Start()
    {
       
    }

    // Update is called once per frame
    void Update()
    {
       
    }

    public void Init(Player p)
    {
        player = p;
        playerUIHandler.SetPlayerName(p.Name);
    }
}

Are you using the new MLAPI 0.1.0? Because the name should be NetworkVariableString. Also from docs you can only use NetworkVariable in the NetworkBehaviour class.

To demonstrate it should be like this more or less. Assign NetworkVariable.Value in Server, and read the value in client.

public NetworkVariableString PlayerName = new NetworkVariableString();
public Text PlayerNameText;

public void SetName()
{
    if (NetworkManager.Singleton.IsServer)
    {
        PlayerName.Value = "Player";
    }
    PlayerNameText.text = PlayerName.Value;
}

Isnt using a network variable for name a huge waste of resources? Would not RPC be a better implementation.