[Netcode] [Invalid Destroy] + MissingReference During Sceneswitching and Spawning

Hey, I’ve been stuck on this problem with spawning in my GameScene with 3 or more clients connected in my multiplayer game in netcode for gameObjects. I have a Character Selection scene which portrays charaterprefabs and their local ready state for everyone, which works fine. They’re all networked and once they are all ready the I call a networked SceneLoader method to load the GameScene method which then on SceneManager_OnLoadEventCompleted triggered should spawn the players.

When there are only 2 players the clients spawn fine but when there are 3 or more, the later clients joined get the following error:

“[Netcode] [Invalid Destroy][CharacterSelectionPrefab(Clone)][NetworkObjectId:5] Destroy a spawned NetworkObject on a non-host client is not valid. Call Destroy or Despawn on the server/host instead.”

And For each Update() : “MissingReferenceException: The object of type ‘Unity.Netcode.NetworkObject’ has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.”

What could be the issue in my logic for my networked scene managers for the characterselectionscene / main game scene manager?

See code references below :smiley:

    private NetworkVariable<State> currentState = new NetworkVariable<State>(State.Start);
    private NetworkVariable<float> countdownTimer = new NetworkVariable<float>(3f); // Use float for countdown
    private bool isLocalPlayerReady = false;
    private Dictionary<ulong, bool> playerReadyDictionary;

    private void Awake()
    {
        Instance = this;
        playerReadyDictionary = new Dictionary<ulong, bool>();
        if (MultiplayerGameManager.IsServerCustom())
        {
            NetworkManager.Singleton.SceneManager.OnLoadEventCompleted += SceneManager_OnLoadEventCompleted;
        }
        Debug.Log("GameManager: Awake");
    }

    private void Start()
    {
        if (IsServer)
        {
            Debug.Log("GameManager: Server is running");
            currentState.Value = State.Start;
            OnStateChanged?.Invoke(this, EventArgs.Empty);
        }
        Debug.Log("GameManager: Start");
    }

    public override void OnNetworkSpawn()
    {
        if (IsServer)
        {
            // Ensure an entry in the dictionary for each connected client
            foreach (ulong clientId in NetworkManager.Singleton.ConnectedClientsIds)
            {
                if (!playerReadyDictionary.ContainsKey(clientId))
                {
                    playerReadyDictionary[clientId] = false;
                }
            }
            Debug.Log("GameManager: Initialized playerReadyDictionary for all clients.");
        }
        currentState.OnValueChanged += State_OnValueChanged;
        Debug.Log("GameManager: OnNetworkSpawn");
    }

    public void SceneManager_OnLoadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, List<ulong> clientsCompleted, List<ulong> clientsTimedOut)
    {
        Debug.Log("GameManager: Scene loaded successfully on the server. Spawning players...");

        // Ensure all clients have completed loading
        foreach (ulong clientId in NetworkManager.Singleton.ConnectedClientsIds)
        {
            if (!clientsCompleted.Contains(clientId))
            {
                Debug.LogWarning($"GameManager: Client {clientId} has not completed loading.");
                return;
            }
        }

        // Spawn players for all clients
        foreach (ulong clientId in NetworkManager.Singleton.ConnectedClientsIds)
        {
            Transform playerTransform = Instantiate(NetworkPlayer);
            playerTransform.GetComponent<NetworkObject>().SpawnAsPlayerObject(clientId, true);
            Debug.Log($"GameManager: Spawned player for client {clientId}");
        }
    }
using UnityEngine;
using Unity.Netcode;
using System;
using System.Collections.Generic;
using System.Collections;

public class CharacterSelectionManager : NetworkBehaviour
{
    public static CharacterSelectionManager Instance { get; private set; }

    [SerializeField] private Transform[] spawnPositions;
    [SerializeField] private GameObject defaultCharacterPrefab;
    [SerializeField] private GameObject loadingUI;

