Memory Leaks when calling Runner.RegisterUniqueObjects() in a custom INetworkSceneObjectProvider

Hello !

I tried creating a custom INetworkSceneObjectProvider to sync everything when I load and unload scenes.

But when I try to load a new level with this system, the application crashes when I call Runner.RegisterUniqueObjects() but ONLY for the host. I checked the content passed in and it is exactly the same content I passed in on the clients. 😱

I know, for sure, that my LevelData and its callbacks work locally (and on the clients), so I should be able to call correctly Runner.InvokeSceneLoadStart() and Runner.InvokeSceneLoadDone() when I start loading and when I receive the finish callback. It seems like it is working correctly on clients. 🤔

I was using lots of coroutines, I suspected that it may be the source of my problem, that is why I moved every important calls in the update method. But it did change nothing 😥


I also had a question about the Initialize() method, I am not really sure if I am supposed to call Runner.InvokeSceneLoadStart() and Runner.InvokeSceneLoadDone() when filling my lists, but since it was not done in the provided base classes, I did not.


Here is my class, you have to call LoadLevel() and passing in a LevelData to load a level :

using Fusion;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;


namespace MOtter.Network
{
    public class NetworkedSceneManager : Fusion.Behaviour, INetworkSceneObjectProvider
    {
        private Dictionary<Guid, NetworkObject> m_sceneNetworkedObjects = new Dictionary<Guid, NetworkObject>();
        private List<SceneRef> m_currentScenes = new List<SceneRef>();
        private bool m_isLoading = false;
        public NetworkRunner Runner { get; private set; }


        #region Cache Values for calls in update()
        private bool m_initializationAsked = false;

        private bool m_loadLevelAsked = false;
        private LevelData.LevelData m_levelToLoad = null;

        private bool m_onLevelLoadedAsked = false;
        #endregion


        #region Called by Fusion
        public void Initialize(NetworkRunner runner)
        {
            Runner = runner;
            Debug.LogError("Initializing asked");
            m_initializationAsked = true;
        }

        public bool IsReady(NetworkRunner runner)
        {
            Assert.Check(Runner == runner);
            return !m_isLoading;
        }


        public void Shutdown(NetworkRunner runner)
        {
            Assert.Check(Runner == runner);
            m_sceneNetworkedObjects.Clear();
            m_currentScenes.Clear();
            m_isLoading = false;
            Runner = null;
        }


        public bool TryResolveSceneObject(NetworkRunner runner, Guid objectGuid, out NetworkObject instance)
        {
            Assert.Check(Runner == runner);
            return m_sceneNetworkedObjects.TryGetValue(objectGuid, out instance);
        }
        #endregion

        private void Update()
        {
            if(m_initializationAsked)
            {
                Debug.LogError("Initializing");
                FetchSceneRefsAndNetworkedObjects();
                m_initializationAsked = false;
            }


            if(m_loadLevelAsked)
            {
                Debug.LogError("LoadLevel");
                
                m_isLoading = true;
                m_levelToLoad.OnLoadingEnded += OnLoadingEnded;
                Runner.InvokeSceneLoadStart();
                m_levelToLoad.LoadLevel();
                m_loadLevelAsked = false;
            }


            if(m_onLevelLoadedAsked)
            {
                Debug.LogError("On Level Loaded treatment");
                FetchSceneRefsAndNetworkedObjects();
                RegisterNetObjects();
                Runner.InvokeSceneLoadDone();
                m_isLoading = false;
                m_onLevelLoaded?.Invoke();
                m_onLevelLoaded = null;


                m_onLevelLoadedAsked = false;
            }
        }


        #region Actions
        private Action m_onLevelLoaded = null;
        public void LoadLevel(LevelData.LevelData p_levelData, Action a_onLevelLoaded = null)
        {
            Debug.LogError("LoadLevel asked");
            m_onLevelLoaded = a_onLevelLoaded;
            m_levelToLoad = p_levelData;
            m_loadLevelAsked = true;
        }
        #endregion


        private void OnLoadingEnded(LevelData.LevelData p_levelData)
        {
            Debug.LogError("OnLevelLoaded treatment asked");
            p_levelData.OnLoadingEnded -= OnLoadingEnded;
            m_onLevelLoadedAsked = true;
        }


        #region Utils
        private void FetchSceneRefsAndNetworkedObjects()
        {
            // Fetching SceneRefs
            m_currentScenes.Clear();
            for(int i = 0; i < SceneManager.sceneCount; ++i)
            {
                Debug.Log("Scene "+SceneManager.GetSceneAt(i).name+" avec index : "+SceneManager.GetSceneAt(i).buildIndex);
                m_currentScenes.Add(SceneManager.GetSceneAt(i).buildIndex);
            }


            // Fetching Networked Objects
            m_sceneNetworkedObjects.Clear();
            var allNetworkedObjects = FindObjectsOfType<NetworkObject>();
            for(int i = 0; i < allNetworkedObjects.Length; ++i)
            {
                m_sceneNetworkedObjects.Add(allNetworkedObjects[i].NetworkGuid, allNetworkedObjects[i]);
            }  
        }


        private void RegisterNetObjects()
        {
            // This makes the host crash when trying to load a level
            Debug.LogError("This makes the host crash when trying to load a level");
            if (m_sceneNetworkedObjects.Values.Count > 0)
            {
                Runner.RegisterUniqueObjects(m_sceneNetworkedObjects.Values);
            }
        }

        #endregion
    }
}