From b7de156566fa60c6e15ec5f3ab8c6a794db83310 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 23 Aug 2021 19:06:35 +0100 Subject: [PATCH 1/3] redo #1030 --- .../SceneManagement/NetworkSceneManager.cs | 105 ++++-- .../Tests/Runtime/NetworkSceneManagerTests.cs | 45 --- .../Tests/Runtime/NetworkSceneManagerTests.cs | 304 ++++++++++++++++++ .../Runtime/NetworkSceneManagerTests.cs.meta | 0 .../Assets/Tests/Runtime/SceneLoadingTest.cs | 262 --------------- .../Tests/Runtime/SceneLoadingTest.cs.meta | 11 - 6 files changed, 391 insertions(+), 336 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs create mode 100644 testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs rename {com.unity.netcode.gameobjects => testproject/Assets}/Tests/Runtime/NetworkSceneManagerTests.cs.meta (100%) delete mode 100644 testproject/Assets/Tests/Runtime/SceneLoadingTest.cs delete mode 100644 testproject/Assets/Tests/Runtime/SceneLoadingTest.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 391fc67b43..42d5110639 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -96,6 +96,10 @@ public class NetworkSceneManager /// private static bool s_IsSceneEventActive = false; +#if UNITY_EDITOR || DEVELOPMENT_BUILD + private bool m_IsRunningUnitTest = SceneManager.GetActiveScene().name.StartsWith("InitTestScene"); +#endif + /// /// The delegate callback definition for scene event notifications /// For more details review over and @@ -538,11 +542,23 @@ private void OnClientUnloadScene() $"because the client scene handle {sceneHandle} was not found in ScenesLoaded!"); } s_IsSceneEventActive = true; - - var sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneHandle]); - + var sceneUnload = (AsyncOperation)null; +#if UNITY_EDITOR || DEVELOPMENT_BUILD + // In unit tests, we don't allow clients to unload scenes since + // MultiInstance unit tests share the same scene space. + if (m_IsRunningUnitTest) + { + sceneUnload = new AsyncOperation(); + } + else + { + sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneHandle]); + sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); + } +#else + sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneHandle]); sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); - +#endif ScenesLoaded.Remove(sceneHandle); // Remove our server to scene handle lookup @@ -559,6 +575,11 @@ private void OnClientUnloadScene() }); +#if UNITY_EDITOR + OnSceneUnloaded(); +#endif + + } /// @@ -704,6 +725,35 @@ private void OnClientSceneLoadingEvent(Stream objectStream) return; } +#if UNITY_EDITOR || DEVELOPMENT_BUILD + // In unit tests, we don't allow clients to load additional scenes since + // MultiInstance unit tests share the same scene space. + if (m_IsRunningUnitTest) + { + // Send the loading message + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = new AsyncOperation(), + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId + }); + + // Unit tests must mirror the server's scenes loaded dictionary, otherwise this portion will fail + if (ScenesLoaded.ContainsKey(SceneEventData.SceneHandle)) + { + OnClientLoadedScene(ScenesLoaded[SceneEventData.SceneHandle]); + } + else + { + throw new Exception($"Could not find the scene handle {SceneEventData.SceneHandle} for scene {GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex)} " + + $"during unit test. Did you forget to register this in the unit test?"); + } + return; + } +#endif + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { // Move ALL NetworkObjects to the temp scene @@ -973,25 +1023,44 @@ private void OnClientBeginSync() ScenePlacedObjects.Clear(); } + var shouldPassThrough = false; + var sceneLoad = (AsyncOperation)null; + // Check to see if the client already has loaded the scene to be loaded - if (sceneName != activeScene.name) + if (sceneName == activeScene.name) { - // If not, then load the scene - var sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); - - // Notify local client that a scene load has begun - OnSceneEvent?.Invoke(new SceneEvent() - { - AsyncOperation = sceneLoad, - SceneEventType = SceneEventData.SceneEventTypes.S2C_Load, - LoadSceneMode = loadSceneMode, - SceneName = sceneName, - ClientId = m_NetworkManager.LocalClientId, - }); + // If the client is already in the same scene, then pass through and + // don't try to reload it. + shouldPassThrough = true; + } +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (m_IsRunningUnitTest) + { + // In unit tests, we don't allow clients to load additional scenes since + // MultiInstance unit tests share the same scene space. + shouldPassThrough = true; + sceneLoad = new AsyncOperation(); + } +#endif + if (!shouldPassThrough) + { + // If not, then load the scene + sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex, sceneHandle); } - else + + // Notify local client that a scene load has begun + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = sceneLoad, + SceneEventType = SceneEventData.SceneEventTypes.S2C_Load, + LoadSceneMode = loadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId, + }); + + if(shouldPassThrough) { // If so, then pass through ClientLoadedSynchronization(sceneIndex, sceneHandle); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs deleted file mode 100644 index c5c5761bdf..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.Netcode.RuntimeTests -{ - public class NetworkSceneManagerTests - { - [Test] - public void SwitchSceneWithoutSceneManagement() - { - //Only used to create a network object based game asset - Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out NetworkManager networkManager)); - var threwException = false; - try - { - networkManager.SceneManager.LoadScene("SomeSceneNane", UnityEngine.SceneManagement.LoadSceneMode.Single); - } - catch (Exception ex) - { - if (ex.Message.Contains($"{nameof(NetworkConfig.EnableSceneManagement)} flag is not enabled in the {nameof(NetworkManager)}'s {nameof(NetworkConfig)}. " + - $"Please set {nameof(NetworkConfig.EnableSceneManagement)} flag to true before " + - $"calling {nameof(NetworkSceneManager.LoadScene)} or {nameof(NetworkSceneManager.UnloadScene)}.")) - { - threwException = true; - } - } - - Assert.IsTrue(threwException); - } - - [SetUp] - public void Setup() - { - //Create, instantiate, and host - NetworkManagerHelper.StartNetworkManager(out _); - } - - [TearDown] - public void TearDown() - { - //Stop, shutdown, and destroy - NetworkManagerHelper.ShutdownNetworkManager(); - } - } -} diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs new file mode 100644 index 0000000000..8bfae869b2 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; +using Unity.Netcode.RuntimeTests; +using Unity.Netcode; + +namespace TestProject.RuntimeTests +{ + public class NetworkSceneManagerTests: BaseMultiInstanceTest + { + protected override int NbClients => 9; + + [UnitySetUp] + public override IEnumerator Setup() + { + m_ShouldWaitList = new List(); + return base.Setup(); + } + + [UnityTearDown] + public override IEnumerator Teardown() + { + return base.Teardown(); + } + + private class SceneTestInfo + { + public bool ShouldWait; + public bool ProcessedEvent; + public ulong ClientId; + } + + private float m_TimeOutMarker; + private bool m_TimedOut; + private string m_CurrentSceneName; + private List m_ShouldWaitList; + private Scene m_CurrentScene; + + + [UnityTest] + public IEnumerator SceneLoadingAndNotifications() + { + m_ServerNetworkManager.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; + m_CurrentSceneName = "AdditiveScene1"; + + // Check that we cannot call LoadScene when EnableSceneManagement is false (from previous legacy test) + var threwException = false; + try + { + m_ServerNetworkManager.NetworkConfig.EnableSceneManagement = false; + m_ServerNetworkManager.SceneManager.LoadScene(m_CurrentSceneName, LoadSceneMode.Single); + } + catch (Exception ex) + { + if (ex.Message.Contains($"{nameof(NetworkConfig.EnableSceneManagement)} flag is not enabled in the {nameof(NetworkManager)}'s {nameof(NetworkConfig)}. " + + $"Please set {nameof(NetworkConfig.EnableSceneManagement)} flag to true before calling " + + $"{nameof(NetworkSceneManager.LoadScene)} or {nameof(NetworkSceneManager.UnloadScene)}.")) + { + threwException = true; + } + } + Assert.IsTrue(threwException); + + m_ServerNetworkManager.NetworkConfig.EnableSceneManagement = true; + + // Check that a client cannot call LoadScene + threwException = false; + try + { + m_ClientNetworkManagers[0].SceneManager.LoadScene(m_CurrentSceneName, LoadSceneMode.Single); + } + catch (Exception ex) + { + if (ex.Message.Contains("Only server can start a scene event!")) + { + threwException = true; + } + } + Assert.IsTrue(threwException); + + + // Now prepare for the loading and unloading additive scene testing + InitializeSceneTestInfo(); + + // Test loading additive scenes and the associated event messaging and notification pipelines + ResetWait(); + var result = m_ServerNetworkManager.SceneManager.LoadScene(m_CurrentSceneName, LoadSceneMode.Additive); + Assert.True(result == SceneEventProgressStatus.Started); + + // Check error status for trying to load during an already in progress scene event + result = m_ServerNetworkManager.SceneManager.LoadScene(m_CurrentSceneName, LoadSceneMode.Additive); + Assert.True(result == SceneEventProgressStatus.SceneEventInProgress); + + // Wait for all clients to load the scene + yield return new WaitWhile(ShouldWait); + Assert.IsFalse(m_TimedOut); + + // Test unloading additive scenes and the associated event messaging and notification pipelines + ResetWait(); + + // Check that a client cannot call UnloadScene + threwException = false; + try + { + m_ClientNetworkManagers[0].SceneManager.UnloadScene(m_CurrentScene); + } + catch (Exception ex) + { + if (ex.Message.Contains("Only server can start a scene event!")) + { + threwException = true; + } + } + Assert.IsTrue(threwException); + + result = m_ServerNetworkManager.SceneManager.UnloadScene(m_CurrentScene); + Assert.True(result == SceneEventProgressStatus.Started); + + yield return new WaitWhile(ShouldWait); + Assert.IsFalse(m_TimedOut); + + // Check error status for trying to unloading something not loaded + ResetWait(); + result = m_ServerNetworkManager.SceneManager.UnloadScene(m_CurrentScene); + Assert.True(result == SceneEventProgressStatus.SceneNotLoaded); + + // Check error status for trying to load an invalid scene name + result = m_ServerNetworkManager.SceneManager.LoadScene("SomeInvalidSceneName", LoadSceneMode.Additive); + Assert.True(result == SceneEventProgressStatus.InvalidSceneName); + + yield break; + } + + /// + /// Initializes the m_ShouldWaitList + /// + private void InitializeSceneTestInfo() + { + m_ShouldWaitList.Add(new SceneTestInfo() { ClientId = m_ServerNetworkManager.ServerClientId, ShouldWait = false }); + if (!m_ServerNetworkManager.NetworkConfig.RegisteredScenes.Contains(m_CurrentSceneName)) + { + m_ServerNetworkManager.NetworkConfig.AllowRuntimeSceneChanges = true; + m_ServerNetworkManager.SceneManager.AddRuntimeSceneName(m_CurrentSceneName, (uint)(m_ServerNetworkManager.NetworkConfig.RegisteredScenes.Count + 1)); + } + + foreach (var manager in m_ClientNetworkManagers) + { + m_ShouldWaitList.Add(new SceneTestInfo() { ClientId = manager.LocalClientId, ShouldWait = false }); + if (!manager.NetworkConfig.RegisteredScenes.Contains(m_CurrentSceneName)) + { + manager.NetworkConfig.AllowRuntimeSceneChanges = true; + manager.SceneManager.AddRuntimeSceneName(m_CurrentSceneName, (uint)(manager.NetworkConfig.RegisteredScenes.Count + 1)); + } + } + } + + /// + /// Resets each SceneTestInfo entry + /// + private void ResetWait() + { + foreach (var entry in m_ShouldWaitList) + { + entry.ShouldWait = true; + entry.ProcessedEvent = false; + } + + m_TimeOutMarker = Time.realtimeSinceStartup + 4.0f; + m_TimedOut = false; + } + + /// + /// Wait until all clients have processed the event and the server has determined the event is completed + /// Will bail if it takes too long via m_TimeOutMarker + /// + /// + private bool ShouldWait() + { + m_TimedOut = m_TimeOutMarker < Time.realtimeSinceStartup; + return (m_ShouldWaitList.Select(c => c).Where(c => c.ProcessedEvent != true && c.ShouldWait == true).Count() > 0) && !m_TimedOut; + } + + /// + /// Determines if the clientId is valid + /// + /// + /// + private bool ContainsClient(ulong clientId) + { + return m_ShouldWaitList.Select(c => c.ClientId).Where(c => c == clientId).Count() > 0; + } + + /// + /// Sets the specific clientId entry as having processed the current event + /// + /// + private void SetClientProcessedEvent(ulong clientId) + { + m_ShouldWaitList.Select(c => c).Where(c => c.ClientId == clientId).First().ProcessedEvent = true; + } + + /// + /// Sets all known clients' ShouldWait value to false + /// + /// + private void SetClientWaitDone(List clients) + { + foreach (var clientId in clients) + { + m_ShouldWaitList.Select(c => c).Where(c => c.ClientId == clientId).First().ShouldWait = false; + } + } + + /// + /// Makes sure the ClientsThatCompleted in the scene event complete notification match the known client identifiers + /// + /// list of client identifiers (ClientsThatCompleted) + /// true or false + private bool ContainsAllClients(List clients) + { + // First, make sure we have the expected client count + if(clients.Count != m_ShouldWaitList.Count) + { + return false; + } + + // Next, make sure we have all client identifiers + foreach(var sceneTestInfo in m_ShouldWaitList) + { + if(!clients.Contains(sceneTestInfo.ClientId)) + { + return false; + } + } + return true; + } + + /// + /// This test only needs to check the server side for the proper event notifications of loading a scene, each + /// client response that it loaded the scene, and the final event notifications S2C_LoadComplete and S2C_UnloadComplete + /// that signify all clients have processed through the loading and unloading process. + /// + /// + private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) + { + switch(sceneEvent.SceneEventType) + { + case SceneEventData.SceneEventTypes.S2C_Load: + case SceneEventData.SceneEventTypes.S2C_Unload: + { + Assert.AreEqual(sceneEvent.SceneName,m_CurrentSceneName); + Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); + Assert.IsNotNull(sceneEvent.AsyncOperation); + break; + } + case SceneEventData.SceneEventTypes.C2S_LoadComplete: + { + if (sceneEvent.ClientId == m_ServerNetworkManager.ServerClientId) + { + m_CurrentScene = sceneEvent.Scene; + var sceneHandle = m_CurrentScene.handle; + foreach (var manager in m_ClientNetworkManagers) + { + if (!manager.SceneManager.ScenesLoaded.ContainsKey(sceneHandle)) + { + manager.SceneManager.ScenesLoaded.Add(sceneHandle, m_CurrentScene); + } + + if (!manager.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(m_CurrentScene.handle)) + { + manager.SceneManager.ServerSceneHandleToClientSceneHandle.Add(m_CurrentScene.handle, m_CurrentScene.handle); + } + } + } + Assert.AreEqual(sceneEvent.SceneName, m_CurrentSceneName); + Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); + SetClientProcessedEvent(sceneEvent.ClientId); + break; + } + case SceneEventData.SceneEventTypes.C2S_UnloadComplete: + { + Assert.AreEqual(sceneEvent.SceneName, m_CurrentSceneName); + Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); + SetClientProcessedEvent(sceneEvent.ClientId); + break; + } + case SceneEventData.SceneEventTypes.S2C_LoadComplete: + case SceneEventData.SceneEventTypes.S2C_UnLoadComplete: + { + Assert.AreEqual(sceneEvent.SceneName, m_CurrentSceneName); + Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); + Assert.IsTrue(ContainsAllClients(sceneEvent.ClientsThatCompleted)); + SetClientWaitDone(sceneEvent.ClientsThatCompleted); + break; + } + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs.meta b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs.meta similarity index 100% rename from com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs.meta rename to testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs.meta diff --git a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs deleted file mode 100644 index 6640ecb89c..0000000000 --- a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs +++ /dev/null @@ -1,262 +0,0 @@ -#if IGNORETHISTEST -using System.Collections; -using UnityEngine; -using NUnit.Framework; -using UnityEngine.TestTools; -using UnityEngine.SceneManagement; -using Unity.Netcode; - -namespace TestProject.RuntimeTests -{ - - /// - /// This is nothing more than a template to follow in order to - /// use a scene to configure your NetworkManager as well as how - /// to switching between scenes without blowing away the temporary - /// test runner scene. This also shows how to switch scenes using - /// NetworkSceneManager during a unit test. - /// - public class SceneLoadingTest - { - - private NetworkManager m_NetworkManager; - - private bool m_SceneLoaded; - private bool m_TimedOut; - private string m_TargetSceneNameToLoad; - private Scene m_LoadedScene; - - [UnityTest] - public IEnumerator SceneLoading() - { - - - - // Keep track of the original test scene - Scene originalScene = SceneManager.GetActiveScene(); - - // Load the first scene with the predefined NetworkManager - m_TargetSceneNameToLoad = "SceneTransitioningTest"; - - SceneManager.sceneLoaded += SceneManager_sceneLoaded; - SceneManager.LoadScene(m_TargetSceneNameToLoad, LoadSceneMode.Additive); - - // Wait for the scene to load - var timeOut = Time.realtimeSinceStartup + 5; - m_TimedOut = false; - while (!m_SceneLoaded) - { - yield return new WaitForSeconds(0.01f); - if (timeOut < Time.realtimeSinceStartup) - { - m_TimedOut = true; - break; - } - } - - // Verify it loaded - Assert.IsFalse(m_TimedOut); - Assert.IsTrue(m_SceneLoaded); - Assert.NotNull(m_LoadedScene); - - // Keep track of the scene we just loaded - Scene primaryScene = m_LoadedScene; - - // Set the scene to be active if it is not the active scene. - // (This is to assure spawned objects instantiate in the newly loaded scene) - if (SceneManager.GetActiveScene().name != m_LoadedScene.name) - { - primaryScene = m_LoadedScene; - Debug.Log($"Loaded scene not active, activating scene {m_TargetSceneNameToLoad}"); - SceneManager.SetActiveScene(m_LoadedScene); - Assert.IsTrue(SceneManager.GetActiveScene().name == m_LoadedScene.name); - } - - // No longer need to be notified of this event - SceneManager.sceneLoaded -= SceneManager_sceneLoaded; - - // Get the NetworkManager instantiated from the scene - var gameObject = GameObject.Find("[NetworkManager]"); - Assert.IsNotNull(gameObject); - - m_NetworkManager = gameObject.GetComponent(); - - - - Assert.IsNotNull(m_NetworkManager); - - // Start in host mode - if (m_NetworkManager) - { - m_NetworkManager.StartHost(); - } - - - // Next, we want to do a scene transition using NetworkSceneManager - m_TargetSceneNameToLoad = "SecondSceneToLoad"; - - // Reference code for adding scenes not included in build settings: - // Assure we are allowing runtime scene changes - // m_NetworkManager.NetworkConfig.AllowRuntimeSceneChanges = true; - // m_NetworkManager.SceneManager.AddRuntimeSceneName(m_TargetSceneNameToLoad, (uint)m_NetworkManager.SceneManager.RegisteredSceneNames.Count); - - // Switch the scene using NetworkSceneManager - var sceneSwitchProgress = m_NetworkManager.SceneManager.SwitchScene(m_TargetSceneNameToLoad, LoadSceneMode.Additive); - - sceneSwitchProgress.OnComplete += SwitchSceneProgress_OnComplete; - m_SceneLoaded = false; - - // Wait for the scene to load - timeOut = Time.realtimeSinceStartup + 30; - m_TimedOut = false; - while (!m_SceneLoaded) - { - yield return new WaitForSeconds(0.01f); - if (timeOut < Time.realtimeSinceStartup) - { - m_TimedOut = true; - break; - } - } - - // Make sure we didn't time out and the scene loaded - Assert.IsFalse(m_TimedOut); - Assert.IsTrue(m_SceneLoaded); - Assert.NotNull(m_LoadedScene); - - sceneSwitchProgress.OnComplete -= SwitchSceneProgress_OnComplete; - - // Set the scene to be active if it is not the active scene - // (This is to assure spawned objects instantiate in the newly loaded scene) - if (!SceneManager.GetActiveScene().name.Contains(m_LoadedScene.name)) - { - - Debug.Log($"Loaded scene not active, activating scene {m_TargetSceneNameToLoad}"); - SceneManager.SetActiveScene(m_LoadedScene); - Assert.IsTrue(SceneManager.GetActiveScene().name == m_LoadedScene.name); - } - - m_SceneLoaded = false; - primaryScene = m_LoadedScene; - m_NetworkManager.SceneManager.OnAdditiveSceneEvent += SceneManager_OnAdditiveSceneEvent; - m_NetworkManager.SceneManager.UnloadScene(primaryScene.name); - - //// Now unload the previous scene - //SceneManager.UnloadSceneAsync(primaryScene).completed += UnloadAsync_completed; - - // Now track the newly loaded and currently active scene - primaryScene = SceneManager.GetActiveScene(); - - // Wait for the previous scene to unload - timeOut = Time.realtimeSinceStartup + 5; - while (!m_SceneLoaded) - { - yield return new WaitForSeconds(0.01f); - if (timeOut < Time.realtimeSinceStartup) - { - m_TimedOut = true; - break; - } - } - Assert.IsFalse(m_TimedOut); - - m_NetworkManager.SceneManager.OnAdditiveSceneEvent -= SceneManager_OnAdditiveSceneEvent; - - // Set the original Test Runner Scene to be the active scene - SceneManager.SetActiveScene(originalScene); - // Unload the previously active scene - SceneManager.UnloadSceneAsync(primaryScene).completed += UnloadAsync_completed; - - // Wait for the scene to unload - timeOut = Time.realtimeSinceStartup + 5; - while (!m_SceneLoaded) - { - yield return new WaitForSeconds(0.01f); - if (timeOut < Time.realtimeSinceStartup) - { - m_TimedOut = true; - break; - } - } - Assert.IsFalse(m_TimedOut); - - m_NetworkManager.DontDestroy = false; - SceneManager.MoveGameObjectToScene(m_NetworkManager.gameObject, originalScene); - // We are now done with the NetworkSceneManager switch scene test so stop the host - m_NetworkManager.StopHost(); - - Object.DestroyImmediate(m_NetworkManager.gameObject); - } - - private void SceneManager_OnAdditiveSceneEvent(AsyncOperation operation, string sceneName, bool isLoading) - { - if(!isLoading) - { - m_SceneLoaded = true; - } - } - - private void SceneManager_sceneUnloaded(Scene arg0) - { - m_SceneLoaded = true; - } - - /// - /// Checks to make sure the scene unloaded - /// - /// - private void UnloadAsync_completed(AsyncOperation obj) - { - m_SceneLoaded = true; - } - - /// - /// NetworkSceneManager switch scene progress OnComplete event - /// - /// - private void SwitchSceneProgress_OnComplete(bool timedOut) - { - m_TimedOut = timedOut; - if (!m_TimedOut) - { - var scene = SceneManager.GetActiveScene(); - - if (scene != null && m_TargetSceneNameToLoad.Contains(scene.name)) - { - m_SceneLoaded = true; - m_LoadedScene = scene; - } - } - } - - /// - /// When invoked, makes sure the scene loaded is the correct scene - /// and then set our scene loaded to true and keep a reference to the loaded scene - /// - private void SceneManager_sceneLoaded(Scene scene, LoadSceneMode sceneMode) - { - if (scene != null && m_TargetSceneNameToLoad.Contains(scene.name)) - { - m_SceneLoaded = true; - m_LoadedScene = scene; - } - } - - [UnitySetUp] - private IEnumerator SetUp() - { - - yield return null; - } - - [UnityTearDown] - private IEnumerator TearDown() - { - - - yield return null; - } - } - -} -#endif diff --git a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs.meta b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs.meta deleted file mode 100644 index e953b5bab3..0000000000 --- a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 129aa4002292479489be6e1e547c33d2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From ce71ed4a92bd3253a8a69e81ed5822b518777a8d Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 23 Aug 2021 19:42:56 +0100 Subject: [PATCH 2/3] comment a todo for `m_IsRunningUnitTest` --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 42d5110639..7028eea2dc 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -96,8 +96,10 @@ public class NetworkSceneManager /// private static bool s_IsSceneEventActive = false; + // TODO: Remove `m_IsRunningUnitTest` entirely after we switch to multi-process testing + // In MultiInstance tests, we cannot allow clients to load additional scenes as they're sharing the same scene space / Unity instance. #if UNITY_EDITOR || DEVELOPMENT_BUILD - private bool m_IsRunningUnitTest = SceneManager.GetActiveScene().name.StartsWith("InitTestScene"); + private readonly bool m_IsRunningUnitTest = SceneManager.GetActiveScene().name.StartsWith("InitTestScene"); #endif /// @@ -544,8 +546,6 @@ private void OnClientUnloadScene() s_IsSceneEventActive = true; var sceneUnload = (AsyncOperation)null; #if UNITY_EDITOR || DEVELOPMENT_BUILD - // In unit tests, we don't allow clients to unload scenes since - // MultiInstance unit tests share the same scene space. if (m_IsRunningUnitTest) { sceneUnload = new AsyncOperation(); @@ -726,8 +726,6 @@ private void OnClientSceneLoadingEvent(Stream objectStream) } #if UNITY_EDITOR || DEVELOPMENT_BUILD - // In unit tests, we don't allow clients to load additional scenes since - // MultiInstance unit tests share the same scene space. if (m_IsRunningUnitTest) { // Send the loading message @@ -1060,7 +1058,7 @@ private void OnClientBeginSync() ClientId = m_NetworkManager.LocalClientId, }); - if(shouldPassThrough) + if (shouldPassThrough) { // If so, then pass through ClientLoadedSynchronization(sceneIndex, sceneHandle); From 090763aeab20b0b6e2c34bc4fd4677d701041010 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 23 Aug 2021 15:35:05 -0500 Subject: [PATCH 3/3] test project Adding check for unit testing at the end of OnClientUnloadScene to determine if we need to just passthrough to the callback (for multiInstance unit testing only) Fixing minor issue with NetworkObject not being around after unloading a scene for NetworkPrefabPool. --- .../SceneManagement/NetworkSceneManager.cs | 9 +++++---- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 15 +++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 7028eea2dc..5f682adf3a 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -575,11 +575,12 @@ private void OnClientUnloadScene() }); -#if UNITY_EDITOR - OnSceneUnloaded(); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (m_IsRunningUnitTest) + { + OnSceneUnloaded(); + } #endif - - } /// diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index bd62dc4c9d..ec09ba7f6b 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -121,15 +121,18 @@ private void OnSceneEvent(SceneEvent sceneEvent) /// private void OnUnloadScene() { - if (IsServer) + if (NetworkObject != null && NetworkManager != null) { - StopCoroutine(SpawnObjects()); - } + if (IsServer) + { + StopCoroutine(SpawnObjects()); + } - // De-register the custom prefab handler - DeregisterCustomPrefabHandler(); + // De-register the custom prefab handler + DeregisterCustomPrefabHandler(); - CleanNetworkObjects(); + CleanNetworkObjects(); + } }