    private Dictionary<ulong, GameObject> spawnedCharacters = new Dictionary<ulong, GameObject>();
    private Dictionary<ulong, PlayerData> playerDataDictionary = new Dictionary<ulong, PlayerData>();

    public event EventHandler OnReadyChanged;

    private void Awake()
    {
        if (Instance != null)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        loadingUI.SetActive(false); // Ensure loading UI is disabled at start
    }

    public override void OnNetworkSpawn()
    {
        if (IsServer)
        {
            InitializeManager();
        }
    }

    private void InitializeManager()
    {
        Debug.Log("CharacterSelectionManager: Initializing on Server.");
        MultiplayerGameManager.Instance.OnPlayerDataNetworkListChanged += OnPlayerDataNetworkListChanged;
        InitializeSpawnPositions();
        UpdatePlayerSpawns(); // Call once to initialize current players on start
    }

    private void OnPlayerDataNetworkListChanged(object sender, EventArgs e)
    {
        if (IsServer)
        {
            Debug.Log("CharacterSelectionManager: Player data network list changed.");
            UpdatePlayerSpawns();
        }
    }

    private void InitializeSpawnPositions()
    {
        Debug.Log("CharacterSelectionManager: Initializing spawn positions.");
        // Set up the spawn positions if needed
    }

    private void UpdatePlayerSpawns()
    {
        var playerDataList = MultiplayerGameManager.Instance.GetPlayerDataList();
        Debug.Log($"CharacterSelectionManager: Updating player spawns. Player count: {playerDataList.Count}");

        for (int i = 0; i < playerDataList.Count; i++)
        {
            SpawnCharacterForPlayer(playerDataList[i]);
        }
    }

    private void SpawnCharacterForPlayer(PlayerData playerData)
    {
        if (spawnedCharacters.ContainsKey(playerData.clientId))
        {
            Debug.Log($"CharacterSelectionManager: Character already spawned for client {playerData.clientId}.");
            return;
        }

        if (playerData.spawnIndex < spawnPositions.Length)
        {
            Transform spawnTransform = spawnPositions[playerData.spawnIndex];
            GameObject character = Instantiate(defaultCharacterPrefab, spawnTransform.position, spawnTransform.rotation);
            Debug.Log($"CharacterSelectionManager: Instantiated character for client {playerData.clientId} at spawn index {playerData.spawnIndex}.");

            NetworkObject characterNetworkObject = character.GetComponent<NetworkObject>();
            if (characterNetworkObject != null)
            {
                characterNetworkObject.SpawnAsPlayerObject(playerData.clientId, true);
                Debug.Log($"CharacterSelectionManager: Spawned NetworkObject for client {playerData.clientId} with NetworkObjectId {characterNetworkObject.NetworkObjectId}.");
            }

            spawnedCharacters[playerData.clientId] = character;
        }
        else
        {
            Debug.LogWarning($"CharacterSelectionManager: Spawn index {playerData.spawnIndex} out of range for client {playerData.clientId}.");
        }
    }

    public void DespawnCharacter(ulong clientId)
    {
        if (!IsServer)
        {
            Debug.LogWarning("DespawnCharacter can only be called on the server.");
            return;
        }

        if (spawnedCharacters.ContainsKey(clientId))
        {
            GameObject character = spawnedCharacters[clientId];
            if (character == null)
            {
                Debug.LogWarning($"CharacterSelectionManager: Character GameObject for client {clientId} is already null.");
                spawnedCharacters.Remove(clientId);
                return;
            }

            NetworkObject networkObject = character.GetComponent<NetworkObject>();
            if (networkObject != null)
            {
                Debug.Log($"CharacterSelectionManager: Despawning NetworkObject for client {clientId} with NetworkObjectId {networkObject.NetworkObjectId}.");
                networkObject.Despawn(true); // Ensure the object is despawned and destroyed
            }
            else
            {
                Debug.LogWarning($"CharacterSelectionManager: No NetworkObject found for client {clientId}.");
            }

            spawnedCharacters.Remove(clientId);
            DespawnCharacterClientRpc(clientId);
        }
        else
        {
            Debug.LogWarning($"CharacterSelectionManager: No character found to despawn for client {clientId}.");
        }
    }

