MPPM error on SceneLoad single with Distributed Authority

Hello,

For reference, i am using Unity 6.0.26f, NGO 2.2.0 and MPPM 1.3.3

i am experiencing a very weird issue that i cannot seem to find answers about. I am in a Distributed Authority setup, and i am switching scene from a lobby scene to a game scene. All connected players have a prefab, spawned using the following code in a NetworkBehaviour of the Lobby scene.

protected void Start()
{
    var player = Instantiate(_playerPrefab,Vector3.zero, Quaternion.identity);
    player.GetComponent<NetworkObject>().Spawn(true);
}

At some point, i switch scene using:

if (IsSessionOwner)
 {
     NetworkManager.Singleton.SceneManager.LoadScene(_sceneName, LoadSceneMode.Single);
}

The scene loads correctly for the session owner, but ONLY when i am using three or more MPPM sessions, i am getting this error on all non-owner clients:

[Netcode] [Invalid Destroy][SpectatorPrefab(Clone)][NetworkObjectId:10001] Destroy a spawned NetworkObject on a non-owner client is not valid during a distributed authority session. Call Destroy or Despawn on the client-owner instead.
UnityEngine.Debug:LogError (object)
Unity.Netcode.NetworkLog:LogError (string) (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs:34)
Unity.Netcode.NetworkObject:OnDestroy () (at ./Library/PackageCache/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:1608)

After investigating The id of the network objects mentionned by the error, the non-owner clients are trying to destroy the prefabs of other players. After reading the doc very very carefully, i am quite certain that i should not have to handle prefabs destruction on scene change, so i am very confused and don’t know where to find more help.

1 Like

I’m facing a similar issue. I’m manually spawning a set of networked manager objects in a “Core” scene which never gets unloaded, this is done by the session owner. When a new client joins with the same “Core” scene loaded + gameplay scene, they try to destroy the manually spawned manager objects present in “Core”, even if they don’t own them. I’m using custom scene management.

DontDestroyOnLoad seems to fix this issue, but its an annoying workaround.

My case is perhaps different. I ended up researching this issue a bit and created a test project, found some workarounds. For this specific issue, perhaps these links are interesting to look at:

The threads for first two links have exactly the same titles :smiley: The discussion in linked GitHub issue offers some suggestions which might be worth looking into.

In my case I’m using custom scene management so its a bit different. In-case anyone ends up facing a similar issue, here is what I found:

  • When you spawn a NetworkedObject, on the Owner it appears in the scene you spawned it at, in my case the “Core” scene where I spawn a bunch of managers
  • For other players on connection, these manager objects will spawn in the active scene
  • If the host and the client don’t the same active scene up and running, the object will get destroyed immediately on the client

A workaround for this is using such a script and plopping it on all networked objects. It will move the spawned object to a chosen scene on host and clients. You just need to make sure both host and client have that scene loaded. In my case - I’ve created a special additive “Networked” scene where I spawn all networked objects for easy tracking.

internal sealed class GameObjectSceneMover : MonoBehaviour
{
    [FoldoutGroup("Scenes", Expanded = true)]
    [SerializeField]
    [Required]
    private ScriptableScene scene;

    private void Awake()
    {
        if (scene == false)
        {
            Debug.LogError($"{nameof(scene)} is not set, object will not be moved", this);
            Destroy(this);
            return;
        }

        if (scene.IsValid == false)
        {
            Debug.LogError($"{nameof(scene)} is invalid, object will not be moved", this);
            Destroy(this);
            return;
        }

        var unityScene = SceneManager.GetSceneByPath(scene.ScenePath);
        if (unityScene == gameObject.scene)
        {
            Destroy(this);
            return;
        }

        SceneManager.MoveGameObjectToScene(gameObject, unityScene);
        Destroy(this);
    }
}

You can replace ScriptableScene with int sceneBuildIndex and use that to load scenes instead. In my case I’m using SOs to store scene info.

Note, I’m using custom scene management, so in my case clients can have a different set of scenes loaded! The “Core” and “Networked” scene in my case always stays loaded for all clients though…

For player prefabs or player specific NetworkObjects that you are instantiating and then spawning, you should make sure of the following:

  1. It does not have the distributable permission setting (which happens to be default). I would recommend making the permission “None” for any player only spawned objects. You can change permissions after a NetworkObject is spawned as well.
  2. Make sure NetworkObject.DestroyWithScene is set to false.

With the script you provided, player.GetComponent<NetworkObject>().Spawn(true) will spawn a NetworkObject that will be destroyed if a new active scene is loaded.

I would also make sure that you only spawn players when they are actually approved by the service which you can subscribe to the NetworkManager.OnConnectionEvent to do this.

Alternately, if you are wanting to spawn a unique player prefab you can assign a callback to NetworkManager.OnFetchLocalPlayerPrefabToSpawn which, as long as the AutoSpawnPlayerPrefabClientSide setting is checked, it will be invoked and you can return whatever registered network prefab you want for that client. This is a distributed authority only feature.

Let me know if this helps you better understand the issue you ran into and if any of the solutions I provided resolved your issue?