diff --git a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs index 563a2eeabd..0f6f162c70 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs @@ -21,7 +21,6 @@ public class NetworkManagerEditor : UnityEditor.Editor // NetworkConfig fields private SerializedProperty m_PlayerPrefabProperty; private SerializedProperty m_ProtocolVersionProperty; - private SerializedProperty m_AllowRuntimeSceneChangesProperty; private SerializedProperty m_NetworkTransportProperty; private SerializedProperty m_TickRateProperty; private SerializedProperty m_MaxObjectUpdatesPerTickProperty; @@ -37,7 +36,6 @@ public class NetworkManagerEditor : UnityEditor.Editor private SerializedProperty m_LoadSceneTimeOutProperty; private ReorderableList m_NetworkPrefabsList; - private ReorderableList m_RegisteredSceneAssetsList; private NetworkManager m_NetworkManager; private bool m_Initialized; @@ -91,8 +89,7 @@ private void Initialize() // NetworkConfig properties m_PlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative(nameof(NetworkConfig.PlayerPrefab)); - m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion"); - m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges"); + m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion"); m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport"); m_TickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("TickRate"); m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout"); @@ -121,7 +118,6 @@ private void CheckNullProperties() // NetworkConfig properties m_PlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative(nameof(NetworkConfig.PlayerPrefab)); m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion"); - m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges"); m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport"); m_TickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("TickRate"); m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout"); @@ -200,30 +196,6 @@ private void OnEnable() } }; m_NetworkPrefabsList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "NetworkPrefabs"); - - m_RegisteredSceneAssetsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.RegisteredSceneAssets)), true, true, true, true); - m_RegisteredSceneAssetsList.elementHeightCallback = index => - { - return EditorGUIUtility.singleLineHeight + 8; - }; - m_RegisteredSceneAssetsList.drawElementCallback = (rect, index, isActive, isFocused) => - { - rect.y += 5; - - var sceneAsset = m_RegisteredSceneAssetsList.serializedProperty.GetArrayElementAtIndex(index); - int firstLabelWidth = 24; - int padding = 2; - - EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), index.ToString()); - EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - padding, EditorGUIUtility.singleLineHeight), sceneAsset, GUIContent.none); - }; - - m_RegisteredSceneAssetsList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "NetworkScenes"); - - m_RegisteredSceneAssetsList.onAddCallback = (registeredList) => - { - m_NetworkManager.NetworkConfig.RegisteredSceneAssets.Add(null); - }; } public override void OnInspectorGUI() @@ -257,13 +229,6 @@ public override void OnInspectorGUI() m_NetworkPrefabsList.DoLayoutList(); EditorGUILayout.Space(); - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) - { - m_RegisteredSceneAssetsList.DoLayoutList(); - EditorGUILayout.Space(); - } - - EditorGUILayout.LabelField("General", EditorStyles.boldLabel); EditorGUILayout.PropertyField(m_ProtocolVersionProperty); @@ -330,7 +295,6 @@ public override void OnInspectorGUI() using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) { EditorGUILayout.PropertyField(m_LoadSceneTimeOutProperty); - EditorGUILayout.PropertyField(m_AllowRuntimeSceneChangesProperty); } serializedObject.ApplyModifiedProperties(); diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs index a123f16064..750c7ce77d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using UnityEngine; -#if UNITY_EDITOR -using UnityEditor; -#endif using System.Linq; namespace Unity.Netcode @@ -26,25 +23,6 @@ public class NetworkConfig [Tooltip("The NetworkTransport to use")] public NetworkTransport NetworkTransport = null; - /// - /// The list of SceneNames built from the RegisteredSceneAssets list - /// - [HideInInspector] - public List RegisteredScenes = new List(); - -#if UNITY_EDITOR - [Tooltip("The Scenes that can be switched to by the server")] - public List RegisteredSceneAssets = new List(); -#endif - - /// - /// Whether or not runtime scene changes should be allowed and expected. - /// If this is true, clients with different initial configurations will not work together. - /// - [Tooltip("Whether or not runtime scene changes should be allowed and expected.\n " + - "If this is true, clients with different initial configurations will not work together.")] - public bool AllowRuntimeSceneChanges = false; - /// /// The default player prefab /// @@ -173,12 +151,6 @@ public class NetworkConfig public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one) public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets) - - private void Sort() - { - RegisteredScenes.Sort(StringComparer.Ordinal); - } - /// /// Returns a base64 encoded version of the configuration /// @@ -189,13 +161,6 @@ public string ToBase64() using var buffer = PooledNetworkBuffer.Get(); using var writer = PooledNetworkWriter.Get(buffer); writer.WriteUInt16Packed(config.ProtocolVersion); - writer.WriteUInt16Packed((ushort)config.RegisteredScenes.Count); - - for (int i = 0; i < config.RegisteredScenes.Count; i++) - { - writer.WriteString(config.RegisteredScenes[i]); - } - writer.WriteInt32Packed(config.TickRate); writer.WriteInt32Packed(config.ClientConnectionBufferTimeout); writer.WriteBool(config.ConnectionApproval); @@ -208,7 +173,6 @@ public string ToBase64() writer.WriteBool(RecycleNetworkIds); writer.WriteSinglePacked(NetworkIdRecycleDelay); writer.WriteBool(EnableNetworkVariable); - writer.WriteBool(AllowRuntimeSceneChanges); writer.WriteBool(EnableNetworkLogs); buffer.PadBuffer(); @@ -225,16 +189,9 @@ public void FromBase64(string base64) byte[] binary = Convert.FromBase64String(base64); using var buffer = new NetworkBuffer(binary); using var reader = PooledNetworkReader.Get(buffer); - config.ProtocolVersion = reader.ReadUInt16Packed(); + config.ProtocolVersion = reader.ReadUInt16Packed(); ushort sceneCount = reader.ReadUInt16Packed(); - config.RegisteredScenes.Clear(); - - for (int i = 0; i < sceneCount; i++) - { - config.RegisteredScenes.Add(reader.ReadString().ToString()); - } - config.TickRate = reader.ReadInt32Packed(); config.ClientConnectionBufferTimeout = reader.ReadInt32Packed(); config.ConnectionApproval = reader.ReadBool(); @@ -247,7 +204,6 @@ public void FromBase64(string base64) config.RecycleNetworkIds = reader.ReadBool(); config.NetworkIdRecycleDelay = reader.ReadSinglePacked(); config.EnableNetworkVariable = reader.ReadBool(); - config.AllowRuntimeSceneChanges = reader.ReadBool(); config.EnableNetworkLogs = reader.ReadBool(); } @@ -266,25 +222,17 @@ public ulong GetConfig(bool cache = true) return m_ConfigHash.Value; } - Sort(); - using var buffer = PooledNetworkBuffer.Get(); using var writer = PooledNetworkWriter.Get(buffer); + writer.WriteUInt16Packed(ProtocolVersion); writer.WriteString(NetworkConstants.PROTOCOL_VERSION); - if (EnableSceneManagement && !AllowRuntimeSceneChanges) - { - for (int i = 0; i < RegisteredScenes.Count; i++) - { - writer.WriteString(RegisteredScenes[i]); - } - } - if (ForceSamePrefabs) { var sortedDictionary = NetworkPrefabOverrideLinks.OrderBy(x => x.Key); foreach (var sortedEntry in sortedDictionary) + { writer.WriteUInt32Packed(sortedEntry.Key); } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 0d3aa78b0b..4348809bb4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -266,17 +266,6 @@ private void OnValidate() } } - if (NetworkConfig.EnableSceneManagement) - { - foreach (var sceneAsset in NetworkConfig.RegisteredSceneAssets) - { - if (!NetworkConfig.RegisteredScenes.Contains(sceneAsset.name)) - { - NetworkConfig.RegisteredScenes.Add(sceneAsset.name); - } - } - } - var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); // If the scene is not dirty or the asset database is currently updating then we can skip updating the NetworkPrefab information @@ -453,25 +442,12 @@ private void Initialize(bool server) // Register INetworkUpdateSystem (always register this after messageQueueContainer has been instantiated) this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate); - if (NetworkConfig.EnableSceneManagement) - { - NetworkConfig.RegisteredScenes.Sort(StringComparer.Ordinal); - - for (int i = 0; i < NetworkConfig.RegisteredScenes.Count; i++) - { - SceneManager.RegisteredSceneNames.Add(NetworkConfig.RegisteredScenes[i]); - SceneManager.SceneIndexToString.Add((uint)i, NetworkConfig.RegisteredScenes[i]); - SceneManager.SceneNameToIndex.Add(NetworkConfig.RegisteredScenes[i], (uint)i); - } - } - // This is used to remove entries not needed or invalid var removeEmptyPrefabs = new List(); // Always clear our prefab override links before building NetworkConfig.NetworkPrefabOverrideLinks.Clear(); - // Build the NetworkPrefabOverrideLinks dictionary // Build the NetworkPrefabOverrideLinks dictionary for (int i = 0; i < NetworkConfig.NetworkPrefabs.Count; i++) { @@ -1536,8 +1512,6 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? } } - - /// /// Spawns the newly approved player /// diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 17061d854e..5e99d222a6 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -115,9 +115,25 @@ public class NetworkSceneManager /// public event SceneEventDelegate OnSceneEvent; - internal readonly HashSet RegisteredSceneNames = new HashSet(); - internal readonly Dictionary SceneNameToIndex = new Dictionary(); - internal readonly Dictionary SceneIndexToString = new Dictionary(); + /// Delegate declaration for the handler that provides + /// an additional level of scene loading security and/or validation to assure the scene being loaded + /// is valid scene to be loaded in the LoadSceneMode specified. + /// + /// Build Settings Scenes in Build List index of the scene + /// Name of the scene + /// LoadSceneMode the scene is going to be loaded + /// true (valid) or false (not valid) + public delegate bool VerifySceneBeforeLoadingDelegateHandler(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode); + + /// + /// Delegate handler defined by that is invoked before the + /// server or client loads a scene during an active netcode game session. + /// Client Side: In order for clients to be notified of this condition you must subscribe to the event. + /// Server Side: will return . + /// + public VerifySceneBeforeLoadingDelegateHandler VerifySceneBeforeLoading; + + internal readonly Dictionary SceneEventProgressTracking = new Dictionary(); /// @@ -151,6 +167,11 @@ public class NetworkSceneManager /// internal Dictionary ServerSceneHandleToClientSceneHandle = new Dictionary(); + /// + /// The scenes in the build without their path + /// + internal List ScenesInBuild = new List(); + /// /// The Condition: While a scene is asynchronously loaded in single loading scene mode, if any new NetworkObjects are spawned /// they need to be moved into the do not destroy temporary scene @@ -181,6 +202,29 @@ public class NetworkSceneManager internal Scene DontDestroyOnLoadScene; + /// + /// Gets the scene name from full path to the scene + /// + /// + internal string GetSceneNameFromPath(string scenePath) + { + var begin = scenePath.LastIndexOf("/", StringComparison.Ordinal) + 1; + var end = scenePath.LastIndexOf(".", StringComparison.Ordinal); + return scenePath.Substring(begin, end - begin); + } + + /// + /// Generates the scenes in build list + /// + internal void GenerateScenesInBuild() + { + ScenesInBuild.Clear(); + for (int i = 0; i < SceneManager.sceneCountInBuildSettings; i++) + { + ScenesInBuild.Add(GetSceneNameFromPath(SceneUtility.GetScenePathByBuildIndex(i))); + } + } + /// /// Constructor /// @@ -191,6 +235,8 @@ internal NetworkSceneManager(NetworkManager networkManager) SceneEventData = new SceneEventData(networkManager); ClientSynchEventData = new SceneEventData(networkManager); + GenerateScenesInBuild(); + // If NetworkManager has this set to true, then we can get the DDOL (DontDestroyOnLoad) from its GaemObject if (networkManager.DontDestroy) { @@ -222,6 +268,35 @@ internal NetworkSceneManager(NetworkManager networkManager) ScenesLoaded.Add(DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene); } + /// + /// If the VerifySceneBeforeLoading delegate handler has been set by the user, this will provide + /// an additional level of security and/or validation that the scene being loaded in the specified + /// loading mode is "a valid scene to be loaded in the LoadSceneMode specified". + /// + /// index into ScenesInBuild + /// Name of the scene + /// LoadSceneMode the scene is going to be loaded + /// true (Valid) or false (Invalid) + internal bool ValidateSceneBeforeLoading(uint sceneIndex, LoadSceneMode loadSceneMode) + { + var validated = true; + var sceneName = ScenesInBuild[(int)sceneIndex]; + if (VerifySceneBeforeLoading != null) + { + validated = VerifySceneBeforeLoading.Invoke((int)sceneIndex, sceneName, loadSceneMode); + } + if (!validated) + { + var serverHostorClient = "Client"; + if (m_NetworkManager.IsServer) + { + serverHostorClient = m_NetworkManager.IsHost ? "Host" : "Server"; + } + Debug.LogWarning($"Scene {sceneName} of Scenes in Build Index {SceneEventData.SceneIndex} being loaded in {loadSceneMode.ToString()} mode failed validation on the {serverHostorClient}!"); + } + return validated; + } + /// /// Since SceneManager.GetSceneByName only returns the first scene that matches the name /// we must "find" a newly added scene by looking through all loaded scenes and determining @@ -354,75 +429,84 @@ private void SendSceneEventData(ulong[] targetClientIds) } /// - /// Returns the Netcode scene index from a scene + /// Verifies the scene name is valid relative to the scenes in build list /// - /// - /// Netcode Scene Index - private uint GetNetcodeSceneIndexFromScene(Scene scene) + /// + /// true (Valid) or false (Invalid) + internal bool IsSceneNameValid(string sceneName) { - uint index = 0; - if (!SceneNameToIndex.TryGetValue(scene.name, out index)) + if (ScenesInBuild.Contains(sceneName)) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"The current scene ({scene.name}) is not registered as a network scene."); - } - //MaxValue denotes an error - return uint.MaxValue; + return true; } - return index; + return false; + } + + /// + /// Used to determine if the index value is within the range of valid + /// build indices. + /// + /// index value to check + /// true (Valid) or false (Invalid) + internal bool IsSceneIndexValid(uint index) + { + return (index >= 0 && index < ScenesInBuild.Count); } /// - /// Returns the scene name from the Netcode scene index - /// Note: This is not the same as the Build Settings Scenes in Build index + /// Gets the build Index value for the scene name /// - /// Netcode Scene Index - /// scene name - private string GetSceneNameFromNetcodeSceneIndex(uint sceneIndex) + /// scene name + /// build index + internal uint GetBuildIndexFromSceneName(string sceneName) { - var sceneName = string.Empty; - if (!SceneIndexToString.TryGetValue(sceneIndex, out sceneName)) + if (IsSceneNameValid(sceneName)) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"The current scene index ({sceneIndex}) is not registered as a network scene."); - } + return (uint)ScenesInBuild.IndexOf(sceneName); } - return sceneName; + return uint.MaxValue; } /// - /// Adds a scene during runtime. - /// The index is REQUIRED to be unique AND the same across all instances. + /// Entry method for scene unloading validation /// - /// Scene name. - /// Index. - public void AddRuntimeSceneName(string sceneName, uint index) + /// the scene to be unloaded + /// + private SceneEventProgress ValidateSceneEventUnLoading(Scene scene) { - if (!m_NetworkManager.NetworkConfig.AllowRuntimeSceneChanges) + if (!m_NetworkManager.IsServer) { - throw new NetworkConfigurationException($"Cannot change the scene configuration when {nameof(NetworkConfig.AllowRuntimeSceneChanges)} is false"); + throw new NotServerException("Only server can start a scene event!"); } - RegisteredSceneNames.Add(sceneName); - SceneIndexToString.Add(index, sceneName); - SceneNameToIndex.Add(sceneName, index); + if (!m_NetworkManager.NetworkConfig.EnableSceneManagement) + { + //Log message about enabling SceneManagement + throw new Exception($"{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)}."); + } + + if (!scene.isLoaded) + { + Debug.LogWarning($"{nameof(UnloadScene)} was called, but the scene {scene.name} is not currently loaded!"); + return new SceneEventProgress(null, SceneEventProgressStatus.SceneNotLoaded); + } + + return ValidateSceneEvent(scene.name, true); } /// - /// Validates the new scene event request by the server-side code. - /// This also initializes some commonly shared values as well as SceneEventProgress + /// Entry method for scene loading validation /// - /// - /// that should have a of otherwise it failed. - private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnloading = false) + /// scene name to load + /// + private SceneEventProgress ValidateSceneEventLoading(string sceneName) { if (!m_NetworkManager.IsServer) { throw new NotServerException("Only server can start a scene event!"); } - if (!m_NetworkManager.NetworkConfig.EnableSceneManagement) { //Log message about enabling SceneManagement @@ -431,6 +515,17 @@ private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnl $"{nameof(NetworkSceneManager.LoadScene)} or {nameof(NetworkSceneManager.UnloadScene)}."); } + return ValidateSceneEvent(sceneName); + } + + /// + /// Validates the new scene event request by the server-side code. + /// This also initializes some commonly shared values as well as SceneEventProgress + /// + /// + /// that should have a of otherwise it failed. + private SceneEventProgress ValidateSceneEvent(string sceneName, bool isUnloading = false) + { // Return scene event already in progress if one is already in progress... :) if (s_IsSceneEventActive) { @@ -438,13 +533,13 @@ private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnl } // Return invalid scene name status if the scene name is invalid... :) - if (!RegisteredSceneNames.Contains(sceneName)) + if (!IsSceneNameValid(sceneName)) { return new SceneEventProgress(null, SceneEventProgressStatus.InvalidSceneName); } var sceneEventProgress = new SceneEventProgress(m_NetworkManager); - sceneEventProgress.SceneName = sceneName; + sceneEventProgress.SceneIndex = GetBuildIndexFromSceneName(sceneName); SceneEventProgressTracking.Add(sceneEventProgress.Guid, sceneEventProgress); if (!isUnloading) @@ -478,7 +573,7 @@ private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress { using var nonNullContext = (InternalCommandContext)context; ClientSynchEventData.SceneEventGuid = sceneEventProgress.Guid; - ClientSynchEventData.SceneIndex = SceneNameToIndex[sceneEventProgress.SceneName]; + ClientSynchEventData.SceneIndex = sceneEventProgress.SceneIndex; ClientSynchEventData.SceneEventType = sceneEventProgress.SceneEventType; ClientSynchEventData.ClientsCompleted = sceneEventProgress.DoneClients; ClientSynchEventData.ClientsTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(); @@ -489,7 +584,7 @@ private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = sceneEventProgress.SceneEventType, - SceneName = sceneEventProgress.SceneName, + SceneName = ScenesInBuild[(int)sceneEventProgress.SceneIndex], ClientId = m_NetworkManager.ServerClientId, LoadSceneMode = sceneEventProgress.LoadSceneMode, ClientsThatCompleted = sceneEventProgress.DoneClients, @@ -518,7 +613,7 @@ public SceneEventProgressStatus UnloadScene(Scene scene) return SceneEventProgressStatus.SceneNotLoaded; } - var sceneEventProgress = ValidateServerSceneEvent(sceneName, true); + var sceneEventProgress = ValidateSceneEventUnLoading(scene); if (sceneEventProgress.Status != SceneEventProgressStatus.Started) { return sceneEventProgress.Status; @@ -532,7 +627,7 @@ public SceneEventProgressStatus UnloadScene(Scene scene) SceneEventData.SceneEventGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload; - SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + SceneEventData.SceneIndex = GetBuildIndexFromSceneName(sceneName); SceneEventData.SceneHandle = sceneHandle; // This will be the message we send to everyone when this scene event sceneEventProgress is complete @@ -564,8 +659,7 @@ public SceneEventProgressStatus UnloadScene(Scene scene) /// private void OnClientUnloadScene() { - var sceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex); - if (sceneName == string.Empty) + if (!IsSceneIndexValid(SceneEventData.SceneIndex)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { @@ -575,9 +669,11 @@ private void OnClientUnloadScene() return; } + var sceneName = ScenesInBuild[(int)SceneEventData.SceneIndex]; + if (!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) { - throw new Exception($"Client failed to unload scene {GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex)} " + + throw new Exception($"Client failed to unload scene {sceneName} " + $"because we are missing the client scene handle due to the server scene handle {SceneEventData.SceneHandle} not being found!"); } @@ -586,7 +682,7 @@ private void OnClientUnloadScene() if (!ScenesLoaded.ContainsKey(sceneHandle)) { // Error scene handle not found! - throw new Exception($"Client failed to unload scene {GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex)} " + + throw new Exception($"Client failed to unload scene {sceneName} " + $"because the client scene handle {sceneHandle} was not found in ScenesLoaded!"); } s_IsSceneEventActive = true; @@ -658,7 +754,7 @@ private void OnSceneUnloaded() { SceneEventType = SceneEventData.SceneEventType, LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + SceneName = ScenesInBuild[(int)SceneEventData.SceneIndex], ClientId = m_NetworkManager.IsServer ? m_NetworkManager.ServerClientId : m_NetworkManager.LocalClientId }); @@ -708,7 +804,7 @@ internal void UnloadAdditivelyLoadedScenes() /// ( means it was successful) public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSceneMode) { - var sceneEventProgress = ValidateServerSceneEvent(sceneName); + var sceneEventProgress = ValidateSceneEventLoading(sceneName); if (sceneEventProgress.Status != SceneEventProgressStatus.Started) { return sceneEventProgress.Status; @@ -721,9 +817,16 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc // Now set up the current scene event SceneEventData.SceneEventGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Load; - SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + SceneEventData.SceneIndex = GetBuildIndexFromSceneName(sceneName); SceneEventData.LoadSceneMode = loadSceneMode; + // This both checks to make sure the scene is valid and if not resets the active scene event + s_IsSceneEventActive = ValidateSceneBeforeLoading(SceneEventData.SceneIndex, loadSceneMode); + if (!s_IsSceneEventActive) + { + return SceneEventProgressStatus.SceneFailedVerification; + } + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { // Destroy current scene objects before switching. @@ -762,13 +865,20 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc /// Stream data associated with the event private void OnClientSceneLoadingEvent(Stream objectStream) { - if (!SceneIndexToString.TryGetValue(SceneEventData.SceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) + if (!IsSceneIndexValid(SceneEventData.SceneIndex)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning("Server requested a scene switch to a non-registered scene"); } + return; + } + + var sceneName = ScenesInBuild[(int)SceneEventData.SceneIndex]; + // Run scene validation before loading a scene + if (!ValidateSceneBeforeLoading(SceneEventData.SceneIndex, SceneEventData.LoadSceneMode)) + { return; } @@ -792,7 +902,7 @@ private void OnClientSceneLoadingEvent(Stream objectStream) } else { - throw new Exception($"Could not find the scene handle {SceneEventData.SceneHandle} for scene {GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex)} " + + throw new Exception($"Could not find the scene handle {SceneEventData.SceneHandle} for scene {sceneName} " + $"during unit test. Did you forget to register this in the unit test?"); } return; @@ -930,13 +1040,12 @@ private void OnServerLoadedScene(Scene scene) } s_IsSceneEventActive = false; - //First, notify local server that the scene was loaded OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + SceneName = ScenesInBuild[(int)SceneEventData.SceneIndex], ClientId = m_NetworkManager.ServerClientId, Scene = scene, }); @@ -965,7 +1074,7 @@ private void OnClientLoadedScene(Scene scene) { SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + SceneName = ScenesInBuild[(int)SceneEventData.SceneIndex], ClientId = m_NetworkManager.LocalClientId, Scene = scene, }); @@ -995,7 +1104,7 @@ internal void SynchronizeNetworkObjects(ulong clientId) { var scene = SceneManager.GetSceneAt(i); - var sceneIndex = GetNetcodeSceneIndexFromScene(scene); + var sceneIndex = GetBuildIndexFromSceneName(scene.name); if (sceneIndex == uint.MaxValue) { @@ -1005,9 +1114,17 @@ internal void SynchronizeNetworkObjects(ulong clientId) // If we are the base scene, then we set the root scene index; if (activeScene == scene) { + if(!ValidateSceneBeforeLoading(sceneIndex, LoadSceneMode.Single)) + { + continue; + } ClientSynchEventData.SceneIndex = sceneIndex; ClientSynchEventData.SceneHandle = scene.handle; } + else if (!ValidateSceneBeforeLoading(sceneIndex, LoadSceneMode.Additive)) + { + continue; + } ClientSynchEventData.AddSceneToSynchronize(sceneIndex, scene.handle); } @@ -1038,19 +1155,24 @@ private void OnClientBeginSync() { var sceneIndex = SceneEventData.GetNextSceneSynchronizationIndex(); var sceneHandle = SceneEventData.GetNextSceneSynchronizationHandle(); - if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) + if (!IsSceneIndexValid(sceneIndex)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning("Server requested a scene switch to a non-registered scene"); } - return; } - + var sceneName = ScenesInBuild[(int)sceneIndex]; var activeScene = SceneManager.GetActiveScene(); var loadSceneMode = sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive; + // Always check to see if the scene needs to be validated + if (!ValidateSceneBeforeLoading(SceneEventData.SceneIndex, loadSceneMode)) + { + return; + } + // If this is the beginning of the synchronization event, then send client a notification that synchronization has begun if (sceneIndex == SceneEventData.SceneIndex) { @@ -1115,7 +1237,7 @@ private void OnClientBeginSync() /// Netcode scene index that was loaded private void ClientLoadedSynchronization(uint sceneIndex, int sceneHandle) { - var sceneName = GetSceneNameFromNetcodeSceneIndex(sceneIndex); + var sceneName = ScenesInBuild[(int)sceneIndex]; var nextScene = GetAndAddNewlyLoadedSceneByName(sceneName); if (!nextScene.isLoaded || !nextScene.IsValid()) @@ -1237,7 +1359,7 @@ private void HandleClientSceneEvent(Stream stream) OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventType, - SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + SceneName = ScenesInBuild[(int)SceneEventData.SceneIndex], ClientId = m_NetworkManager.ServerClientId, LoadSceneMode = SceneEventData.LoadSceneMode, ClientsThatCompleted = SceneEventData.ClientsCompleted, @@ -1270,7 +1392,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { SceneEventType = SceneEventData.SceneEventType, LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + SceneName = ScenesInBuild[(int)SceneEventData.SceneIndex], ClientId = clientId }); @@ -1292,7 +1414,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { SceneEventType = SceneEventData.SceneEventType, LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + SceneName = ScenesInBuild[(int)SceneEventData.SceneIndex], ClientId = clientId }); diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs index d8db7d3476..d38df4ddfb 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs @@ -37,7 +37,11 @@ public enum SceneEventProgressStatus /// or is invalid /// InvalidSceneName, - + /// + /// Server side: Returned if the delegate handler returns false + /// (i.e. scene is considered not valid/safe to load) + /// + SceneFailedVerification, /// /// This is used for internal error notifications. /// If you receive this event then it is most likely due to a bug. @@ -84,7 +88,7 @@ internal class SceneEventProgress /// internal bool AreAllClientsDoneLoading { get; private set; } - internal string SceneName { get; set; } + internal uint SceneIndex { get; set; } internal Guid Guid { get; } = Guid.NewGuid(); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerCustomMessageManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerCustomMessageManagerTests.cs index 2da24c6d38..2094b1efd3 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerCustomMessageManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerCustomMessageManagerTests.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using NUnit.Framework; using UnityEngine; -using UnityEngine.SceneManagement; namespace Unity.Netcode.EditorTests { @@ -14,19 +12,13 @@ public void CustomMessageManagerAssigned() var networkManager = gameObject.AddComponent(); var transport = gameObject.AddComponent(); - // Netcode sets this in validate - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name } - }; - + networkManager.NetworkConfig = new NetworkConfig(); // Set dummy transport that does nothing networkManager.NetworkConfig.NetworkTransport = transport; CustomMessagingManager preManager = networkManager.CustomMessagingManager; - // Start server to cause init + // Start server to cause initialization networkManager.StartServer(); Debug.Assert(preManager == null); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs index 45727c22f8..2b01929ccc 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using Unity.Netcode.Editor; using UnityEngine; -using UnityEngine.SceneManagement; using UnityEngine.TestTools; using Object = UnityEngine.Object; @@ -19,13 +17,7 @@ public void MessageHandlerReceivedMessageServerClient() var networkManager = gameObject.AddComponent(); var transport = gameObject.AddComponent(); - // Netcode sets this in validate - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name } - }; - + networkManager.NetworkConfig = new NetworkConfig(); // Set dummy transport that does nothing networkManager.NetworkConfig.NetworkTransport = transport; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerSceneManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerSceneManagerTests.cs index c641b2f4b1..c1f5b30d14 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerSceneManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerSceneManagerTests.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using NUnit.Framework; using UnityEngine; -using UnityEngine.SceneManagement; namespace Unity.Netcode.EditorTests { @@ -13,20 +11,13 @@ public void SceneManagerAssigned() var gameObject = new GameObject(nameof(SceneManagerAssigned)); var networkManager = gameObject.AddComponent(); var transport = gameObject.AddComponent(); - - // Netcode sets this in validate - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name } - }; - + networkManager.NetworkConfig = new NetworkConfig(); // Set dummy transport that does nothing networkManager.NetworkConfig.NetworkTransport = transport; NetworkSceneManager preManager = networkManager.SceneManager; - // Start server to cause init + // Start server to cause initialization process networkManager.StartServer(); Debug.Assert(preManager == null); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs index 756385a7af..ce1691f339 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs @@ -43,8 +43,7 @@ public virtual IEnumerator Teardown() // Make sure any NetworkObject with a GlobalObjectIdHash value of 0 is destroyed // If we are tearing down, we don't want to leave NetworkObjects hanging around var networkObjects = Object.FindObjectsOfType().ToList(); - var networkObjectsList = networkObjects.Where(c => c.GlobalObjectIdHash == 0); - foreach (var networkObject in networkObjectsList) + foreach (var networkObject in networkObjects) { Object.DestroyImmediate(networkObject); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Helpers/NetworkManagerHelper.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Helpers/NetworkManagerHelper.cs index 91cbe0a571..57129aef1b 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Helpers/NetworkManagerHelper.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Helpers/NetworkManagerHelper.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using UnityEngine; -using UnityEngine.SceneManagement; using NUnit.Framework; using Unity.Netcode.Transports.UNET; @@ -71,19 +70,13 @@ public static bool StartNetworkManager(out NetworkManager networkManager, Networ Debug.Log($"{nameof(NetworkManager)} Instantiated."); var unetTransport = NetworkManagerGameObject.AddComponent(); - if (networkConfig == null) { networkConfig = new NetworkConfig { EnableSceneManagement = false, - RegisteredScenes = new List() { SceneManager.GetActiveScene().name } }; } - else - { - networkConfig.RegisteredScenes.Add(SceneManager.GetActiveScene().name); - } NetworkManagerObject.NetworkConfig = networkConfig; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs index c2eb731e19..a59d3300dd 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs @@ -31,7 +31,6 @@ public static class MultiInstanceHelpers public static bool Create(int clientCount, out NetworkManager server, out NetworkManager[] clients, int targetFrameRate = 60) { s_NetworkManagerInstances = new List(); - CreateNewClients(clientCount, out clients); // Create gameObject @@ -44,8 +43,6 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ // Set the NetworkConfig server.NetworkConfig = new NetworkConfig() { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, // Set transport NetworkTransport = go.AddComponent() }; @@ -65,7 +62,7 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ public static bool CreateNewClients(int clientCount, out NetworkManager[] clients) { clients = new NetworkManager[clientCount]; - + var activeSceneName = SceneManager.GetActiveScene().name; for (int i = 0; i < clientCount; i++) { // Create gameObject @@ -76,8 +73,6 @@ public static bool CreateNewClients(int clientCount, out NetworkManager[] client // Set the NetworkConfig clients[i].NetworkConfig = new NetworkConfig() { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, // Set transport NetworkTransport = go.AddComponent() }; @@ -131,7 +126,7 @@ public static void Destroy() // Destroy the network manager instances foreach (var networkManager in NetworkManagerInstances) { - Object.Destroy(networkManager.gameObject); + Object.DestroyImmediate(networkManager.gameObject); } NetworkManagerInstances.Clear(); @@ -139,7 +134,8 @@ public static void Destroy() // Destroy the temporary GameObject used to run co-routines if (s_CoroutineRunner != null) { - Object.Destroy(s_CoroutineRunner); + s_CoroutineRunner.StopAllCoroutines(); + Object.DestroyImmediate(s_CoroutineRunner); } Application.targetFrameRate = s_OriginalTargetFrameRate; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs index 37d262d516..835f852625 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs @@ -57,7 +57,6 @@ public override IEnumerator Setup() [UnityTearDown] public override IEnumerator Teardown() { - yield return base.Teardown(); if (m_TestNetworkObjectPrefab != null) { @@ -68,6 +67,8 @@ public override IEnumerator Teardown() { Object.Destroy(m_TestNetworkObjectInstance); } + yield return base.Teardown(); + } /// diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs index c4fdcac29f..da1b712051 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs @@ -17,13 +17,13 @@ public class NetworkPrefabHandlerTests { private const string k_TestPrefabObjectName = "NetworkPrefabTestObject"; - private uint m_GlobalObjectIdHashBase = 123456; + private uint m_ObjectId = 1; private GameObject MakeValidNetworkPrefab() { - Guid baseObjectID = NetworkManagerHelper.AddGameNetworkObject(k_TestPrefabObjectName + m_GlobalObjectIdHashBase.ToString()); + Guid baseObjectID = NetworkManagerHelper.AddGameNetworkObject(k_TestPrefabObjectName + m_ObjectId.ToString()); NetworkObject validPrefab = NetworkManagerHelper.InstantiatedNetworkObjects[baseObjectID]; - MultiInstanceHelpers.MakeNetworkObjectTestPrefab(validPrefab, m_GlobalObjectIdHashBase); - m_GlobalObjectIdHashBase++; + MultiInstanceHelpers.MakeNetworkObjectTestPrefab(validPrefab); + m_ObjectId++; return validPrefab.gameObject; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs index 9f1386bae7..90f7317216 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs @@ -40,11 +40,24 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) } } + private bool VerifySceneBeforeLoading(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode) + { + if(sceneName.StartsWith("InitTestScene")) + { + return false; + } + return true; + } + [UnitySetUp] public IEnumerator Setup() { SceneManager.sceneLoaded += OnSceneLoaded; + // We need NetworkManager to be instantiated first before you load scenes externally in order to be able to determine if + // we are running a unit test or not. (it is this or manually setting a property) + Assert.That(MultiInstanceHelpers.Create(k_ClientInstanceCount, out m_ServerNetworkManager, out m_ClientNetworkManagers)); + var execAssembly = Assembly.GetExecutingAssembly(); var packagePath = PackageInfo.FindForAssembly(execAssembly).assetPath; var scenePath = Path.Combine(packagePath, $"Tests/Runtime/ObjectParenting/{nameof(NetworkObjectParentingTests)}.unity"); @@ -56,7 +69,6 @@ public IEnumerator Setup() const int setCount = k_ClientInstanceCount + 1; - Assert.That(MultiInstanceHelpers.Create(k_ClientInstanceCount, out m_ServerNetworkManager, out m_ClientNetworkManagers)); Assert.That(m_ServerNetworkManager, Is.Not.Null); Assert.That(m_ClientNetworkManagers, Is.Not.Null); Assert.That(m_ClientNetworkManagers.Length, Is.EqualTo(k_ClientInstanceCount)); @@ -86,6 +98,18 @@ public IEnumerator Setup() // Start server and client NetworkManager instances Assert.That(MultiInstanceHelpers.Start(true, m_ServerNetworkManager, m_ClientNetworkManagers)); + m_ServerNetworkManager.SceneManager.ScenesInBuild.Add(nameof(NetworkObjectParentingTests)); + // Register our scene verification delegate handler so we don't load the unit test scene + m_ServerNetworkManager.SceneManager.VerifySceneBeforeLoading = VerifySceneBeforeLoading; + foreach (var entry in m_ClientNetworkManagers) + { + if (!entry.SceneManager.ScenesInBuild.Contains(nameof(NetworkObjectParentingTests))) + { + entry.SceneManager.ScenesInBuild.Add(nameof(NetworkObjectParentingTests)); + } + // Register our scene verification delegate handler so we don't load the unit test scene + entry.SceneManager.VerifySceneBeforeLoading = VerifySceneBeforeLoading; + } // Wait for connection on client side yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(m_ClientNetworkManagers)); diff --git a/testproject/Assets/Prefabs/RandomMoverObject.prefab b/testproject/Assets/Prefabs/RandomMoverObject.prefab index 852d8d0059..44665854b4 100644 --- a/testproject/Assets/Prefabs/RandomMoverObject.prefab +++ b/testproject/Assets/Prefabs/RandomMoverObject.prefab @@ -146,7 +146,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: m_EditorClassIdentifier: - Authority: 2 Channel: 0 InLocalSpace: 0 SyncPositionX: 1 @@ -158,6 +157,9 @@ MonoBehaviour: SyncScaleX: 1 SyncScaleY: 1 SyncScaleZ: 1 + PositionThreshold: 0 + RotAngleThreshold: 0 + ScaleThreshold: 0 FixedSendsPerSecond: 5 --- !u!114 &1932194183178713890 MonoBehaviour: @@ -168,7 +170,7 @@ MonoBehaviour: m_GameObject: {fileID: 771575417923360811} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: c552ef0ccf6ff8148a9c9606e7d3fc15, type: 3} + m_Script: {fileID: 11500000, guid: c4152da5a2f4e7746aecdcd815f6616b, type: 3} m_Name: m_EditorClassIdentifier: --- !u!1 &2058396567867412356 diff --git a/testproject/Assets/Prefabs/SceneLevelGeometry.prefab b/testproject/Assets/Prefabs/SceneLevelGeometry.prefab index 680403c8f0..da9864d139 100644 --- a/testproject/Assets/Prefabs/SceneLevelGeometry.prefab +++ b/testproject/Assets/Prefabs/SceneLevelGeometry.prefab @@ -1,5 +1,93 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1 &910007655143077103 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6283120762215196916} + - component: {fileID: 3739510624437302406} + m_Layer: 0 + m_Name: CornerBumper (1) + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6283120762215196916 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910007655143077103} + m_LocalRotation: {x: -0, y: -0.9244967, z: -0, w: -0.38119} + m_LocalPosition: {x: -29.72, y: 0.98, z: 29.82} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4012615692778511849} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: -224.815, z: 0} +--- !u!65 &3739510624437302406 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910007655143077103} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 4, y: 2, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &1854705290947220173 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2290144462706082272} + - component: {fileID: 4559046433245738380} + m_Layer: 0 + m_Name: CornerBumper (2) + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2290144462706082272 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1854705290947220173} + m_LocalRotation: {x: -0, y: -0.3771283, z: -0, w: -0.92616105} + m_LocalPosition: {x: -29.53, y: 0.98, z: -29.71} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4012615692778511849} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: -315.688, z: 0} +--- !u!65 &4559046433245738380 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1854705290947220173} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 4, y: 2, z: 1} + m_Center: {x: 0, y: 0, z: 0} --- !u!1 &4012615691354089848 GameObject: m_ObjectHideFlags: 0 @@ -190,8 +278,8 @@ BoxCollider: m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} + m_Size: {x: 2, y: 1, z: 1} + m_Center: {x: -0.5, y: 0, z: 0} --- !u!1 &4012615691965054905 GameObject: m_ObjectHideFlags: 0 @@ -286,8 +374,8 @@ BoxCollider: m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} + m_Size: {x: 1, y: 1, z: 2} + m_Center: {x: 0, y: 0, z: 0.5} --- !u!1 &4012615692269653858 GameObject: m_ObjectHideFlags: 0 @@ -382,8 +470,8 @@ BoxCollider: m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} + m_Size: {x: 1, y: 1, z: 2} + m_Center: {x: 0, y: 0, z: -0.5} --- !u!1 &4012615692778511854 GameObject: m_ObjectHideFlags: 0 @@ -394,7 +482,7 @@ GameObject: m_Component: - component: {fileID: 4012615692778511849} m_Layer: 0 - m_Name: Level + m_Name: SceneLevelGeometry m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -416,6 +504,10 @@ Transform: - {fileID: 4012615692269653854} - {fileID: 4012615691503252839} - {fileID: 4012615692791378778} + - {fileID: 3910294717376836327} + - {fileID: 6283120762215196916} + - {fileID: 2290144462706082272} + - {fileID: 6959258897999621209} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -513,5 +605,93 @@ BoxCollider: m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 - m_Size: {x: 1, y: 1, z: 1} + m_Size: {x: 2, y: 1, z: 1} + m_Center: {x: 0.5, y: 0, z: 0} +--- !u!1 &4674276234353933548 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3910294717376836327} + - component: {fileID: 3136259738973340924} + m_Layer: 0 + m_Name: CornerBumper + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3910294717376836327 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4674276234353933548} + m_LocalRotation: {x: 0, y: -0.38268343, z: 0, w: 0.92387956} + m_LocalPosition: {x: 29.7, y: 0.98, z: -29.61} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4012615692778511849} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: -45, z: 0} +--- !u!65 &3136259738973340924 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4674276234353933548} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 4, y: 2, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &7080625901286762351 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6959258897999621209} + - component: {fileID: 7672408768716900064} + m_Layer: 0 + m_Name: CornerBumper (3) + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6959258897999621209 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7080625901286762351} + m_LocalRotation: {x: -0, y: 0.93588465, z: -0, w: -0.35230666} + m_LocalPosition: {x: 29.26, y: 0.98, z: 29.45} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4012615692778511849} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: -498.74298, z: 0} +--- !u!65 &7672408768716900064 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7080625901286762351} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 4, y: 2, z: 1} m_Center: {x: 0, y: 0, z: 0} diff --git a/testproject/Assets/Samples/PrefabPool/PrefabPoolOverrideExample.unity b/testproject/Assets/Samples/PrefabPool/PrefabPoolOverrideExample.unity index d3ffd1e1ec..d358a72d23 100644 --- a/testproject/Assets/Samples/PrefabPool/PrefabPoolOverrideExample.unity +++ b/testproject/Assets/Samples/PrefabPool/PrefabPoolOverrideExample.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -265,6 +265,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -563,6 +564,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -850,7 +852,7 @@ GameObject: - component: {fileID: 1024114719} - component: {fileID: 1024114718} m_Layer: 0 - m_Name: '[NetworkManager]' + m_Name: NetworkManager m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -874,10 +876,6 @@ MonoBehaviour: NetworkConfig: ProtocolVersion: 0 NetworkTransport: {fileID: 1024114719} - RegisteredScenes: - - PrefabPoolExample - - PrefabPoolOverrideExample - AllowRuntimeSceneChanges: 0 PlayerPrefab: {fileID: 4079352819444256614, guid: c16f03336b6104576a565ef79ad643c0, type: 3} NetworkPrefabs: @@ -905,6 +903,10 @@ MonoBehaviour: LoadSceneTimeOut: 120 MessageBufferTimeout: 20 EnableNetworkLogs: 1 + UseSnapshotDelta: 0 + UseSnapshotSpawn: 0 + ScenesInBuild: {fileID: 11400000, guid: bf58cb10b5121b843991aa419917e22c, type: 2} + DefaultScenesInBuildAssetNameAndPath: Assets/ScenesInBuildList.asset --- !u!114 &1024114719 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1091,6 +1093,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -1464,6 +1467,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -1684,6 +1688,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -1781,6 +1786,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_ClientServerToggle: {fileID: 1588117327} + m_TrackSceneEvents: 0 + m_LogSceneEventsToConsole: 1 --- !u!114 &2107482023 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs index a2491102d0..e599b080bb 100644 --- a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs +++ b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs @@ -333,15 +333,16 @@ public override void OnNetworkSpawn() } /// - /// Unregister for the client connected and disconnected events upon being destroyed + /// Unregister for the client connected and disconnected events upon being despawned /// - private void OnDestroy() + public override void OnNetworkDespawn() { if (IsServer) { NetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback; NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback; } + base.OnNetworkDespawn(); } /// diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity index 5e2a4dcbf1..6ea9d5e74b 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity @@ -123,6 +123,154 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1001 &151772428 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -5591000292386890817, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: GlobalObjectIdHash + value: 2264043126 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360811, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_Name + value: RandomMoverObject (1) + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.x + value: 4.08 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: ea906834639fa3f4ba65c95db6181d6b, type: 3} +--- !u!1001 &365996112 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -5591000292386890817, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: GlobalObjectIdHash + value: 179273863 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360811, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_Name + value: RandomMoverObject (3) + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.z + value: -5.45 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: ea906834639fa3f4ba65c95db6181d6b, type: 3} --- !u!850595691 &903034822 LightingSettings: m_ObjectHideFlags: 0 @@ -184,6 +332,80 @@ LightingSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1001 &1152084533 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -5591000292386890817, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: GlobalObjectIdHash + value: 860074760 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360811, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_Name + value: RandomMoverObject (2) + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalPosition.z + value: 8.05 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: ea906834639fa3f4ba65c95db6181d6b, type: 3} --- !u!1001 &1247056486 PrefabInstance: m_ObjectHideFlags: 0 @@ -209,7 +431,7 @@ PrefabInstance: - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, type: 3} propertyPath: m_LocalPosition.x - value: 0 + value: -3.8 objectReference: {fileID: 0} - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, type: 3} diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index b1e24eb859..ef13b2dc9f 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; @@ -100,7 +101,7 @@ private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) } else if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.C2S_UnloadComplete) { - if (sceneEvent.ClientId == NetworkManager.Singleton.ServerClientId && !m_SceneLoaded.isLoaded) + if (sceneEvent.ClientId == NetworkManager.Singleton.ServerClientId && !m_SceneLoaded.isLoaded ) { m_SceneLoaded = new Scene(); m_WaitForSceneLoadOrUnload = false; @@ -126,19 +127,22 @@ public void OnToggle() if (m_ToggleObject) { m_ToggleObject.enabled = false; - StartCoroutine(SceneEventCoroutine(m_ToggleObject.isOn)); + ToggleSceneManager.AddNewToggleHandler(this); } } } + private bool m_WaitForSceneLoadOrUnload; - private IEnumerator SceneEventCoroutine(bool isLoading) + public IEnumerator SceneEventCoroutine() { + var isLoading = m_ToggleObject.isOn; var sceneEventProgressStatus = SceneEventProgressStatus.None; var continueCheck = true; + NetworkManager.Singleton.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; - while (continueCheck && sceneEventProgressStatus != SceneEventProgressStatus.Started) + while (continueCheck && sceneEventProgressStatus != SceneEventProgressStatus.Started && sceneEventProgressStatus != SceneEventProgressStatus.SceneFailedVerification) { if (isLoading) { @@ -148,31 +152,31 @@ private IEnumerator SceneEventCoroutine(bool isLoading) { sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneLoaded); } - if (sceneEventProgressStatus == SceneEventProgressStatus.SceneEventInProgress) - { - yield return new WaitForSeconds(0.25f); - } - else + + switch (sceneEventProgressStatus) { - switch (sceneEventProgressStatus) + case SceneEventProgressStatus.SceneEventInProgress: { - case SceneEventProgressStatus.Started: - { - continueCheck = false; - break; - } - case SceneEventProgressStatus.InternalNetcodeError: - case SceneEventProgressStatus.InvalidSceneName: - case SceneEventProgressStatus.SceneNotLoaded: - { - continueCheck = false; - break; - } + yield return new WaitForSeconds(0.25f); + break; } + case SceneEventProgressStatus.Started: + { + continueCheck = false; + break; + } + case SceneEventProgressStatus.InternalNetcodeError: + case SceneEventProgressStatus.InvalidSceneName: + case SceneEventProgressStatus.SceneNotLoaded: + { + Debug.Log($"Scene Event Error: {sceneEventProgressStatus}"); + continueCheck = false; + break; + } } } m_WaitForSceneLoadOrUnload = true; - var timeOutAfter = Time.realtimeSinceStartup + 10.0f; + var timeOutAfter = Time.realtimeSinceStartup + 5.0f; while (m_WaitForSceneLoadOrUnload) { if (timeOutAfter < Time.realtimeSinceStartup) @@ -183,11 +187,46 @@ private IEnumerator SceneEventCoroutine(bool isLoading) yield return new WaitForSeconds(0.5f); } - NetworkManager.Singleton.SceneManager.OnSceneEvent -= SceneManager_OnSceneEvent; m_ToggleObject.isOn = isLoading; m_ToggleObject.enabled = true; + ToggleSceneManager.CurrentQueueItem = null; yield return null; } } + + + public static class ToggleSceneManager + { + + private static Queue s_QueueUpForLoadUnload = new Queue(); + public static AdditiveSceneToggleHandler CurrentQueueItem; + static private IEnumerator GlobalQueueToggleRoutine() + { + while (s_QueueUpForLoadUnload.Count > 0) + { + CurrentQueueItem = s_QueueUpForLoadUnload.Dequeue(); + CurrentQueueItem.StartCoroutine(CurrentQueueItem.SceneEventCoroutine()); + while (CurrentQueueItem != null) + { + yield return new WaitForSeconds(0.25f); + } + } + yield return null; + } + + + public static void AddNewToggleHandler(AdditiveSceneToggleHandler handler) + { + if (s_QueueUpForLoadUnload.Count == 0 && CurrentQueueItem == null) + { + s_QueueUpForLoadUnload.Enqueue(handler); + NetworkManager.Singleton.StartCoroutine(GlobalQueueToggleRoutine()); + } + else + { + s_QueueUpForLoadUnload.Enqueue(handler); + } + } + } } diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs index 6e36aa912f..f4e9838a04 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs @@ -38,6 +38,25 @@ private void Start() m_NetworkManager = gameObject.GetComponent(); } + private void DeregisterFromOnSceneEvent() + { + if (m_IsInitialized && m_NetworkManager != null && m_NetworkManager.SceneManager != null) + { + m_IsInitialized = false; + m_NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; + } + } + + private void OnApplicationQuit() + { + DeregisterFromOnSceneEvent(); + } + + private void OnDisable() + { + DeregisterFromOnSceneEvent(); + } + /// /// Invoked on all local scene event notifications /// @@ -97,11 +116,6 @@ private void Update() m_SceneEvents.Dequeue(); } } - else if (m_IsInitialized) - { - m_IsInitialized = false; - m_NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; - } } } } diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 54c12bae18..9fdb0b1d3b 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -5053,6 +5053,46 @@ PrefabInstance: propertyPath: m_Name value: ExitButton objectReference: {fileID: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Text + value: X + objectReference: {fileID: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_FontData.m_Font + value: + objectReference: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_FontData.m_MaxSize + value: 40 + objectReference: {fileID: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_FontData.m_MinSize + value: 10 + objectReference: {fileID: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_FontData.m_FontSize + value: 14 + objectReference: {fileID: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_FontData.m_RichText + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_FontData.m_Alignment + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 2848221157716786269, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_FontData.m_LineSpacing + value: 1 + objectReference: {fileID: 0} - target: {fileID: 5266522511616468950, guid: 3200770c16e3b2b4ebe7f604154faac7, type: 3} propertyPath: m_SceneMenuToLoad diff --git a/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta index 37294f1a0e..1e4e5b2879 100644 --- a/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta +++ b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta @@ -1,5 +1,9 @@ fileFormatVersion: 2 +<<<<<<< HEAD:com.unity.netcode.gameobjects/Runtime/SceneManagement/ScenesInBuild.cs.meta +guid: c4152da5a2f4e7746aecdcd815f6616b +======= guid: c552ef0ccf6ff8148a9c9606e7d3fc15 +>>>>>>> test/AdditiveSceneLoading:testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index ec09ba7f6b..f4334eb4c4 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -7,7 +7,7 @@ namespace TestProject.ManualTests { - public class NetworkPrefabPool : NetworkBehaviour + public class NetworkPrefabPool : NetworkBehaviour, INetworkPrefabInstanceHandler { [Header("General Settings")] public bool AutoSpawnEnable = true; @@ -42,8 +42,6 @@ public class NetworkPrefabPool : NetworkBehaviour private GameObject m_ObjectToSpawn; private List m_ObjectPool; - private MyCustomPrefabSpawnHandler m_MyCustomPrefabSpawnHandler; - /// /// Called when enabled, if already connected we register any custom prefab spawn handler here /// @@ -55,24 +53,37 @@ private void OnEnable() } } + private bool m_IsQuitting; + private void OnApplicationQuit() + { + m_IsQuitting = true; + } + + private void OnDisable() + { + if (!m_IsQuitting) + { + DeregisterCustomPrefabHandler(); + } + } + /// /// Handles registering the custom prefab handler /// private void RegisterCustomPrefabHandler() { // Register the custom spawn handler? - if (m_MyCustomPrefabSpawnHandler == null && EnableHandler) + if (EnableHandler) { if (NetworkManager && NetworkManager.PrefabHandler != null) { - m_MyCustomPrefabSpawnHandler = new MyCustomPrefabSpawnHandler(this); if (RegisterUsingNetworkObject) { - NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool.GetComponent(), m_MyCustomPrefabSpawnHandler); + NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool.GetComponent(), this); } else { - NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool, m_MyCustomPrefabSpawnHandler); + NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool, this); } } else if (!IsServer) @@ -82,15 +93,10 @@ private void RegisterCustomPrefabHandler() } } - private void OnDisable() - { - OnUnloadScene(); - } - private void DeregisterCustomPrefabHandler() { // Register the custom spawn handler? - if (EnableHandler && NetworkManager && NetworkManager.PrefabHandler != null && m_MyCustomPrefabSpawnHandler != null) + if (EnableHandler && NetworkManager && NetworkManager.PrefabHandler != null) { NetworkManager.PrefabHandler.RemoveHandler(ServerObjectToPool); if (IsClient && m_ObjectToSpawn != null) @@ -132,12 +138,18 @@ private void OnUnloadScene() DeregisterCustomPrefabHandler(); CleanNetworkObjects(); + + if (NetworkManager != null && NetworkManager.SceneManager != null) + { + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; + } } } public override void OnNetworkDespawn() { + DeregisterCustomPrefabHandler(); if (NetworkManager != null) { NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; @@ -392,18 +404,10 @@ private IEnumerator SpawnObjects() } } } - } - - /// - /// The custom prefab handler that returns an object from the prefab pool - /// - public class MyCustomPrefabSpawnHandler : INetworkPrefabInstanceHandler - { - private NetworkPrefabPool m_PrefabPool; public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) { - var obj = m_PrefabPool.GetObject(); + var obj = GetObject(); if (obj != null) { obj.transform.position = position; @@ -421,13 +425,8 @@ public void Destroy(NetworkObject networkObject) } else { - Object.Destroy(networkObject.gameObject); + Destroy(networkObject.gameObject); } } - - public MyCustomPrefabSpawnHandler(NetworkPrefabPool objectPool) - { - m_PrefabPool = objectPool; - } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index b4c0c2f515..0fd3cc8f5f 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -6,7 +6,7 @@ namespace TestProject.ManualTests { - public class NetworkPrefabPoolAdditive : NetworkBehaviour + public class NetworkPrefabPoolAdditive : NetworkBehaviour, INetworkPrefabInstanceHandler { [Header("General Settings")] public bool RandomMovement = true; @@ -43,26 +43,23 @@ public class NetworkPrefabPoolAdditive : NetworkBehaviour private GameObject m_ObjectToSpawn; private List m_ObjectPool; - private MyAdditiveCustomPrefabSpawnHandler m_AdditiveCustomPrefabSpawnHandler; - /// /// Handles registering the custom prefab handler /// private void RegisterCustomPrefabHandler() { // Register the custom spawn handler? - if (m_AdditiveCustomPrefabSpawnHandler == null && EnableHandler) + if (EnableHandler) { if (NetworkManager && NetworkManager.PrefabHandler != null) { - m_AdditiveCustomPrefabSpawnHandler = new MyAdditiveCustomPrefabSpawnHandler(this); if (RegisterUsingNetworkObject) { - NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool.GetComponent(), m_AdditiveCustomPrefabSpawnHandler); + NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool.GetComponent(), this); } else { - NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool, m_AdditiveCustomPrefabSpawnHandler); + NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool, this); } } } @@ -71,7 +68,7 @@ private void RegisterCustomPrefabHandler() private void DeregisterCustomPrefabHandler() { // Register the custom spawn handler? - if (EnableHandler && NetworkManager && NetworkManager.PrefabHandler != null && m_AdditiveCustomPrefabSpawnHandler != null) + if (EnableHandler && NetworkManager && NetworkManager.PrefabHandler != null) { NetworkManager.PrefabHandler.RemoveHandler(ServerObjectToPool); if (IsClient && m_ObjectToSpawn != null) @@ -108,7 +105,6 @@ private void Start() } - /// /// For additive scenes, we only clear out our pooled NetworkObjects if we are migrating them from the ActiveScene /// to the scene where this NetworkPrefabPoolAdditive component is instantiated. @@ -368,6 +364,12 @@ public void UpdateSpawnsPerSecond() } + + private void OnDisable() + { + StopCoroutine(SpawnObjects()); + } + /// /// Coroutine to spawn boxes /// @@ -425,18 +427,10 @@ private IEnumerator SpawnObjects() } } } - } - - /// - /// The custom prefab handler that returns an object from the prefab pool - /// - public class MyAdditiveCustomPrefabSpawnHandler : INetworkPrefabInstanceHandler - { - private NetworkPrefabPoolAdditive m_PrefabPool; public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) { - var obj = m_PrefabPool.GetObject(); + var obj = GetObject(); if (obj != null) { obj.transform.position = position; @@ -455,13 +449,8 @@ public void Destroy(NetworkObject networkObject) } else { - Object.Destroy(networkObject.gameObject); + Destroy(networkObject.gameObject); } } - - public MyAdditiveCustomPrefabSpawnHandler(NetworkPrefabPoolAdditive objectPool) - { - m_PrefabPool = objectPool; - } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/PlayerMovementManager.cs b/testproject/Assets/Tests/Manual/Scripts/PlayerMovementManager.cs index 554920a9ec..73a887c657 100644 --- a/testproject/Assets/Tests/Manual/Scripts/PlayerMovementManager.cs +++ b/testproject/Assets/Tests/Manual/Scripts/PlayerMovementManager.cs @@ -19,7 +19,6 @@ public class PlayerMovementManager : NetworkBehaviour private void Start() { m_RandomMovement = GetComponent(); - } public override void OnNetworkSpawn() @@ -33,19 +32,22 @@ public override void OnNetworkSpawn() private void Update() { - if (NetworkObject.IsOwner && Input.GetKeyDown(KeyCode.Space)) + if (NetworkObject != null) { - if (m_RandomMovement) + if (IsOwner && Input.GetKeyDown(KeyCode.Space)) { - m_RandomMovement.enabled = !m_RandomMovement.enabled; + if (m_RandomMovement) + { + m_RandomMovement.enabled = !m_RandomMovement.enabled; + } } - } - if (NetworkObject && NetworkObject.NetworkManager && NetworkObject.NetworkManager.IsListening) - { - if (m_RandomMovement.enabled) + if (NetworkObject != null && NetworkObject.NetworkManager != null && NetworkObject.NetworkManager.IsListening) { - m_RandomMovement.Move(MoveSpeed); + if (m_RandomMovement.enabled) + { + m_RandomMovement.Move(MoveSpeed); + } } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs index 5d1e1f1422..7134251686 100644 --- a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs +++ b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs @@ -46,7 +46,11 @@ public void Move(int speed) else if (!IsServer && IsOwner) { // Client must sent Rpc - MovePlayerServerRpc(m_Direction * speed); + MovePlayerServerRpc(m_Direction * speed*1.05f); + } + else if (IsServer && !IsOwner) + { + m_MoveTowardsPosition = Vector3.Lerp(m_MoveTowardsPosition, Vector3.zero, 0.01f); } } diff --git a/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs b/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs index a543cfb3c0..edab517e84 100644 --- a/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs +++ b/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs @@ -51,8 +51,8 @@ public IEnumerator TestFixedUpdateMessagesAreOnlyProcessedOnce() var networkObject = m_Prefab.AddComponent(); // Make it a prefab - MultiInstanceHelpers.MakeNetworkObjectTestPrefab(networkObject, 1); - var handler = new SpawnRpcDespawnInstanceHandler(1); + MultiInstanceHelpers.MakeNetworkObjectTestPrefab(networkObject); + var handler = new SpawnRpcDespawnInstanceHandler(networkObject.GlobalObjectIdHash); foreach (var client in clients) { client.PrefabHandler.AddHandler(networkObject, handler); diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs index 508d5a341b..495d6b29ab 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs @@ -139,22 +139,20 @@ public IEnumerator SceneLoadingAndNotifications() /// /// Initializes the m_ShouldWaitList /// - private void InitializeSceneTestInfo() + private void InitializeSceneTestInfo(bool enableSceneVerification = false) { m_ShouldWaitList.Add(new SceneTestInfo() { ClientId = m_ServerNetworkManager.ServerClientId, ShouldWait = false }); - if (!m_ServerNetworkManager.NetworkConfig.RegisteredScenes.Contains(m_CurrentSceneName)) + if (enableSceneVerification) { - m_ServerNetworkManager.NetworkConfig.AllowRuntimeSceneChanges = true; - m_ServerNetworkManager.SceneManager.AddRuntimeSceneName(m_CurrentSceneName, (uint)(m_ServerNetworkManager.NetworkConfig.RegisteredScenes.Count + 1)); + m_ServerNetworkManager.SceneManager.VerifySceneBeforeLoading = ServerVerifySceneBeforeLoading; } foreach (var manager in m_ClientNetworkManagers) { m_ShouldWaitList.Add(new SceneTestInfo() { ClientId = manager.LocalClientId, ShouldWait = false }); - if (!manager.NetworkConfig.RegisteredScenes.Contains(m_CurrentSceneName)) + if (enableSceneVerification) { - manager.NetworkConfig.AllowRuntimeSceneChanges = true; - manager.SceneManager.AddRuntimeSceneName(m_CurrentSceneName, (uint)(manager.NetworkConfig.RegisteredScenes.Count + 1)); + manager.SceneManager.VerifySceneBeforeLoading = ClientVerifySceneBeforeLoading; } } } @@ -182,7 +180,15 @@ private void ResetWait() 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; + if (!m_IsTestingVerifyScene) + { + return (m_ShouldWaitList.Select(c => c).Where(c => c.ProcessedEvent != true && c.ShouldWait == true).Count() > 0) && !m_TimedOut; + } + else + { + return (m_ShouldWaitList.Select(c => c).Where(c => c.ProcessedEvent != true && c.ShouldWait == true && + c.ClientId == m_ServerNetworkManager.ServerClientId).Count() > 0) && !m_TimedOut && m_ClientsThatFailedVerification != NbClients; + } } /// @@ -300,5 +306,107 @@ private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) } } } + + private bool m_IsTestingVerifyScene; + private bool m_ServerVerifyScene; + private bool m_ClientVerifyScene; + private int m_ExpectedSceneIndex; + private int m_ClientsThatFailedVerification; + private string m_ExpectedSceneName; + private LoadSceneMode m_ExpectedLoadMode; + + private bool ServerVerifySceneBeforeLoading(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode) + { + Assert.IsTrue(m_ExpectedSceneIndex == sceneIndex); + Assert.IsTrue(m_ExpectedSceneName == sceneName); + Assert.IsTrue(m_ExpectedLoadMode == loadSceneMode); + + return m_ServerVerifyScene; + } + + private bool ClientVerifySceneBeforeLoading(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode) + { + Assert.IsTrue(m_ExpectedSceneIndex == sceneIndex); + Assert.IsTrue(m_ExpectedSceneName == sceneName); + Assert.IsTrue(m_ExpectedLoadMode == loadSceneMode); + if (!m_ClientVerifyScene) + { + m_ClientsThatFailedVerification++; + } + return m_ClientVerifyScene; + } + + /// + /// Unit test to verify that user defined scene verification works on both the client and + /// the server side. + /// + /// + [UnityTest] + public IEnumerator SceneVerifyBeforeLoadTest() + { + m_ServerNetworkManager.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; + m_CurrentSceneName = "AdditiveScene1"; + + // Now prepare for the loading and unloading additive scene testing + InitializeSceneTestInfo(true); + + // Test VerifySceneBeforeLoading with both server and client set to true + ResetWait(); + m_ServerVerifyScene = m_ClientVerifyScene = true; + m_ExpectedSceneIndex = (int)m_ServerNetworkManager.SceneManager.GetBuildIndexFromSceneName(m_CurrentSceneName); + m_ExpectedSceneName = m_CurrentSceneName; + m_ExpectedLoadMode = LoadSceneMode.Additive; + var result = m_ServerNetworkManager.SceneManager.LoadScene(m_CurrentSceneName, LoadSceneMode.Additive); + Assert.True(result == SceneEventProgressStatus.Started); + + // Wait for all clients to load the scene + yield return new WaitWhile(ShouldWait); + Assert.IsFalse(m_TimedOut); + + // Unload the scene + ResetWait(); + result = m_ServerNetworkManager.SceneManager.UnloadScene(m_CurrentScene); + Assert.True(result == SceneEventProgressStatus.Started); + + yield return new WaitWhile(ShouldWait); + Assert.IsFalse(m_TimedOut); + + // Test VerifySceneBeforeLoading with m_ServerVerifyScene set to false + // Server will notify it failed scene verification and no client should load + ResetWait(); + m_ServerVerifyScene = false; + result = m_ServerNetworkManager.SceneManager.LoadScene(m_CurrentSceneName, LoadSceneMode.Additive); + Assert.True(result == SceneEventProgressStatus.SceneFailedVerification); + + // Test VerifySceneBeforeLoading with m_ServerVerifyScene set to true and m_ClientVerifyScene set to false + // Server should load and clients will notify they failed scene verification + ResetWait(); + m_CurrentSceneName = "AdditiveScene2"; + m_ExpectedSceneName = m_CurrentSceneName; + m_ExpectedSceneIndex = (int)m_ServerNetworkManager.SceneManager.GetBuildIndexFromSceneName(m_CurrentSceneName); + m_ServerVerifyScene = true; + m_ClientVerifyScene = false; + m_IsTestingVerifyScene = true; + m_ClientsThatFailedVerification = 0; + result = m_ServerNetworkManager.SceneManager.LoadScene(m_CurrentSceneName, LoadSceneMode.Additive); + Assert.True(result == SceneEventProgressStatus.Started); + + // Now wait for server to complete and all clients to fail + yield return new WaitWhile(ShouldWait); + Assert.IsFalse(m_TimedOut); + + // Now unload the scene the server loaded from last test + ResetWait(); + m_IsTestingVerifyScene = false; + result = m_ServerNetworkManager.SceneManager.UnloadScene(m_CurrentScene); + Assert.True(result == SceneEventProgressStatus.Started); + + // Now wait for server to unload and clients will fake unload + yield return new WaitWhile(ShouldWait); + Assert.IsFalse(m_TimedOut); + + yield break; + } + } } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs.meta b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs.meta index 7e4d9c6ae8..31c443308c 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs.meta +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f7bf3513fa4b1c846af40172cdb7c9af +guid: e536693df1c81e94388cab0f062c2352 MonoImporter: externalObjects: {} serializedVersion: 2