    [ClientRpc]
    private void DespawnCharacterClientRpc(ulong clientId)
    {
        if (spawnedCharacters.ContainsKey(clientId))
        {
            GameObject character = spawnedCharacters[clientId];
            if (character != null)
            {
                Destroy(character);
            }
            spawnedCharacters.Remove(clientId);
        }
    }

    private void DespawnAllCharacters()
    {
        if (!IsServer)
        {
            Debug.LogWarning("DespawnAllCharacters can only be called on the server.");
            return;
        }

        // Create a list of keys to iterate over
        List<ulong> clientIds = new List<ulong>(spawnedCharacters.Keys);
        foreach (var clientId in clientIds)
        {
            DespawnCharacter(clientId);
        }
        spawnedCharacters.Clear(); // Clear the dictionary after despawning
    }

    public void SetPlayerReady()
    {
        SetPlayerReadyServerRpc();
    }

    [ServerRpc(RequireOwnership = false)]
    private void SetPlayerReadyServerRpc(ServerRpcParams serverRpcParams = default)
    {
        ulong clientId = serverRpcParams.Receive.SenderClientId;
        if (playerDataDictionary.ContainsKey(clientId))
        {
            PlayerData playerData = playerDataDictionary[clientId];
            playerData.isReady = true;
            playerDataDictionary[clientId] = playerData;
        }
        else
        {
            playerDataDictionary[clientId] = new PlayerData
            {
                clientId = clientId,
                isReady = true
            };
        }

        SetPlayerReadyClientRpc(clientId, playerDataDictionary[clientId]);

        bool allClientsReady = true;
        foreach (ulong id in NetworkManager.Singleton.ConnectedClientsIds)
        {
            if (!playerDataDictionary.ContainsKey(id) || !playerDataDictionary[id].isReady)
            {
                allClientsReady = false;
                break;
            }
        }
        if (allClientsReady)
        {
            ShowLoadingUIClientRpc();
            StartCoroutine(WaitForDespawnAndSwitchScene());
        }
        Debug.Log("All players: " + allClientsReady);
    }

    private IEnumerator WaitForDespawnAndSwitchScene()
    {
        DespawnAllCharacters();

        // Wait until all characters are despawned
        while (spawnedCharacters.Count > 0)
        {
            Debug.Log("Waiting for all characters to despawn...");
            yield return null;
        }

        Debug.Log("All characters despawned. Switching scene.");
        SceneLoader.LoadNetwork(SceneLoader.Scene.GameScene);
    }

    [ClientRpc]
    private void SetPlayerReadyClientRpc(ulong clientId, PlayerData playerData)
    {
        playerDataDictionary[clientId] = playerData;
        OnReadyChanged?.Invoke(this, EventArgs.Empty);
    }

    [ClientRpc]
    private void ShowLoadingUIClientRpc()
    {
        loadingUI.SetActive(true);
    }

    public bool IsPlayerReady(ulong clientId)
    {
        return playerDataDictionary.ContainsKey(clientId) && playerDataDictionary[clientId].isReady;
    }
}

It may be related to calling DespawnCharacterClientRpc but I can’t see where spawnedCharacters is populated on the client side. In any case it shouldn’t be necessary to have the client destroy the object.

Post the stack trace of the error as it may contain more info.

Ah yeah possibly. I tried mixing with some of the Rpc and all of that but I didn’t manage to make it work. It just seems as the third client glitches and doesn’t despawn properly compared to the first two.

This is the script attached to each player prefab model:

using System;
using Unity.Netcode;
using UnityEngine;
using TMPro; // Add this line to use TextMeshPro

public class CharacterSelection : NetworkBehaviour
{
    [SerializeField] private GameObject readyUpText;
    [SerializeField] private TextMeshPro playerNameText; // Change TextMesh to TextMeshPro

    private NetworkVariable<PlayerData> playerData = new NetworkVariable<PlayerData>();

    public override void OnNetworkSpawn()
    {
        if (IsOwner)
        {
            if (CharacterSelectionManager.Instance != null)
            {
                CharacterSelectionManager.Instance.OnReadyChanged += OnReadyChanged;
            }
        }

        playerData.OnValueChanged += OnPlayerDataChanged;
        UpdateCharacterAppearance(playerData.Value.selectedSkin);
        UpdateReadyText(playerData.Value.isReady);
        UpdatePlayerName(GetPlayerName(playerData.Value)); // Use GetPlayerName method
    }

    public override void OnNetworkDespawn()
    {
        if (IsOwner && CharacterSelectionManager.Instance != null)
        {
            CharacterSelectionManager.Instance.OnReadyChanged -= OnReadyChanged;
        }
        playerData.OnValueChanged -= OnPlayerDataChanged;
        Destroy(gameObject);
    }

This is the full stacktrace, I can visually also see how one character remains spawned although the other two that work when the fault one is disconnected, despawn as they should.

[Netcode] [Invalid Destroy][CharacterSelectionPrefab(Clone)][NetworkObjectId:6] Destroy a spawned NetworkObject on a non-host client is not valid. Call Destroy or Despawn on the server/host instead.
UnityEngine.Debug:LogError (object)
Unity.Netcode.NetworkLog:LogError (string) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs:34)
Unity.Netcode.NetworkLog:LogServer (string,Unity.Netcode.NetworkLog/LogType) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs:98)
Unity.Netcode.NetworkLog:LogErrorServer (string) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs:58)
Unity.Netcode.NetworkObject:OnDestroy () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:1531)

MissingReferenceException: The object of type ‘Unity.Netcode.NetworkObject’ has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Object+MarshalledUnityObject.TryThrowEditorNullExceptionObject (UnityEngine.Object unityObj, System.String parameterName) (at :0)
UnityEngine.Bindings.ThrowHelper.ThrowNullReferenceException (System.Object obj) (at :0)
UnityEngine.Component.get_gameObject () (at :0)
Unity.Netcode.NetworkObject.UpdateForSceneChanges () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:3235)
Unity.Netcode.NetworkObject.UpdateNetworkObjectSceneChanges () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:3204)
Unity.Netcode.NetworkManager.NetworkUpdate (Unity.Netcode.NetworkUpdateStage updateStage) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs:371)
Unity.Netcode.NetworkUpdateLoop.RunNetworkUpdateStage (Unity.Netcode.NetworkUpdateStage updateStage) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs:191)
Unity.Netcode.NetworkUpdateLoop+NetworkPostLateUpdate+<>c.b__0_0 () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs:286)

Edit: Ignore what I said about Distributed Authority as I guess you’re using v2.1.1, I’ve just updated and it now aligns with the correct error log.

The Destroy call inside OnNetworkDespawn looks suspicious, you could try removing it and see if that makes a difference.

I tried it out by removing the OnNetworkDespawn() and still got the following result. It worked for 2 of the clients (host +1) but not for the 3rd client.

[Netcode] [Invalid Destroy][CharacterSelectionPrefab(Clone)][NetworkObjectId:6] Destroy a spawned NetworkObject on a non-host client is not valid. Call Destroy or Despawn on the server/host instead.
UnityEngine.Debug:LogError (object)
Unity.Netcode.NetworkLog:LogError (string) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs:34)
Unity.Netcode.NetworkLog:LogServer (string,Unity.Netcode.NetworkLog/LogType) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs:98)
Unity.Netcode.NetworkLog:LogErrorServer (string) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs:58)
Unity.Netcode.NetworkObject:OnDestroy () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:1531)

MissingReferenceException: The object of type ‘Unity.Netcode.NetworkObject’ has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Object+MarshalledUnityObject.TryThrowEditorNullExceptionObject (UnityEngine.Object unityObj, System.String parameterName) (at :0)
UnityEngine.Bindings.ThrowHelper.ThrowNullReferenceException (System.Object obj) (at :0)
UnityEngine.Component.get_gameObject () (at :0)
Unity.Netcode.NetworkObject.UpdateForSceneChanges () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:3235)
Unity.Netcode.NetworkObject.UpdateNetworkObjectSceneChanges () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:3204)
Unity.Netcode.NetworkManager.NetworkUpdate (Unity.Netcode.NetworkUpdateStage updateStage) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs:371)
Unity.Netcode.NetworkUpdateLoop.RunNetworkUpdateStage (Unity.Netcode.NetworkUpdateStage updateStage) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs:191)
Unity.Netcode.NetworkUpdateLoop+NetworkPostLateUpdate+<>c.b__0_0 () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs:286)

Here is the updated code:

using UnityEngine;
using Unity.Netcode;
using System;
using System.Collections.Generic;

public class CharacterSelectionManager : NetworkBehaviour
{
    public static CharacterSelectionManager Instance { get; private set; }

    [SerializeField] private Transform[] spawnPositions;
    [SerializeField] private GameObject defaultCharacterPrefab;
    [SerializeField] private GameObject loadingUI;

    private Dictionary<ulong, GameObject> spawnedCharacters = new Dictionary<ulong, GameObject>();
    private Dictionary<ulong, bool> playerReadyState = new Dictionary<ulong, bool>();
    private HashSet<int> takenSpawnIndices = new HashSet<int>();

    public event EventHandler OnReadyChanged;

    private void Awake()
    {
        if (Instance != null)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        loadingUI.SetActive(false); // Ensure loading UI is disabled at start
    }

    public override void OnNetworkSpawn()
    {
        if (IsServer)
        {
            InitializeManager();
        }
    }

    private void InitializeManager()
    {
        Debug.Log("CharacterSelectionManager: Initializing on Server.");
        MultiplayerGameManager.Instance.OnPlayerDataNetworkListChanged += OnPlayerDataNetworkListChanged;
        NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnect;
        InitializeSpawnPositions();
        UpdatePlayerSpawns(); // Call once to initialize current players on start
    }

    private void OnPlayerDataNetworkListChanged(object sender, EventArgs e)
    {
        if (IsServer)
        {
            Debug.Log("CharacterSelectionManager: Player data network list changed.");
            UpdatePlayerSpawns();
        }
    }

    private void InitializeSpawnPositions()
    {
        Debug.Log("CharacterSelectionManager: Initializing spawn positions.");
        // Set up the spawn positions if needed
    }

    private void UpdatePlayerSpawns()
    {
        var playerDataList = MultiplayerGameManager.Instance.GetPlayerDataList();
        Debug.Log($"CharacterSelectionManager: Updating player spawns. Player count: {playerDataList.Count}");

        for (int i = 0; i < playerDataList.Count; i++)
        {
            SpawnCharacterForPlayer(playerDataList[i].clientId);
        }
    }

    private void SpawnCharacterForPlayer(ulong clientId)
    {
        if (spawnedCharacters.ContainsKey(clientId))
        {
            Debug.Log($"CharacterSelectionManager: Character already spawned for client {clientId}.");
            return;
        }

        int playerIndex = MultiplayerGameManager.Instance.GetPlayerDataIndexFromClientId(clientId);
        if (playerIndex == -1)
        {
            Debug.LogWarning($"CharacterSelectionManager: PlayerData not found for client {clientId}.");
            return;
        }

        var playerData = MultiplayerGameManager.Instance.GetPlayerDataFromPlayerIndex(playerIndex);
        if (playerData.spawnIndex < spawnPositions.Length)
        {
            Transform spawnTransform = spawnPositions[playerData.spawnIndex];
            GameObject character = Instantiate(defaultCharacterPrefab, spawnTransform.position, spawnTransform.rotation);
            Debug.Log($"CharacterSelectionManager: Instantiated character for client {clientId} at spawn index {playerData.spawnIndex}.");

            NetworkObject characterNetworkObject = character.GetComponent<NetworkObject>();
            if (characterNetworkObject != null)
            {
                characterNetworkObject.SpawnAsPlayerObject(clientId, true);
                Debug.Log($"CharacterSelectionManager: Spawned NetworkObject for client {clientId} with NetworkObjectId {characterNetworkObject.NetworkObjectId}.");
            }

            spawnedCharacters[clientId] = character;
            takenSpawnIndices.Add(playerData.spawnIndex);
        }
        else
        {
            Debug.LogWarning($"CharacterSelectionManager: Spawn index {playerData.spawnIndex} out of range for client {clientId}.");
        }
    }

    public void DespawnCharacter(ulong clientId)
    {
        if (IsServer && spawnedCharacters.ContainsKey(clientId))
        {
            GameObject character = spawnedCharacters[clientId];
            if (character == null)
            {
                Debug.LogWarning($"CharacterSelectionManager: Character GameObject for client {clientId} is already null.");
                spawnedCharacters.Remove(clientId);
                return;
            }

            NetworkObject networkObject = character.GetComponent<NetworkObject>();
            if (networkObject != null)
            {
                Debug.Log($"CharacterSelectionManager: Despawning NetworkObject for client {clientId} with NetworkObjectId {networkObject.NetworkObjectId}.");
                networkObject.Despawn(true); // Ensure the object is despawned and destroyed
            }
            else
            {
                Debug.LogWarning($"CharacterSelectionManager: No NetworkObject found for client {clientId}.");
            }

            spawnedCharacters.Remove(clientId);
        }
        else
        {
            Debug.LogWarning($"CharacterSelectionManager: No character found to despawn for client {clientId}.");
        }
    }

    private void DespawnAllCharacters()
    {
        if (IsServer)
        {
            // Clear the dictionary and hashset without despawning the characters
            spawnedCharacters.Clear();
            takenSpawnIndices.Clear();
        }
    }

    public void SetPlayerReady()
    {
        SetPlayerReadyServerRpc();
        OnReadyChanged?.Invoke(this, EventArgs.Empty);
    }

    [ServerRpc(RequireOwnership = false)]
    private void SetPlayerReadyServerRpc(ServerRpcParams serverRpcParams = default)
    {
        ulong clientId = serverRpcParams.Receive.SenderClientId;
        playerReadyState[clientId] = true;

        // Notify all clients about the ready state change
        UpdateReadyStateClientRpc(clientId, true);

        bool allClientsReady = true;
        foreach (ulong id in NetworkManager.Singleton.ConnectedClientsIds)
        {
            if (!playerReadyState.ContainsKey(id) || !playerReadyState[id])
            {
                allClientsReady = false;
                break;
            }
        }
        if (allClientsReady)
        {
            ShowLoadingUIClientRpc();
            DespawnAllCharacters();
            LobbyManager.Instance.DeleteLobby(); // Delete the lobby after all players are ready
            SceneLoader.LoadNetwork(SceneLoader.Scene.GameScene);
        }
        Debug.Log("All players: " + allClientsReady);
    }

    [ClientRpc]
    private void ShowLoadingUIClientRpc()
    {
        loadingUI.SetActive(true);
    }

    [ClientRpc]
    private void UpdateReadyStateClientRpc(ulong clientId, bool isReady)
    {
        if (playerReadyState.ContainsKey(clientId))
        {
            playerReadyState[clientId] = isReady;
        }
        else
        {
            playerReadyState.Add(clientId, isReady);
        }

        // Trigger the OnReadyChanged event on the client side
        OnReadyChanged?.Invoke(this, EventArgs.Empty);
    }

    public bool IsPlayerReady(ulong clientId)
    {
        return playerReadyState.ContainsKey(clientId) && playerReadyState[clientId];
    }

    private void OnClientDisconnect(ulong clientId)
    {
        if (IsServer)
        {
            if (playerReadyState.ContainsKey(clientId))
            {
                playerReadyState.Remove(clientId);
            }

            if (spawnedCharacters.ContainsKey(clientId))
            {
                DespawnCharacter(clientId);
            }
        }
    }

    public override void OnDestroy()
    {
        if (IsServer)
        {
            MultiplayerGameManager.Instance.OnPlayerDataNetworkListChanged -= OnPlayerDataNetworkListChanged;
            NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnect;
        }
        base.OnDestroy();
    }
}
using System.Collections;
using UnityEngine;
using TMPro;
using Unity.Netcode;
using Unity.Collections;

public class CharacterSelection : NetworkBehaviour
{
    [SerializeField] private GameObject readyUpText;
    [SerializeField] private TextMeshPro playerNameText;

    private void Start()
    {
        if (MultiplayerGameManager.Instance == null)
        {
            Debug.LogError("MultiplayerGameManager instance is not available.");
            return;
        }

        MultiplayerGameManager.Instance.OnPlayerDataNetworkListChanged += MultiplayerGameManager_OnPlayerDataNetworkListChanged;
        CharacterSelectionManager.Instance.OnReadyChanged += CharacterSelectionManager_OnReadyChanged;

        UpdatePlayer();
    }

    private void MultiplayerGameManager_OnPlayerDataNetworkListChanged(object sender, System.EventArgs e)
    {
        UpdatePlayer();
    }

    private void CharacterSelectionManager_OnReadyChanged(object sender, System.EventArgs e)
    {
        UpdatePlayer();
    }

    private void UpdatePlayer()
    {
        // Re-fetch player data to ensure the latest data is used
        ulong clientId = OwnerClientId;
        Debug.Log($"UpdatePlayer: OwnerClientId = {clientId}");

        int playerIndex = MultiplayerGameManager.Instance.GetPlayerDataIndexFromClientId(clientId);
        Debug.Log($"UpdatePlayer: PlayerIndex = {playerIndex}");

        if (MultiplayerGameManager.Instance.IsPlayerIndexConnected(playerIndex))
        {
            Show();
            PlayerData playerData = MultiplayerGameManager.Instance.GetPlayerDataFromPlayerIndex(playerIndex);
            playerNameText.text = playerData.playerName.ToString();
            readyUpText.SetActive(CharacterSelectionManager.Instance.IsPlayerReady(clientId));
            Debug.Log($"CharacterSelection: Updating player with skin {playerData.selectedSkin}");
            UpdateCharacterAppearance(playerData.selectedSkin);
        }
        else
        {
            Debug.LogError("PlayerData not found or player is not connected.");
            Hide();
        }
    }

    private void UpdateCharacterAppearance(int selectedSkin)
    {
        // Update the character's appearance based on the selected skin
        switch (selectedSkin)
        {
            case 0:
                // Apply default appearance
                break;
            case 1:
                // Apply skin 1
                break;
            case 2:
                // Apply skin 2
                break;
            default:
                Debug.LogWarning($"Unknown customization {selectedSkin}");
                break;
        }
    }

    public void SelectSkin(int skinIndex)
    {
        UpdateCharacterAppearance(skinIndex);
    }

    public void Show()
    {
        gameObject.SetActive(true);
    }

    public void Hide()
    {
        gameObject.SetActive(false);
    }

    public override void OnDestroy()
    {
        if (MultiplayerGameManager.Instance != null)
        {
            MultiplayerGameManager.Instance.OnPlayerDataNetworkListChanged -= MultiplayerGameManager_OnPlayerDataNetworkListChanged;
        }

        if (CharacterSelectionManager.Instance != null)
        {
            CharacterSelectionManager.Instance.OnReadyChanged -= CharacterSelectionManager_OnReadyChanged;
        }
        base.OnDestroy();
    }
}

If the object’s not being explicitly destroyed by the client it may be the object is destroyed when the scene is switched, I can’t see enough to tell though. If you can share the project code I’ll have a go at reproducing the issue.

Absolutely, will send it tommorow bro, i can also send more pictures

I have an identical error… with a slightly different scenario. I’m just trying to reload the Scene (via a “Retry” pause menu item). I’m using the NetworkManager SceneManager to reload the scene. Works great for the Host … but if I have a connected Client, the Client will error with the same error you’re seeing above and crash. I don’t know what I’m doing incorrectly… there must be something somewhere else that is causing the Network Object to be accessed on the client when it shouldn’t be. I’ve scanned all of my code and I’m struggling to find it… but I’ll keep looking. I’ll be watching this thread with great interest :slight_smile:

Im trying to fix uploading it to git or something so you can see the project but on the side meanwhile:

Maybe another issue could be because I’m using Unity 6’s Multiplayer Services but using code that’s meant for earlier versions where for example Relay etc is combined into one. I for example get this error when trying to integrate relay in my lobby

Severity Code Description
Error (active) CS0433 The type ‘RelayServiceException’ exists in both ‘Unity.Services.Multiplayer, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null’ and ‘Unity.Services.Relay, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null’

I’m not sure on the error as I don’t use Unity’s multiplayer services. For uploading the project you could try Github Desktop as I find that simplifies the process.

I did try and replicate what you were doing, in a fashion, I didn’t encounter any errors though. The code is available here in case it’s of any help to you.

Alright ill look into it. Thank you for the assistance

Took me a while to fix it:

I also fixed the issues with Relay Service conflict with Multiplayer Services and some other minor fixes so it should work. If any packages are missing it might be because I deleted unnecessary things for because they were too large for git. The spawning with 3+ clients persists. Since I use Relay I think It is needed to use Multiplayer Play Mode window instead of multiple builds.

Can you explain the problem and point out exactly where you fixed it? I’ve been on holidays for a week so haven’t had much chance to debug this myself - I’m curious where the fix actually resides in your code. Which file/set of files? What actually caused the problem?

The relay - multiplayer services conflict was solved by deleting the relay package and using the newer Allocation codenames:

" If you are using the Multiplayer Services SDK (com.unity.services.multiplayer ), you can use AllocationUtils.ToRelayServerData(allocation, connectionType) to build the RelayServerData model."

will try to do what I can to fix the other thing but the git is up there, if anyone has experience with this type of problems.

I had a look but I’m not able to get it running, it looks like Plastic is getting confused with who I am. This may be a moot point though if I need access to UGS to run the project.

Can you create a new project and isolate the code that causes the problem, also did you see anything fundamentally different in your code and my example project?

I think u can solve that if u go into project settings > services general

Change the Unity Project ID so it is linked with your cloud instead.

Otherwise I could share the project to you in private, or try to excert the important code snippets if that’s better. Problem is that all components are linked with each other and it’d be hard to send the spawning and ready up scene alone. However I do know that the issue lies in the switch between “CharacterSelction” and “GameScene” as the issue is that the 3rd+ clients Character Selection Manager and character prefab seems to be stuck and not depsawning correctly.

I checked your project and it worked but I didn’t see any large difference or errors, since Its working for only 2 people but not 3+ it might be some internal error and that’s whats hard about solving it.

There were other issues with Vivox and Quantum, linking the Visual Studio and not recognising NGO and other packages. I did Reimport All and that completely broke it, there were Plastic errors, no Assets were listed, trying to open Package Manager gave an error. I gave up at that point. :smiley:
It may not have helped that there’s no .gitignore file so a ton of unneeded files are included, and that may be further confusing it opening what was a Windows project on a Mac. Unity also crashes if I delete the Library folder.

I would try separating it out as best you can. I’ll see if I can make sense of it but Unity is feeling very unstable (the assets disappeared again without a re-import) and this is a pretty old Macbook I use and in Unity 6 at the best of times it’s slow going. I’ll be ordering a new M4 Macbook before Black Friday ends though. :slight_smile:

You could also try creating a new Github repo with the .gitignore file, Github Desktop should give the option of using one made for Unity.

In my example I did have a host and 2 clients, I didn’t see a need to add more as I didn’t expect to see any issues but it’s easy enough to add more clones.