From 039e4ab39ad22a0509f18d5d5dff6a15d86efb05 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 1 Jul 2021 17:47:48 -0500 Subject: [PATCH 001/106] feat This is a quick and dirty additive scene loading implementation example with minimal changes to the develop branch. --- .../Messaging/InternalMessageHandler.cs | 8 +- .../SceneManagement/NetworkSceneManager.cs | 65 +- testproject/Assets/Samples/SamplesMenu.unity | 1 + .../Samples/SceneTransitioningAdditive.meta | 8 + .../SceneTransitioningBase.unity | 2700 +++++++++++++++++ .../SceneTransitioningBase.unity.meta | 7 + ...neTransitioningBaseSceneRegistration.asset | 23 + ...nsitioningBaseSceneRegistration.asset.meta | 8 + .../SecondSceneAdditive.unity | 256 ++ .../SecondSceneAdditive.unity.meta | 7 + .../SwitchSceneHandlerAdditive.cs | 127 + .../SwitchSceneHandlerAdditive.cs.meta | 11 + .../ThirdSceneAdditive.unity | 256 ++ .../ThirdSceneAdditive.unity.meta | 7 + .../Scripts/NetworkPrefabPoolAdditive.cs | 334 ++ .../Scripts/NetworkPrefabPoolAdditive.cs.meta | 11 + .../Tests/Manual/Scripts/StatsDisplay.cs | 5 +- .../ProjectSettings/EditorBuildSettings.asset | 9 + 18 files changed, 3811 insertions(+), 32 deletions(-) create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive.meta create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity.meta create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity.meta create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity.meta create mode 100644 testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs create mode 100644 testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 09fa658c5a..47fb77968d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -86,7 +86,7 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv void DelayedSpawnAction(Stream continuationStream) { - + using (var continuationReader = PooledNetworkReader.Get(continuationStream)) { if (!NetworkManager.NetworkConfig.EnableSceneManagement) @@ -95,7 +95,7 @@ void DelayedSpawnAction(Stream continuationStream) } else { - NetworkManager.SceneManager.PopulateScenePlacedObjects(); + NetworkManager.SceneManager.PopulateScenePlacedObjects(SceneManager.GetActiveScene()); } var objectCount = continuationReader.ReadUInt32Packed(); @@ -208,12 +208,12 @@ public void HandleSwitchScene(ulong clientId, Stream stream) { uint sceneIndex = reader.ReadUInt32Packed(); var switchSceneGuid = new Guid(reader.ReadByteArray()); - + var isAdditivelyLoadedScene = reader.ReadBool(); var objectBuffer = new NetworkBuffer(); objectBuffer.CopyUnreadFrom(stream); objectBuffer.Position = 0; - m_NetworkManager.SceneManager.OnSceneSwitch(sceneIndex, switchSceneGuid, objectBuffer); + m_NetworkManager.SceneManager.OnSceneSwitch(sceneIndex, switchSceneGuid, objectBuffer, isAdditivelyLoadedScene ? LoadSceneMode.Additive:LoadSceneMode.Single); } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 776901ca16..9a7066346c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -81,7 +81,6 @@ public class NetworkSceneManager internal readonly Dictionary SceneSwitchProgresses = new Dictionary(); internal readonly Dictionary ScenePlacedObjects = new Dictionary(); - private static Scene s_LastScene; private static string s_NextSceneName; private static bool s_IsSwitching = false; internal static uint CurrentSceneIndex = 0; @@ -136,7 +135,7 @@ public void AddRuntimeSceneName(string sceneName, uint index) /// The name of the scene to switch to /// The mode to load the scene (Additive vs Single) /// SceneSwitchProgress - public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) + public SceneSwitchProgress SwitchScene(string sceneName,LoadSceneMode loadSceneMode = LoadSceneMode.Single, List scenesToUnload = null) { if (!m_NetworkManager.IsServer) { @@ -169,9 +168,11 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene return null; } - m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects(); //Destroy current scene objects before switching. + if (loadSceneMode != LoadSceneMode.Additive) + { + m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects(); //Destroy current scene objects before switching. + } s_IsSwitching = true; - s_LastScene = SceneManager.GetActiveScene(); var switchSceneProgress = new SceneSwitchProgress(m_NetworkManager); SceneSwitchProgresses.Add(switchSceneProgress.Guid, switchSceneProgress); @@ -195,8 +196,11 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene } }; - // Move ALL NetworkObjects to the temp scene - MoveObjectsToDontDestroyOnLoad(); + if (loadSceneMode != LoadSceneMode.Additive) + { + // Move ALL NetworkObjects to the temp scene + MoveObjectsToDontDestroyOnLoad(); + } IsSpawnedObjectsPendingInDontDestroyOnLoad = true; @@ -205,7 +209,7 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene s_NextSceneName = sceneName; - sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(switchSceneProgress.Guid, null); }; + sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(switchSceneProgress.Guid, null, loadSceneMode); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); OnSceneSwitchStarted?.Invoke(sceneLoad); @@ -213,7 +217,7 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene } // Called on client - internal void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream objectStream) + internal void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream objectStream, LoadSceneMode loadSceneMode) { if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) { @@ -225,21 +229,25 @@ internal void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream object return; } - s_LastScene = SceneManager.GetActiveScene(); - - // Move ALL NetworkObjects to the temp scene - MoveObjectsToDontDestroyOnLoad(); + if (loadSceneMode == LoadSceneMode.Single) + { + // Move ALL NetworkObjects to the temp scene + MoveObjectsToDontDestroyOnLoad(); + } IsSpawnedObjectsPendingInDontDestroyOnLoad = true; - var sceneLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single); + var sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); s_NextSceneName = sceneName; - sceneLoad.completed += asyncOp2 => OnSceneLoaded(switchSceneGuid, objectStream); + sceneLoad.completed += asyncOp2 => OnSceneLoaded(switchSceneGuid, objectStream, loadSceneMode); OnSceneSwitchStarted?.Invoke(sceneLoad); } + + + internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) { if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) @@ -257,7 +265,6 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) return; //This scene is already loaded. This usually happends at first load } - s_LastScene = SceneManager.GetActiveScene(); s_NextSceneName = sceneName; CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; @@ -283,7 +290,7 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) /// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene. /// Added the ability to choose not to clear the scene placed objects for additive scene loading. /// - internal void PopulateScenePlacedObjects(bool clearScenePlacedObjects = true) + internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) { if (clearScenePlacedObjects) { @@ -294,13 +301,13 @@ internal void PopulateScenePlacedObjects(bool clearScenePlacedObjects = true) // Just add every NetworkObject found that isn't already in the list // If any "non-in-scene placed NetworkObjects" are added to this list it shouldn't matter - // The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash + // The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash foreach (var networkObjectInstance in networkObjects) { if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) { - // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible - if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager) + // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) + if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy) { ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, networkObjectInstance); } @@ -308,17 +315,20 @@ internal void PopulateScenePlacedObjects(bool clearScenePlacedObjects = true) } } - private void OnSceneLoaded(Guid switchSceneGuid, Stream objectStream) + private void OnSceneLoaded(Guid switchSceneGuid, Stream objectStream, LoadSceneMode loadSceneMode) { CurrentActiveSceneIndex = SceneNameToIndex[s_NextSceneName]; var nextScene = SceneManager.GetSceneByName(s_NextSceneName); SceneManager.SetActiveScene(nextScene); //Get all NetworkObjects loaded by the scene - PopulateScenePlacedObjects(); + PopulateScenePlacedObjects(nextScene); - // Move all objects to the new scene - MoveObjectsToScene(nextScene); + if (loadSceneMode == LoadSceneMode.Single) + { + // Move all objects to the new scene + MoveObjectsToScene(nextScene); + } IsSpawnedObjectsPendingInDontDestroyOnLoad = false; @@ -326,15 +336,15 @@ private void OnSceneLoaded(Guid switchSceneGuid, Stream objectStream) if (m_NetworkManager.IsServer) { - OnServerLoadedScene(switchSceneGuid); + OnServerLoadedScene(switchSceneGuid, loadSceneMode); } else { - OnClientLoadedScene(switchSceneGuid, objectStream); + OnClientLoadedScene(switchSceneGuid, objectStream, loadSceneMode); } } - private void OnServerLoadedScene(Guid switchSceneGuid) + private void OnServerLoadedScene(Guid switchSceneGuid, LoadSceneMode loadSceneMode) { // Register in-scene placed NetworkObjects with MLAPI foreach (var keyValuePair in ScenePlacedObjects) @@ -354,6 +364,7 @@ private void OnServerLoadedScene(Guid switchSceneGuid) { writer.WriteUInt32Packed(CurrentActiveSceneIndex); writer.WriteByteArray(switchSceneGuid.ToByteArray()); + writer.WriteBool(loadSceneMode == LoadSceneMode.Additive); uint sceneObjectsToSpawn = 0; @@ -390,7 +401,7 @@ private void OnServerLoadedScene(Guid switchSceneGuid) OnSceneSwitched?.Invoke(); } - private void OnClientLoadedScene(Guid switchSceneGuid, Stream objectStream) + private void OnClientLoadedScene(Guid switchSceneGuid, Stream objectStream, LoadSceneMode loadSceneMode) { var networkObjects = UnityEngine.Object.FindObjectsOfType(); diff --git a/testproject/Assets/Samples/SamplesMenu.unity b/testproject/Assets/Samples/SamplesMenu.unity index 8a2a70878f..514f43d26f 100644 --- a/testproject/Assets/Samples/SamplesMenu.unity +++ b/testproject/Assets/Samples/SamplesMenu.unity @@ -2495,6 +2495,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_SceneMenus: + - {fileID: 11400000, guid: 9a8d9296fb33f794f95514bf38de3cf9, type: 2} - {fileID: 11400000, guid: 21aae92071ad50448a45b013d8346639, type: 2} - {fileID: 11400000, guid: 138603ab28f532140b48a57bea0e54b0, type: 2} m_SceneMenusDropDownList: {fileID: 1362704539} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive.meta b/testproject/Assets/Samples/SceneTransitioningAdditive.meta new file mode 100644 index 0000000000..6ac5deb347 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 31f1b8236850b674a85716389e467ba0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity new file mode 100644 index 0000000000..433379e212 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity @@ -0,0 +1,2700 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 903034822} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &34066664 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 34066665} + - component: {fileID: 34066666} + m_Layer: 5 + m_Name: SwitchSceneParent + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &34066665 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 34066664} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1347823142} + m_Father: {fileID: 290861172} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0} + m_AnchorMax: {x: 0.5, y: 0} + m_AnchoredPosition: {x: 0, y: 100} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &34066666 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 34066664} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fd82b56fddb35f2418f27a65b6bb3b54, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SwitchSceneButtonObject: {fileID: 1347823141} + m_SceneToSwitchTo: + - SecondSceneAdditive + - ThirdSceneAdditive + m_SceneAssets: + - {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} + - {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} +--- !u!1 &37242881 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 37242883} + - component: {fileID: 37242882} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &37242882 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 37242881} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &37242883 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 37242881} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!224 &42803802 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + m_PrefabInstance: {fileID: 2848221156282925290} + m_PrefabAsset: {fileID: 0} +--- !u!1 &57392470 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 57392474} + - component: {fileID: 57392473} + - component: {fileID: 57392472} + - component: {fileID: 57392471} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &57392471 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &57392472 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &57392473 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &57392474 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -30.5, y: 0.49999994, z: 0} + m_LocalScale: {x: 1, y: 3, z: 62} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &167044830 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 167044834} + - component: {fileID: 167044833} + - component: {fileID: 167044832} + - component: {fileID: 167044831} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &167044831 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &167044832 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &167044833 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &167044834 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1865409449} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &290861168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 290861172} + - component: {fileID: 290861171} + - component: {fileID: 290861170} + - component: {fileID: 290861169} + m_Layer: 5 + m_Name: UI + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &290861169 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &290861170 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1024, y: 768} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &290861171 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 575203309} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &290861172 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1588117328} + - {fileID: 34066665} + - {fileID: 42803802} + - {fileID: 1834318148} + - {fileID: 2058276876} + - {fileID: 1383741138} + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &336568645 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 336568649} + - component: {fileID: 336568648} + - component: {fileID: 336568647} + - component: {fileID: 336568646} + m_Layer: 0 + m_Name: Floor + m_TagString: Floor + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &336568646 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &336568647 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &336568648 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &336568649 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -0.50000006, z: 0} + m_LocalScale: {x: 60, y: 1, z: 60} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &562991978 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 562991979} + - component: {fileID: 562991981} + - component: {fileID: 562991980} + m_Layer: 5 + m_Name: BoxGeneratorCount + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &562991979 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2058276876} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 3, y: 20} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &562991980 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.9811321, g: 0.9811321, b: 0.9811321, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 0 +--- !u!222 &562991981 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_CullTransparentMesh: 1 +--- !u!1 &575203307 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 575203310} + - component: {fileID: 575203309} + - component: {fileID: 575203308} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &575203308 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 575203307} + m_Enabled: 1 +--- !u!20 &575203309 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 575203307} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &575203310 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 575203307} + m_LocalRotation: {x: 0.41890106, y: -0, z: -0, w: 0.9080319} + m_LocalPosition: {x: 0, y: 42, z: -46} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 49.530003, y: 0, z: 0} +--- !u!1 &599972120 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 599972121} + - component: {fileID: 599972123} + - component: {fileID: 599972122} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &599972121 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 599972120} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1588117328} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &599972122 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 599972120} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.5058824, b: 0.003921569, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Show Server Stats +--- !u!222 &599972123 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 599972120} + m_CullTransparentMesh: 1 +--- !u!850595691 &903034822 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &1024114717 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1024114720} + - component: {fileID: 1024114719} + - component: {fileID: 1024114718} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1024114718 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1024114717} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3} + m_Name: + m_EditorClassIdentifier: + DontDestroy: 1 + RunInBackground: 1 + LogLevel: 1 + NetworkConfig: + ProtocolVersion: 0 + NetworkTransport: {fileID: 1024114719} + RegisteredScenes: + - SceneTransitioningBase + - SecondSceneAdditive + - ThirdSceneAdditive + AllowRuntimeSceneChanges: 0 + PlayerPrefab: {fileID: 4079352819444256614, guid: c16f03336b6104576a565ef79ad643c0, + type: 3} + NetworkPrefabs: + - Override: 0 + Prefab: {fileID: 771575417923360811, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + SourcePrefabToOverride: {fileID: 771575417923360811, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 8059323910720821982, guid: d143db9172e1f234e99d450286b77695, + type: 3} + - Override: 0 + Prefab: {fileID: 1086419352113069865, guid: ed799d5402b325c4f8f41918224a1ba7, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 1086419352113069865, guid: 8a415e2c2210434458836779302bc4d3, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + ReceiveTickrate: 64 + NetworkTickIntervalSec: 0.05 + MaxReceiveEventsPerTickRate: 500 + EventTickrate: 64 + ClientConnectionBufferTimeout: 10 + ConnectionApproval: 0 + ConnectionData: + EnableTimeResync: 0 + TimeResyncInterval: 30 + EnableNetworkVariable: 1 + EnsureNetworkVariableLengthSafety: 0 + EnableSceneManagement: 1 + ForceSamePrefabs: 1 + RecycleNetworkIds: 1 + NetworkIdRecycleDelay: 120 + RpcHashSize: 0 + LoadSceneTimeOut: 120 + EnableMessageBuffering: 1 + MessageBufferTimeout: 20 + EnableNetworkLogs: 1 +--- !u!114 &1024114719 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1024114717} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b84c2d8dfe509a34fb59e2b81f8e1319, type: 3} + m_Name: + m_EditorClassIdentifier: + MessageBufferSize: 65535 + MaxConnections: 100 + MaxSentMessageQueueSize: 512 + ConnectAddress: 127.0.0.1 + ConnectPort: 7777 + ServerListenPort: 7777 + ServerWebsocketListenPort: 8887 + SupportWebsocket: 0 + Channels: [] + UseMLAPIRelay: 0 + MLAPIRelayAddress: 184.72.104.138 + MLAPIRelayPort: 8888 + MessageSendMode: 0 +--- !u!4 &1024114720 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1024114717} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.000061035156, y: 0.000015258789, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1113539278 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1113539280} + - component: {fileID: 1113539281} + - component: {fileID: 1113539279} + m_Layer: 0 + m_Name: PooledPrefabSpawnHandler + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1113539279 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8c48ea35c67e64f7fac22a3f6831ca88, type: 3} + m_Name: + m_EditorClassIdentifier: + AutoSpawnEnable: 1 + InitialSpawnDelay: 0.2 + SpawnsPerSecond: 1 + PoolSize: 128 + ObjectSpeed: 8 + EnableHandler: 0 + RegisterUsingNetworkObject: 0 + ServerObjectToPool: {fileID: 771575417923360811, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + ClientObjectToPool: {fileID: 0} + SwitchScene: {fileID: 0} + SpawnSlider: {fileID: 2058276877} + SpawnSliderValueText: {fileID: 562991980} +--- !u!4 &1113539280 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1113539281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 1983031731 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!1 &1332123091 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1332123092} + m_Layer: 0 + m_Name: Level + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1332123092 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1332123091} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.000000059604645, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 336568649} + - {fileID: 2028091272} + - {fileID: 1857685347} + - {fileID: 57392474} + - {fileID: 1336081255} + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1336081251 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1336081255} + - component: {fileID: 1336081254} + - component: {fileID: 1336081253} + - component: {fileID: 1336081252} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1336081252 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1336081253 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1336081254 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1336081255 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 30.5, y: 0.49999994, z: 0} + m_LocalScale: {x: 1, y: 3, z: 62} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1347823141 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1347823142} + - component: {fileID: 1347823145} + - component: {fileID: 1347823144} + - component: {fileID: 1347823143} + m_Layer: 5 + m_Name: SwitchSceneButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1347823142 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1846334315} + m_Father: {fileID: 34066665} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0} + m_AnchorMax: {x: 0.5, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1347823143 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1347823144} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 34066666} + m_TargetAssemblyTypeName: TestProject.ManualTests.SwitchSceneHandlerAdditive, + Assembly-CSharp + m_MethodName: OnSwitchScene + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &1347823144 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.1981132, g: 0.1981132, b: 0.1981132, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1347823145 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_CullTransparentMesh: 1 +--- !u!1 &1383741137 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1383741138} + - component: {fileID: 1383741140} + - component: {fileID: 1383741139} + m_Layer: 5 + m_Name: SceneName + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1383741138 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383741137} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 290861172} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -139.70001, y: 45.59999} + m_SizeDelta: {x: 250, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1383741139 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383741137} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.990566, g: 0.990566, b: 0.990566, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: First Scene Loaded +--- !u!222 &1383741140 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383741137} + m_CullTransparentMesh: 0 +--- !u!1 &1387688804 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1387688805} + m_Layer: 5 + m_Name: Handle Slide Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1387688805 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1387688804} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1523424137} + m_Father: {fileID: 2058276876} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1523424136 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1523424137} + - component: {fileID: 1523424139} + - component: {fileID: 1523424138} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1523424137 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523424136} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1387688805} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1523424138 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523424136} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1523424139 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523424136} + m_CullTransparentMesh: 1 +--- !u!1 &1549858058 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1549858059} + - component: {fileID: 1549858061} + - component: {fileID: 1549858060} + m_Layer: 5 + m_Name: Fill + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1549858059 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549858058} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1889006547} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 10, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1549858060 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549858058} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1549858061 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549858058} + m_CullTransparentMesh: 1 +--- !u!1 &1588117327 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1588117328} + - component: {fileID: 1588117331} + - component: {fileID: 1588117330} + - component: {fileID: 1588117329} + m_Layer: 5 + m_Name: ToggleClientServerStats + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1588117328 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 599972121} + m_Father: {fileID: 290861172} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 130, y: 51} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1588117329 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1588117330} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 2107482022} + m_TargetAssemblyTypeName: StatsDisplay, Assembly-CSharp + m_MethodName: ToggleClientSever + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &1588117330 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.1981132, g: 0.1981132, b: 0.1981132, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1588117331 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_CullTransparentMesh: 1 +--- !u!1 &1834318145 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1834318148} + - component: {fileID: 1834318147} + - component: {fileID: 1834318146} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1834318146 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1834318145} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1834318147 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1834318145} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &1834318148 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1834318145} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -431, y: -242.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 290861172} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1846334314 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1846334315} + - component: {fileID: 1846334317} + - component: {fileID: 1846334316} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1846334315 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1846334314} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1347823142} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1846334316 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1846334314} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.5058824, b: 0.003921569, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Switch Scene +--- !u!222 &1846334317 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1846334314} + m_CullTransparentMesh: 1 +--- !u!1 &1857685343 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1857685347} + - component: {fileID: 1857685346} + - component: {fileID: 1857685345} + - component: {fileID: 1857685344} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1857685344 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1857685345 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1857685346 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1857685347 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.49999994, z: -30.5} + m_LocalScale: {x: 60, y: 3, z: 1} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1865409448 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 167044834} + m_Modifications: + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_SizeDelta.x + value: -952 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_SizeDelta.y + value: -344 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6963777608485144162, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Name + value: ConnectionModeButtons + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: d725b5588e1b956458798319e6541d84, type: 3} +--- !u!224 &1865409449 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + m_PrefabInstance: {fileID: 1865409448} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1889006546 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1889006547} + m_Layer: 5 + m_Name: Fill Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1889006547 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889006546} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1549858059} + m_Father: {fileID: 2058276876} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.25} + m_AnchorMax: {x: 1, y: 0.75} + m_AnchoredPosition: {x: -5, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &2021718438 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2021718439} + - component: {fileID: 2021718441} + - component: {fileID: 2021718440} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2021718439 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021718438} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2058276876} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.25} + m_AnchorMax: {x: 1, y: 0.75} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2021718440 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021718438} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2021718441 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021718438} + m_CullTransparentMesh: 1 +--- !u!1 &2028091268 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2028091272} + - component: {fileID: 2028091271} + - component: {fileID: 2028091270} + - component: {fileID: 2028091269} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &2028091269 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2028091270 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2028091271 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &2028091272 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.49999994, z: 30.5} + m_LocalScale: {x: 60, y: 3, z: 1} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2058276875 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2058276876} + - component: {fileID: 2058276877} + m_Layer: 5 + m_Name: BoxGeneratorSlider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2058276876 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058276875} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2021718439} + - {fileID: 1889006547} + - {fileID: 1387688805} + - {fileID: 562991979} + m_Father: {fileID: 290861172} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0.99993896, y: -63.99997} + m_SizeDelta: {x: 400, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2058276877 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058276875} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 67db9e8f0e2ae9c40bc1e2b64352a6b4, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1523424138} + m_FillRect: {fileID: 1549858059} + m_HandleRect: {fileID: 1523424137} + m_Direction: 0 + m_MinValue: 0 + m_MaxValue: 550 + m_WholeNumbers: 1 + m_Value: 2 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1113539279} + m_TargetAssemblyTypeName: ServerBoxGenerator, Core + m_MethodName: UpdateSpawnsPerSecond + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &2107482020 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2107482021} + - component: {fileID: 2107482022} + - component: {fileID: 2107482023} + m_Layer: 0 + m_Name: Stats + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2107482021 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2107482020} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 318.45444, y: 110.697815, z: 216.79077} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2107482022 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2107482020} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cb5f3e55f5dd247129d8a4979b80ebbb, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ClientServerToggle: {fileID: 1588117327} +--- !u!114 &2107482023 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2107482020} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 3197939627 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!1001 &2848221156282925290 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 290861172} + m_Modifications: + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMin.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMin.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SizeDelta.x + value: 20 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SizeDelta.y + value: 25 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchoredPosition.x + value: -23.40039 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -23.800293 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247795, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Name + value: ExitButton + objectReference: {fileID: 0} + - target: {fileID: 5266522511616468950, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SceneMenuToLoad + value: + objectReference: {fileID: 11400000, guid: c10d995498e0c514a853c3506031d3fb, + type: 2} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 3200770c16e3b2b4ebe7f604154faac7, type: 3} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity.meta new file mode 100644 index 0000000000..b672e0c94a --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 780f96a61e8ac8e41b638ae8ec3a3236 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset new file mode 100644 index 0000000000..3fad7d44e8 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset @@ -0,0 +1,23 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 39a16938ffb5cd846a9f6df7a686a9c4, type: 3} + m_Name: SceneTransitioningBaseSceneRegistration + m_EditorClassIdentifier: + SceneToReference: {fileID: 102900000, guid: 780f96a61e8ac8e41b638ae8ec3a3236, type: 3} + m_IncludedScenes: + - {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} + - {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} + m_DisplayName: Additive Scene Loading + m_ReferencedScenes: + - SceneTransitioningBase + - SecondSceneAddtive + - ThirdSceneAdditive diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta new file mode 100644 index 0000000000..a112789c5a --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a8d9296fb33f794f95514bf38de3cf9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity new file mode 100644 index 0000000000..eb3eeb3370 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity @@ -0,0 +1,256 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 903034822} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!850595691 &903034822 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &1113539278 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1113539280} + - component: {fileID: 1113539281} + - component: {fileID: 1113539279} + m_Layer: 0 + m_Name: PooledPrefabSpawnHandler + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1113539279 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 398d7c0793567354082687193357a99f, type: 3} + m_Name: + m_EditorClassIdentifier: + AutoSpawnEnable: 1 + InitialSpawnDelay: 0.2 + SpawnsPerSecond: 3 + PoolSize: 128 + ObjectSpeed: 8 + EnableHandler: 0 + RegisterUsingNetworkObject: 0 + ServerObjectToPool: {fileID: 1086419352113069865, guid: ed799d5402b325c4f8f41918224a1ba7, + type: 3} + ClientObjectToPool: {fileID: 0} +--- !u!4 &1113539280 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1113539281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 3066165563 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity.meta new file mode 100644 index 0000000000..17079dbe11 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 41a0239b0c49e2047b7063c822f0df8a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs new file mode 100644 index 0000000000..59cdf672b9 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -0,0 +1,127 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + +using MLAPI; +using MLAPI.SceneManagement; + +namespace TestProject.ManualTests +{ + public class SwitchSceneHandlerAdditive : NetworkBehaviour + { + public static bool ExitingNow { get; internal set; } + + [SerializeField] + private GameObject m_SwitchSceneButtonObject; + + [HideInInspector] + [SerializeField] + private List m_SceneToSwitchTo; + +#if UNITY_EDITOR + [SerializeField] + private List m_SceneAssets; + private void OnValidate() + { + m_SceneToSwitchTo = new List(); + foreach(var sceneAsset in m_SceneAssets) + { + m_SceneToSwitchTo.Add(sceneAsset.name); + } + } +#endif + + private int m_CurrentSceneIndex; + + private void Awake() + { + m_CurrentSceneIndex = 0; + ExitingNow = false; + } + + private void Start() + { + m_SwitchSceneButtonObject.SetActive(false); + StartCoroutine(CheckForVisibility()); + } + + private bool m_ExitingScene; + private void OnDestroy() + { + m_ExitingScene = true; + StopAllCoroutines(); + } + + private IEnumerator CheckForVisibility() + { + while (!m_ExitingScene) + { + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) + { + if (m_SwitchSceneButtonObject) + { + m_SwitchSceneButtonObject.SetActive(true); + } + } + else + { + m_SwitchSceneButtonObject.SetActive(false); + } + + yield return new WaitForSeconds(0.5f); + } + + yield return null; + } + + public override void OnNetworkSpawn() + { + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) + { + if (m_SwitchSceneButtonObject) + { + m_SwitchSceneButtonObject.SetActive(true); + } + } + else + { + m_SwitchSceneButtonObject.SetActive(false); + } + base.OnNetworkSpawn(); + } + + private SceneSwitchProgress m_CurrentSceneSwitchProgress; + + public delegate void OnSceneSwitchBeginDelegateHandler(); + + public event OnSceneSwitchBeginDelegateHandler OnSceneSwitchBegin; + + public void OnSwitchScene() + { + if (m_CurrentSceneIndex < m_SceneToSwitchTo.Count) + { + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) + { + OnSceneSwitchBegin?.Invoke(); + m_ExitingScene = true; + ExitingNow = true; + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.SwitchScene(m_SceneToSwitchTo[m_CurrentSceneIndex], UnityEngine.SceneManagement.LoadSceneMode.Additive); + m_CurrentSceneIndex++; + m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; + } + } + } + + public delegate void OnSceneSwitchCompletedDelegateHandler(); + + public event OnSceneSwitchCompletedDelegateHandler OnSceneSwitchCompleted; + + private void CurrentSceneSwitchProgress_OnComplete(bool timedOut) + { + OnSceneSwitchCompleted?.Invoke(); + } + } +} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta new file mode 100644 index 0000000000..54a2c9df0d --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd82b56fddb35f2418f27a65b6bb3b54 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity new file mode 100644 index 0000000000..f09d219be5 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity @@ -0,0 +1,256 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 903034822} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!850595691 &903034822 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &1113539278 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1113539280} + - component: {fileID: 1113539281} + - component: {fileID: 1113539282} + m_Layer: 0 + m_Name: PooledPrefabSpawnHandler + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1113539280 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1113539281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 156686626 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!114 &1113539282 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 398d7c0793567354082687193357a99f, type: 3} + m_Name: + m_EditorClassIdentifier: + AutoSpawnEnable: 1 + InitialSpawnDelay: 0.2 + SpawnsPerSecond: 3 + PoolSize: 128 + ObjectSpeed: 8 + EnableHandler: 0 + RegisterUsingNetworkObject: 0 + ServerObjectToPool: {fileID: 1086419352113069865, guid: 8a415e2c2210434458836779302bc4d3, + type: 3} + ClientObjectToPool: {fileID: 0} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity.meta new file mode 100644 index 0000000000..ab923a2625 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c6a3d883c8253ee43bca4f2b03797d7b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs new file mode 100644 index 0000000000..f943512bea --- /dev/null +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -0,0 +1,334 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using MLAPI; +using MLAPI.Spawning; + +namespace TestProject.ManualTests +{ + public class NetworkPrefabPoolAdditive : NetworkBehaviour + { + [Header("General Settings")] + public bool AutoSpawnEnable = true; + public float InitialSpawnDelay; + public int SpawnsPerSecond; + public int PoolSize; + public float ObjectSpeed = 10.0f; + + + [Header("Prefab Instance Handling")] + [Tooltip("When enabled, this will utilize the NetworkPrefabHandler to register a custom INetworkPrefabInstanceHandler")] + public bool EnableHandler; + + [Tooltip("When enabled, this will register a custom INetworkPrefabInstanceHandler using a NetworkObject reference")] + public bool RegisterUsingNetworkObject; + + [Tooltip("What is going to be spawned on the server side from the pool.")] + public GameObject ServerObjectToPool; + [Tooltip("What is going to be spawned on the client side from the ")] + public GameObject ClientObjectToPool; + + private bool m_IsSpawningObjects; + + private float m_EntitiesPerFrame; + private float m_DelaySpawning; + + private GameObject m_ObjectToSpawn; + private List m_ObjectPool; + + private MyAdditiveCustomPrefabSpawnHandler m_AdditiveCustomPrefabSpawnHandler; + + /// + /// Called when enabled, if already connected we register any custom prefab spawn handler here + /// + private void OnEnable() + { + //This registers early under the condition of a scene transition + RegisterCustomPrefabHandler(); + } + + /// + /// Handles registering the custom prefab handler + /// + private void RegisterCustomPrefabHandler() + { + // Register the custom spawn handler? + if (m_AdditiveCustomPrefabSpawnHandler == null && EnableHandler) + { + if (NetworkManager && NetworkManager.PrefabHandler != null) + { + m_AdditiveCustomPrefabSpawnHandler = new MyAdditiveCustomPrefabSpawnHandler(this); + if (RegisterUsingNetworkObject) + { + NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool.GetComponent(), m_AdditiveCustomPrefabSpawnHandler); + } + else + { + NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool, m_AdditiveCustomPrefabSpawnHandler); + } + } + else if (!IsServer) + { + Debug.LogWarning($"Failed to register custom spawn handler and {nameof(EnableHandler)} is set to true!"); + } + } + } + + /// + /// When disabled, stop spawning objects + /// + private void OnDisable() + { + m_IsSpawningObjects = false; + if (NetworkManager.Singleton && EnableHandler && m_AdditiveCustomPrefabSpawnHandler != null) + { + var no = ServerObjectToPool.GetComponent(); + NetworkManager.Singleton.PrefabHandler.RemoveHandler(no); + m_AdditiveCustomPrefabSpawnHandler = null; + } + } + + /// + /// General clean up + /// The custom prefab handler is unregistered here + /// + private void OnDestroy() + { + if (NetworkManager != null && NetworkManager.SceneManager != null) + { + NetworkManager.SceneManager.OnSceneSwitchStarted -= SceneManager_OnSceneSwitchStarted; + } + } + + // Start is called before the first frame update + private void Start() + { + SpawnsPerSecond = 3; + NetworkManager.SceneManager.OnSceneSwitchStarted += SceneManager_OnSceneSwitchStarted; + //Call this again in case we didn't have access to the NetworkManager already (i.e. first scene loaded) + RegisterCustomPrefabHandler(); + + } + + private void SceneManager_OnSceneSwitchStarted(AsyncOperation operation) + { + //OnSceneSwitchBegin(); + } + + /// + /// Detect when we are switching scenes in order + /// to assure we stop spawning objects + /// + private void OnSceneSwitchBegin() + { + if (IsServer) + { + StopCoroutine(SpawnObjects()); + + if (m_ObjectPool != null) + { + foreach (var obj in m_ObjectPool) + { + var networkObject = obj.GetComponent(); + if (networkObject.IsSpawned) + { + networkObject.Despawn(); + } + Destroy(obj); + } + m_ObjectPool.Clear(); + } + } + } + + /// + /// Override NetworkBehaviour.NetworkStart + /// + public override void OnNetworkSpawn() + { + InitializeObjectPool(); + if (IsServer) + { + if (isActiveAndEnabled) + { + m_DelaySpawning = Time.realtimeSinceStartup + InitialSpawnDelay; + StartSpawningBoxes(); + + //Make sure our slider reflects the current spawn rate + UpdateSpawnsPerSecond(); + } + } + } + + /// + /// Determines which object is going to be spawned and then + /// initializes the object pool based on + /// + public void InitializeObjectPool() + { + m_ObjectToSpawn = ServerObjectToPool; + if (!IsServer && EnableHandler) + { + m_ObjectToSpawn = ClientObjectToPool; + } + + m_ObjectPool = new List(PoolSize); + + for (int i = 0; i < PoolSize; i++) + { + AddNewInstance(); + } + } + + /// + /// Gets an object from the pool + /// + /// + public GameObject GetObject() + { + if (m_ObjectPool != null) + { + foreach (var obj in m_ObjectPool) + { + if (!obj.activeInHierarchy) + { + obj.SetActive(true); + return obj; + } + } + var newObj = AddNewInstance(); + newObj.SetActive(true); + return newObj; + } + return null; + } + + /// + /// Add a new instance to the object pool + /// + /// new instance of the m_ObjectToSpawn prefab + private GameObject AddNewInstance() + { + var obj = Instantiate(m_ObjectToSpawn); + var no = obj.GetComponent(); + obj.SetActive(false); + m_ObjectPool.Add(obj); + return obj; + } + + /// + /// Starts the + /// + private void StartSpawningBoxes() + { + if (SpawnsPerSecond > 0) + { + StartCoroutine(SpawnObjects()); + } + } + + /// + /// Checks to determine if we need to update our spawns per second calculations + /// + public void UpdateSpawnsPerSecond() + { + // Handle case where the initial value is set to zero and so coroutine needs to be started + if(SpawnsPerSecond > 0 && !m_IsSpawningObjects) + { + StartSpawningBoxes(); + } + else //Handle case where spawning coroutine is running but we set our spawn rate to zero + if (SpawnsPerSecond == 0 && m_IsSpawningObjects) + { + m_IsSpawningObjects = false; + StopCoroutine(SpawnObjects()); + } + + } + + /// + /// Coroutine to spawn boxes + /// + /// + private IEnumerator SpawnObjects() + { + //Exit if we are a client or we happen to not have a NetworkManager + if (NetworkManager == null || (NetworkManager.IsClient && !NetworkManager.IsHost && !NetworkManager.IsServer)) + { + yield return null; + } + + if (m_DelaySpawning > Time.realtimeSinceStartup) + { + yield return new WaitForSeconds(m_DelaySpawning - Time.realtimeSinceStartup); + } + + m_IsSpawningObjects = true; + while (m_IsSpawningObjects) + { + //Start spawning if auto spawn is enabled + if (AutoSpawnEnable) + { + float entitySpawnUpdateRate = 1.0f; + if (SpawnsPerSecond > 0) + { + entitySpawnUpdateRate = 1.0f / Mathf.Min(SpawnsPerSecond, 60.0f); + //While not 100% accurate, this basically allows for higher entities per second generation + m_EntitiesPerFrame = (float)SpawnsPerSecond * entitySpawnUpdateRate; + int entitityCountPerFrame = Mathf.RoundToInt(m_EntitiesPerFrame); + //Spawn (n) entities then wait for 1/60th of a second and repeat + for (int i = 0; i < entitityCountPerFrame; i++) + { + GameObject go = GetObject(); + if (go != null) + { + go.transform.position = transform.position; + + float ang = Random.Range(0.0f, 2 * Mathf.PI); + go.GetComponent().SetDirectionAndVelocity(new Vector3(Mathf.Cos(ang), 0, Mathf.Sin(ang)), ObjectSpeed); + + var no = go.GetComponent(); + if (!no.IsSpawned) + { + no.Spawn(null, true); + } + } + } + } + yield return new WaitForSeconds(entitySpawnUpdateRate); + } + else //Just hang out until it is enabled + { + yield return new WaitForSeconds(1.0f); + } + } + } + } + + + /// + /// The custom prefab handler that returns an object from the prefab pool + /// + public class MyAdditiveCustomPrefabSpawnHandler : INetworkPrefabInstanceHandler + { + private NetworkPrefabPoolAdditive m_PrefabPool; + public NetworkObject HandleNetworkPrefabSpawn(ulong ownerClientId, Vector3 position, Quaternion rotation) + { + var obj = m_PrefabPool.GetObject(); + obj.transform.position = position; + obj.transform.rotation = rotation; + return obj.GetComponent(); + } + public void HandleNetworkPrefabDestroy(NetworkObject networkObject) + { + networkObject.transform.position = Vector3.zero; + networkObject.gameObject.SetActive(false); + } + + public MyAdditiveCustomPrefabSpawnHandler(NetworkPrefabPoolAdditive objectPool) + { + m_PrefabPool = objectPool; + } + } +} diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs.meta b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs.meta new file mode 100644 index 0000000000..45f6535d2a --- /dev/null +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 398d7c0793567354082687193357a99f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs index 55236ef71a..6633e410eb 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs @@ -76,7 +76,10 @@ private void OnClientConnectedCallback(ulong clientId) /// private void OnDestroy() { - NetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback; + if (NetworkManager != null) + { + NetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback; + } } /// diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index 0f1a066d92..5b8d35134c 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -41,4 +41,13 @@ EditorBuildSettings: - enabled: 1 path: Assets/Tests/Manual/NetworkSceneManagerCallbacks/SceneWeAreSwitchingFrom.unity guid: 073bd2111475c0643be45b7abe6a97ad + - enabled: 1 + path: Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity + guid: 780f96a61e8ac8e41b638ae8ec3a3236 + - enabled: 1 + path: Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity + guid: 41a0239b0c49e2047b7063c822f0df8a + - enabled: 1 + path: Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity + guid: c6a3d883c8253ee43bca4f2b03797d7b m_configObjects: {} From 8f7227bf707e0c99a37ab4cb395d40c09d25e7ca Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 3 Jul 2021 16:03:07 -0500 Subject: [PATCH 002/106] refator Updating basic scene registration to accept SceneAssets as opposed to string based names. --- .../Editor/NetworkManagerEditor.cs | 20 ++++++ .../Runtime/Configuration/NetworkConfig.cs | 12 +++- .../Runtime/Core/NetworkManager.cs | 66 +++++++++++++++---- .../SceneTransitioningBase.unity | 4 ++ 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs b/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs index 960d1fa1ba..ec0091d842 100644 --- a/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs +++ b/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs @@ -47,6 +47,7 @@ public class NetworkManagerEditor : UnityEditor.Editor private ReorderableList m_NetworkPrefabsList; private ReorderableList m_RegisteredScenesList; + private ReorderableList m_RegisteredSceneAssetsList; private NetworkManager m_NetworkManager; private bool m_Initialized; @@ -235,6 +236,24 @@ private void OnEnable() EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - padding, EditorGUIUtility.singleLineHeight), element, GUIContent.none); }; m_RegisteredScenesList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Registered Scene Names"); + + m_RegisteredSceneAssetsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.RegisteredSceneAssets)), true, true, true, true); + m_RegisteredSceneAssetsList.drawElementCallback = (rect, index, isActive, isFocused) => + { + var sceneAsset = m_RegisteredSceneAssetsList.serializedProperty.GetArrayElementAtIndex(index); + int firstLabelWidth = 38; + int padding = 2; + + EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), $"{index}"); + 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, "Registered Scene Entries"); + + m_RegisteredSceneAssetsList.onAddCallback = (registeredList) => + { + m_NetworkManager.NetworkConfig.RegisteredSceneAssets.Add(null); + }; } public override void OnInspectorGUI() @@ -272,6 +291,7 @@ public override void OnInspectorGUI() using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) { m_RegisteredScenesList.DoLayoutList(); + m_RegisteredSceneAssetsList.DoLayoutList(); EditorGUILayout.Space(); } diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs index 2e74c3f7fe..21a91acf5a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs @@ -1,6 +1,10 @@ using System; using System.Collections.Generic; using UnityEngine; +using UnityEngine.SceneManagement; +#if UNITY_EDITOR +using UnityEditor; +#endif using System.Linq; using MLAPI.Transports; using MLAPI.Hashing; @@ -33,6 +37,11 @@ public class NetworkConfig [Tooltip("The Scenes that can be switched to by the server")] 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. @@ -48,7 +57,7 @@ public class NetworkConfig public GameObject PlayerPrefab; /// - /// A list of spawnable prefabs + /// A list of prefabs that can be dynamically spawned. /// [SerializeField] [Tooltip("The prefabs that can be spawned across the network")] @@ -336,3 +345,4 @@ public bool CompareConfig(ulong hash) } } } + diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index e37d1cf57a..f0e6c08c7a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -2,6 +2,10 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + using MLAPI.Logging; using MLAPI.Configuration; using MLAPI.Internal; @@ -220,7 +224,7 @@ public ulong LocalClientId public NetworkConfig NetworkConfig; /// - /// The current hostname we are connected to, used to validate certificate + /// The current host name we are connected to, used to validate certificate /// public string ConnectedHostname { get; private set; } @@ -243,27 +247,65 @@ private void OnValidate() } var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); - var activeSceneName = activeScene.name; - if (!NetworkConfig.RegisteredScenes.Contains(activeSceneName)) + + if (NetworkConfig.EnableSceneManagement) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + //var activeSceneName = activeScene.name; + //if (!NetworkConfig.RegisteredScenes.Contains(activeSceneName)) + //{ + // if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + // { + // NetworkLog.LogWarning("Active scene is not registered as a network scene. The MLAPI has added it"); + // } + + // NetworkConfig.RegisteredScenes.Add(activeSceneName); + // EditorApplication.delayCall += () => + // { + // if (!EditorApplication.isPlaying) + // { + // EditorUtility.SetDirty(this); + // UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); + // } + // }; + //} + + var activeSceneAsset = AssetDatabase.LoadAssetAtPath(activeScene.path); + if (!NetworkConfig.RegisteredSceneAssets.Contains(activeSceneAsset)) { - NetworkLog.LogWarning("Active scene is not registered as a network scene. The MLAPI has added it"); + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogInfo($"Active scene {activeSceneAsset.name} is not registered as a network scene. Since scene management is enabled, it was automatically added."); + } + + NetworkConfig.RegisteredSceneAssets.Add(activeSceneAsset); + EditorApplication.delayCall += () => + { + if (!EditorApplication.isPlaying) + { + EditorUtility.SetDirty(this); + UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); + } + }; } - NetworkConfig.RegisteredScenes.Add(activeSceneName); - UnityEditor.EditorApplication.delayCall += () => + if(NetworkConfig.RegisteredSceneAssets.Count > 0) { - if (!UnityEditor.EditorApplication.isPlaying) + foreach(var sceneAsset in NetworkConfig.RegisteredSceneAssets) { - UnityEditor.EditorUtility.SetDirty(this); - UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); + if(NetworkConfig.RegisteredScenes == null) + { + NetworkConfig.RegisteredScenes = new List(); + } + if(!NetworkConfig.RegisteredScenes.Contains(sceneAsset.name)) + { + NetworkConfig.RegisteredScenes.Add(sceneAsset.name); + } } - }; + } } // If the scene is not dirty or the asset database is currently updating then we can skip updating the NetworkPrefab information - if (!activeScene.isDirty || UnityEditor.EditorApplication.isUpdating) + if (!activeScene.isDirty || EditorApplication.isUpdating) { return; } diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity index 433379e212..abc44e992c 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity @@ -1017,6 +1017,10 @@ MonoBehaviour: - SceneTransitioningBase - SecondSceneAdditive - ThirdSceneAdditive + RegisteredSceneAssets: + - {fileID: 102900000, guid: 780f96a61e8ac8e41b638ae8ec3a3236, type: 3} + - {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} + - {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} AllowRuntimeSceneChanges: 0 PlayerPrefab: {fileID: 4079352819444256614, guid: c16f03336b6104576a565ef79ad643c0, type: 3} From 000319b86b0a4dd0c1c9079ac25c13d8f45e4dd6 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 3 Jul 2021 16:12:47 -0500 Subject: [PATCH 003/106] refactor Removing the automatic addition of a scene asset to NetworkConfig. This has been a problematic approach to assuring the scene containing the NetworkManager is included. --- .../Editor/NetworkManagerEditor.cs | 15 -------- .../Runtime/Configuration/NetworkConfig.cs | 4 +-- .../Runtime/Core/NetworkManager.cs | 34 +++++++++---------- 3 files changed, 19 insertions(+), 34 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs b/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs index ec0091d842..4b2890f6e9 100644 --- a/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs +++ b/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs @@ -46,7 +46,6 @@ public class NetworkManagerEditor : UnityEditor.Editor private SerializedProperty m_MessageBufferTimeoutProperty; private ReorderableList m_NetworkPrefabsList; - private ReorderableList m_RegisteredScenesList; private ReorderableList m_RegisteredSceneAssetsList; private NetworkManager m_NetworkManager; @@ -225,18 +224,6 @@ private void OnEnable() }; m_NetworkPrefabsList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "NetworkPrefabs"); - m_RegisteredScenesList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.RegisteredScenes)), true, true, true, true); - m_RegisteredScenesList.drawElementCallback = (rect, index, isActive, isFocused) => - { - var element = m_RegisteredScenesList.serializedProperty.GetArrayElementAtIndex(index); - int firstLabelWidth = 50; - int padding = 20; - - EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), "Name"); - EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - padding, EditorGUIUtility.singleLineHeight), element, GUIContent.none); - }; - m_RegisteredScenesList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Registered Scene Names"); - m_RegisteredSceneAssetsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.RegisteredSceneAssets)), true, true, true, true); m_RegisteredSceneAssetsList.drawElementCallback = (rect, index, isActive, isFocused) => { @@ -273,7 +260,6 @@ public override void OnInspectorGUI() } } - if (!m_NetworkManager.IsServer && !m_NetworkManager.IsClient) { serializedObject.Update(); @@ -290,7 +276,6 @@ public override void OnInspectorGUI() using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) { - m_RegisteredScenesList.DoLayoutList(); m_RegisteredSceneAssetsList.DoLayoutList(); EditorGUILayout.Space(); } diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs index 21a91acf5a..409a833af1 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs @@ -32,9 +32,9 @@ public class NetworkConfig public NetworkTransport NetworkTransport = null; /// - /// A list of SceneNames that can be used during networked games. + /// The list of SceneNames built from the RegisteredSceneAssets list /// - [Tooltip("The Scenes that can be switched to by the server")] + [HideInInspector] public List RegisteredScenes = new List(); #if UNITY_EDITOR diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index f0e6c08c7a..c62bec30b9 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -269,24 +269,24 @@ private void OnValidate() // }; //} - var activeSceneAsset = AssetDatabase.LoadAssetAtPath(activeScene.path); - if (!NetworkConfig.RegisteredSceneAssets.Contains(activeSceneAsset)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogInfo($"Active scene {activeSceneAsset.name} is not registered as a network scene. Since scene management is enabled, it was automatically added."); - } + //var activeSceneAsset = AssetDatabase.LoadAssetAtPath(activeScene.path); + //if (!NetworkConfig.RegisteredSceneAssets.Contains(activeSceneAsset)) + //{ + // if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + // { + // NetworkLog.LogInfo($"Active scene {activeSceneAsset.name} is not registered as a network scene. Since scene management is enabled, it was automatically added."); + // } - NetworkConfig.RegisteredSceneAssets.Add(activeSceneAsset); - EditorApplication.delayCall += () => - { - if (!EditorApplication.isPlaying) - { - EditorUtility.SetDirty(this); - UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); - } - }; - } + // NetworkConfig.RegisteredSceneAssets.Add(activeSceneAsset); + // EditorApplication.delayCall += () => + // { + // if (!EditorApplication.isPlaying && !EditorApplication.isUpdating) + // { + // EditorUtility.SetDirty(this); + // UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); + // } + // }; + //} if(NetworkConfig.RegisteredSceneAssets.Count > 0) { From 19bd9c3ca99ac9e2e498777feb9f8e76b22388eb Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 3 Jul 2021 16:27:53 -0500 Subject: [PATCH 004/106] refactor - wip Adjusting scene related commands. Keeping the original commands in place, but providing two new commands: SCENE_EVENT: This will contain sub-commands and additional information specific to the command. SCENE_NOTIFICATION: This is more for providing a mechanism to send various notifications (i.e. scene switch complete, scene loaded, scene unloaded, errors, etc) with the ability to include additional information about the event. --- .../Runtime/Configuration/NetworkConstants.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs index 3222f27f78..98785d5eb1 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs @@ -11,8 +11,6 @@ internal static class NetworkConstants internal const byte CONNECTION_APPROVED = 4; internal const byte ADD_OBJECT = 5; internal const byte DESTROY_OBJECT = 6; - internal const byte SWITCH_SCENE = 7; - internal const byte CLIENT_SWITCH_SCENE_COMPLETED = 8; internal const byte CHANGE_OWNER = 9; internal const byte ADD_OBJECTS = 10; internal const byte TIME_SYNC = 11; @@ -26,7 +24,11 @@ internal static class NetworkConstants internal const byte SNAPSHOT_DATA = 25; internal const byte SERVER_RPC = 30; internal const byte CLIENT_RPC = 31; - internal const byte INVALID = 32; + internal const byte SWITCH_SCENE = 32; + internal const byte SCENE_EVENT = 33; + internal const byte SCENE_EVENT_NOTIFICATION = 34; + internal const byte CLIENT_SWITCH_SCENE_COMPLETED = 35; + internal const byte INVALID = 36; internal static readonly string[] MESSAGE_NAMES = { @@ -37,8 +39,8 @@ internal static class NetworkConstants "CONNECTION_APPROVED", "ADD_OBJECT", "DESTROY_OBJECT", - "SWITCH_SCENE", - "CLIENT_SWITCH_SCENE_COMPLETED", + "", + "", "CHANGE_OWNER", "ADD_OBJECTS", "TIME_SYNC", @@ -62,7 +64,11 @@ internal static class NetworkConstants "", "SERVER_RPC", "CLIENT_RPC", - "INVALID" // 32 + "SWITCH_SCENE", + "SCENE_EVENT", // New Scene Event command (to replace and add Switch, Load, and Unload with sub-commands and additional information to define the scene event) + "SCENE_EVENT_NOTIFICATION", // New Scene Event Notification (to provide a single command for scene event notifications, sub-commands and additional information will be included) + "CLIENT_SWITCH_SCENE_COMPLETED", + "INVALID" // 36 }; } } From 7f228a33f619a427fd8637c9c5c1bee65b13e7b9 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 6 Jul 2021 17:07:01 -0500 Subject: [PATCH 005/106] feat Added new SCENE_EVENT that will handle "all things scene relative" with the only exception of the approval process. That will need to be handled in a different PR as that requires a bunch of additional changes. Focus for this branch is to get additive scene loading and unloading working. --- .../Runtime/Configuration/NetworkConstants.cs | 2 - .../Runtime/Core/NetworkManager.cs | 80 ++-- .../Messaging/InternalMessageHandler.cs | 98 ++++- .../SceneManagement/NetworkSceneManager.cs | 343 ++++++++++++++---- .../NetworkManagerMessageHandlerTests.cs | 40 +- 5 files changed, 399 insertions(+), 164 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs index 98785d5eb1..d3bdf99cd0 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs @@ -66,8 +66,6 @@ internal static class NetworkConstants "CLIENT_RPC", "SWITCH_SCENE", "SCENE_EVENT", // New Scene Event command (to replace and add Switch, Load, and Unload with sub-commands and additional information to define the scene event) - "SCENE_EVENT_NOTIFICATION", // New Scene Event Notification (to provide a single command for scene event notifications, sub-commands and additional information will be included) - "CLIENT_SWITCH_SCENE_COMPLETED", "INVALID" // 36 }; } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index c62bec30b9..17bef1baf6 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -246,48 +246,8 @@ private void OnValidate() } } - var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); - if (NetworkConfig.EnableSceneManagement) { - //var activeSceneName = activeScene.name; - //if (!NetworkConfig.RegisteredScenes.Contains(activeSceneName)) - //{ - // if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - // { - // NetworkLog.LogWarning("Active scene is not registered as a network scene. The MLAPI has added it"); - // } - - // NetworkConfig.RegisteredScenes.Add(activeSceneName); - // EditorApplication.delayCall += () => - // { - // if (!EditorApplication.isPlaying) - // { - // EditorUtility.SetDirty(this); - // UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); - // } - // }; - //} - - //var activeSceneAsset = AssetDatabase.LoadAssetAtPath(activeScene.path); - //if (!NetworkConfig.RegisteredSceneAssets.Contains(activeSceneAsset)) - //{ - // if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - // { - // NetworkLog.LogInfo($"Active scene {activeSceneAsset.name} is not registered as a network scene. Since scene management is enabled, it was automatically added."); - // } - - // NetworkConfig.RegisteredSceneAssets.Add(activeSceneAsset); - // EditorApplication.delayCall += () => - // { - // if (!EditorApplication.isPlaying && !EditorApplication.isUpdating) - // { - // EditorUtility.SetDirty(this); - // UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene); - // } - // }; - //} - if(NetworkConfig.RegisteredSceneAssets.Count > 0) { foreach(var sceneAsset in NetworkConfig.RegisteredSceneAssets) @@ -304,6 +264,8 @@ private void OnValidate() } } + 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 if (!activeScene.isDirty || EditorApplication.isUpdating) { @@ -1284,13 +1246,27 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, } break; - case NetworkConstants.SWITCH_SCENE: - if (IsClient) + //case NetworkConstants.SWITCH_SCENE: + // { + // if (IsClient) + // { + // MessageHandler.HandleSwitchScene(clientId, messageStream); + + // } + // break; + // } + case NetworkConstants.SCENE_EVENT: { - MessageHandler.HandleSwitchScene(clientId, messageStream); + if (SceneManager != null) + { + SceneManager.HandleSceneEvent(clientId, messageStream); + } + else + { + Debug.LogError($"Received {nameof(NetworkConstants.SCENE_EVENT)} but {nameof(SceneManager)} is null!"); + } + break; } - - break; case NetworkConstants.CHANGE_OWNER: if (IsClient) { @@ -1335,24 +1311,12 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, break; case NetworkConstants.NAMED_MESSAGE: MessageHandler.HandleNamedMessage(clientId, messageStream); - break; - case NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED: - if (IsServer && NetworkConfig.EnableSceneManagement) - { - MessageHandler.HandleClientSwitchSceneCompleted(clientId, messageStream); - } - else if (!NetworkConfig.EnableSceneManagement) - { - NetworkLog.LogWarning($"Server received {nameof(NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED)} from client id {clientId}"); - } - break; case NetworkConstants.ALL_CLIENTS_LOADED_SCENE: if (IsClient) { MessageHandler.HandleAllClientsSwitchSceneCompleted(clientId, messageStream); } - break; case NetworkConstants.SERVER_LOG: if (IsServer && NetworkConfig.EnableNetworkLogs) @@ -1687,6 +1651,8 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? if (ownerClientId != ServerClientId) { + + // Don't send any data over the wire if the host "connected" using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 47fb77968d..ec0ee20989 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -135,6 +135,80 @@ void OnSceneLoadComplete() } } + public void HandleConnectionApprovedOriginal(ulong clientId, Stream stream, float receiveTime) + { + using (var reader = PooledNetworkReader.Get(stream)) + { + NetworkManager.LocalClientId = reader.ReadUInt64Packed(); + + uint sceneIndex = 0; + var sceneSwitchProgressGuid = new Guid(); + + if (NetworkManager.NetworkConfig.EnableSceneManagement) + { + sceneIndex = reader.ReadUInt32Packed(); + sceneSwitchProgressGuid = new Guid(reader.ReadByteArray()); + } + + bool sceneSwitch = NetworkManager.NetworkConfig.EnableSceneManagement && NetworkManager.SceneManager.HasSceneMismatch(sceneIndex); + + float netTime = reader.ReadSinglePacked(); + NetworkManager.UpdateNetworkTime(clientId, netTime, receiveTime, true); + + NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); + + + void DelayedSpawnAction(Stream continuationStream) + { + + using (var continuationReader = PooledNetworkReader.Get(continuationStream)) + { + if (!NetworkManager.NetworkConfig.EnableSceneManagement) + { + NetworkManager.SpawnManager.DestroySceneObjects(); + } + else + { + NetworkManager.SceneManager.PopulateScenePlacedObjects(SceneManager.GetActiveScene()); + } + + var objectCount = continuationReader.ReadUInt32Packed(); + for (int i = 0; i < objectCount; i++) + { + NetworkObject.DeserializeSceneObject(continuationStream as NetworkBuffer, continuationReader, m_NetworkManager); + } + + NetworkManager.IsConnectedClient = true; + NetworkManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId); + } + } + + if (sceneSwitch) + { + UnityAction onSceneLoaded = null; + + var continuationBuffer = new NetworkBuffer(); + continuationBuffer.CopyUnreadFrom(stream); + continuationBuffer.Position = 0; + + void OnSceneLoadComplete() + { + SceneManager.activeSceneChanged -= onSceneLoaded; + NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + DelayedSpawnAction(continuationBuffer); + } + + onSceneLoaded = (oldScene, newScene) => { OnSceneLoadComplete(); }; + SceneManager.activeSceneChanged += onSceneLoaded; + m_NetworkManager.SceneManager.OnFirstSceneSwitchSync(sceneIndex, sceneSwitchProgressGuid); + } + else + { + DelayedSpawnAction(stream); + } + } + } + public void HandleAddObject(ulong clientId, Stream stream) { using (var reader = PooledNetworkReader.Get(stream)) @@ -204,17 +278,19 @@ public void HandleDestroyObject(ulong clientId, Stream stream) public void HandleSwitchScene(ulong clientId, Stream stream) { - using (var reader = PooledNetworkReader.Get(stream)) - { - uint sceneIndex = reader.ReadUInt32Packed(); - var switchSceneGuid = new Guid(reader.ReadByteArray()); - var isAdditivelyLoadedScene = reader.ReadBool(); - var objectBuffer = new NetworkBuffer(); - objectBuffer.CopyUnreadFrom(stream); - objectBuffer.Position = 0; - - m_NetworkManager.SceneManager.OnSceneSwitch(sceneIndex, switchSceneGuid, objectBuffer, isAdditivelyLoadedScene ? LoadSceneMode.Additive:LoadSceneMode.Single); - } + Debug.LogError("HandleSwitchScene is no longer supported!\n"); + //m_NetworkManager.SceneManager.OnSceneSwitch(stream); + //using (var reader = PooledNetworkReader.Get(stream)) + //{ + // uint sceneIndex = reader.ReadUInt32Packed(); + // var switchSceneGuid = new Guid(reader.ReadByteArray()); + // var isAdditivelyLoadedScene = reader.ReadBool(); + // var objectBuffer = new NetworkBuffer(); + // objectBuffer.CopyUnreadFrom(stream); + // objectBuffer.Position = 0; + + // m_NetworkManager.SceneManager.OnSceneSwitch(sceneIndex, switchSceneGuid, objectBuffer, isAdditivelyLoadedScene ? LoadSceneMode.Additive:LoadSceneMode.Single); + //} } public void HandleClientSwitchSceneCompleted(ulong clientId, Stream stream) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 9a7066346c..50f9c54650 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -11,6 +11,7 @@ using UnityEngine; using UnityEngine.SceneManagement; using MLAPI.Messaging.Buffering; +using MLAPI.Serialization; using MLAPI.Transports; namespace MLAPI.SceneManagement @@ -87,11 +88,14 @@ public class NetworkSceneManager internal static Guid CurrentSceneSwitchProgressGuid = new Guid(); internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + internal SceneEventData SceneEventData; + private NetworkManager m_NetworkManager { get; } internal NetworkSceneManager(NetworkManager networkManager) { m_NetworkManager = networkManager; + SceneEventData = new SceneEventData(); } internal void SetCurrentSceneIndex() @@ -135,7 +139,7 @@ public void AddRuntimeSceneName(string sceneName, uint index) /// The name of the scene to switch to /// The mode to load the scene (Additive vs Single) /// SceneSwitchProgress - public SceneSwitchProgress SwitchScene(string sceneName,LoadSceneMode loadSceneMode = LoadSceneMode.Single, List scenesToUnload = null) + public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single, List scenesToUnload = null) { if (!m_NetworkManager.IsServer) { @@ -178,6 +182,12 @@ public SceneSwitchProgress SwitchScene(string sceneName,LoadSceneMode loadSceneM SceneSwitchProgresses.Add(switchSceneProgress.Guid, switchSceneProgress); CurrentSceneSwitchProgressGuid = switchSceneProgress.Guid; + SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; + CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; + SceneEventData.SceneIndex = CurrentActiveSceneIndex; + SceneEventData.LoadSceneMode = loadSceneMode; + switchSceneProgress.OnClientLoadedScene += clientId => { OnNotifyServerClientLoadedScene?.Invoke(switchSceneProgress, clientId); }; switchSceneProgress.OnComplete += timedOut => { @@ -209,7 +219,7 @@ public SceneSwitchProgress SwitchScene(string sceneName,LoadSceneMode loadSceneM s_NextSceneName = sceneName; - sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(switchSceneProgress.Guid, null, loadSceneMode); }; + sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); OnSceneSwitchStarted?.Invoke(sceneLoad); @@ -217,9 +227,11 @@ public SceneSwitchProgress SwitchScene(string sceneName,LoadSceneMode loadSceneM } // Called on client - internal void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream objectStream, LoadSceneMode loadSceneMode) + internal void OnSceneSwitch(Stream objectStream) { - if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) + SceneEventData.CopyUndreadFromStream(objectStream); + + if (!SceneIndexToString.TryGetValue(SceneEventData.SceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { @@ -229,7 +241,7 @@ internal void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream object return; } - if (loadSceneMode == LoadSceneMode.Single) + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { // Move ALL NetworkObjects to the temp scene MoveObjectsToDontDestroyOnLoad(); @@ -237,11 +249,11 @@ internal void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream object IsSpawnedObjectsPendingInDontDestroyOnLoad = true; - var sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); + var sceneLoad = SceneManager.LoadSceneAsync(sceneName, SceneEventData.LoadSceneMode); s_NextSceneName = sceneName; - sceneLoad.completed += asyncOp2 => OnSceneLoaded(switchSceneGuid, objectStream, loadSceneMode); + sceneLoad.completed += asyncOp2 => OnSceneLoaded(); OnSceneSwitchStarted?.Invoke(sceneLoad); } @@ -284,47 +296,16 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) - /// - /// Should be invoked on both the client and server side after: - /// -- A new scene has been loaded - /// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene. - /// Added the ability to choose not to clear the scene placed objects for additive scene loading. - /// - internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) - { - if (clearScenePlacedObjects) - { - ScenePlacedObjects.Clear(); - } - - var networkObjects = UnityEngine.Object.FindObjectsOfType(); - - // Just add every NetworkObject found that isn't already in the list - // If any "non-in-scene placed NetworkObjects" are added to this list it shouldn't matter - // The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash - foreach (var networkObjectInstance in networkObjects) - { - if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) - { - // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) - if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy) - { - ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, networkObjectInstance); - } - } - } - } - private void OnSceneLoaded(Guid switchSceneGuid, Stream objectStream, LoadSceneMode loadSceneMode) + private void OnSceneLoaded() { - CurrentActiveSceneIndex = SceneNameToIndex[s_NextSceneName]; var nextScene = SceneManager.GetSceneByName(s_NextSceneName); SceneManager.SetActiveScene(nextScene); //Get all NetworkObjects loaded by the scene PopulateScenePlacedObjects(nextScene); - if (loadSceneMode == LoadSceneMode.Single) + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { // Move all objects to the new scene MoveObjectsToScene(nextScene); @@ -336,15 +317,15 @@ private void OnSceneLoaded(Guid switchSceneGuid, Stream objectStream, LoadSceneM if (m_NetworkManager.IsServer) { - OnServerLoadedScene(switchSceneGuid, loadSceneMode); + OnServerLoadedScene(); } else { - OnClientLoadedScene(switchSceneGuid, objectStream, loadSceneMode); + OnClientLoadedScene(); } } - private void OnServerLoadedScene(Guid switchSceneGuid, LoadSceneMode loadSceneMode) + private void OnServerLoadedScene() { // Register in-scene placed NetworkObjects with MLAPI foreach (var keyValuePair in ScenePlacedObjects) @@ -360,32 +341,13 @@ private void OnServerLoadedScene(Guid switchSceneGuid, LoadSceneMode loadSceneMo if (m_NetworkManager.ConnectedClientsList[j].ClientId != m_NetworkManager.ServerClientId) { using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteUInt32Packed(CurrentActiveSceneIndex); - writer.WriteByteArray(switchSceneGuid.ToByteArray()); - writer.WriteBool(loadSceneMode == LoadSceneMode.Additive); - - uint sceneObjectsToSpawn = 0; - - foreach (var keyValuePair in ScenePlacedObjects) + using (var writer = PooledNetworkWriter.Get(buffer)) { - if (keyValuePair.Value.Observers.Contains(m_NetworkManager.ConnectedClientsList[j].ClientId)) - { - sceneObjectsToSpawn++; - } - } + SynchronizeInSceneObjects(m_NetworkManager.ConnectedClientsList[j].ClientId,writer); - // Write number of scene objects to spawn - writer.WriteUInt32Packed(sceneObjectsToSpawn); - foreach (var keyValuePair in ScenePlacedObjects) - { - if (keyValuePair.Value.Observers.Contains(m_NetworkManager.ConnectedClientsList[j].ClientId)) - { - keyValuePair.Value.SerializeSceneObject(writer, m_NetworkManager.ConnectedClientsList[j].ClientId); - } + m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } - m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SWITCH_SCENE, NetworkChannel.Internal, buffer); } } } @@ -393,7 +355,7 @@ private void OnServerLoadedScene(Guid switchSceneGuid, LoadSceneMode loadSceneMo // Tell server that scene load is completed if (m_NetworkManager.IsHost) { - OnClientSwitchSceneCompleted(m_NetworkManager.LocalClientId, switchSceneGuid); + OnClientSwitchSceneCompleted(m_NetworkManager.LocalClientId, SceneEventData.SwitchSceneGuid); } s_IsSwitching = false; @@ -401,25 +363,27 @@ private void OnServerLoadedScene(Guid switchSceneGuid, LoadSceneMode loadSceneMo OnSceneSwitched?.Invoke(); } - private void OnClientLoadedScene(Guid switchSceneGuid, Stream objectStream, LoadSceneMode loadSceneMode) + private void OnClientLoadedScene() { var networkObjects = UnityEngine.Object.FindObjectsOfType(); - using (var reader = PooledNetworkReader.Get(objectStream)) + using (var reader = PooledNetworkReader.Get(SceneEventData.InternalBuffer)) { var newObjectsCount = reader.ReadUInt32Packed(); for (int i = 0; i < newObjectsCount; i++) { - NetworkObject.DeserializeSceneObject(objectStream as Serialization.NetworkBuffer, reader, m_NetworkManager); + NetworkObject.DeserializeSceneObject(SceneEventData.InternalBuffer as NetworkBuffer, reader, m_NetworkManager); } } using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteByteArray(switchSceneGuid.ToByteArray()); - m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, NetworkChannel.Internal, buffer); + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH_COMPLETE; + writer.WriteObjectPacked(SceneEventData); + //writer.WriteByteArray(SceneEventData.SwitchSceneGuid.ToByteArray()); + m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } s_IsSwitching = false; @@ -427,6 +391,36 @@ private void OnClientLoadedScene(Guid switchSceneGuid, Stream objectStream, Load OnSceneSwitched?.Invoke(); } + + + internal void SynchronizeInSceneObjects(ulong clientId, NetworkWriter writer) + { + writer.WriteObjectPacked(SceneEventData); + //writer.WriteUInt32Packed(CurrentActiveSceneIndex); + //writer.WriteByteArray(switchSceneGuid.ToByteArray()); + //writer.WriteBool(loadSceneMode == LoadSceneMode.Additive); + + uint sceneObjectsToSpawn = 0; + + foreach (var keyValuePair in ScenePlacedObjects) + { + if (keyValuePair.Value.Observers.Contains(clientId)) + { + sceneObjectsToSpawn++; + } + } + + // Write number of scene objects to spawn + writer.WriteUInt32Packed(sceneObjectsToSpawn); + foreach (var keyValuePair in ScenePlacedObjects) + { + if (keyValuePair.Value.Observers.Contains(clientId)) + { + keyValuePair.Value.SerializeSceneObject(writer, clientId); + } + } + } + internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; // Called on server @@ -444,7 +438,6 @@ internal void OnClientSwitchSceneCompleted(ulong clientId, Guid switchSceneGuid) } } - internal void RemoveClientFromSceneSwitchProgresses(ulong clientId) { foreach (var switchSceneProgress in SceneSwitchProgresses.Values) @@ -460,7 +453,7 @@ private void MoveObjectsToDontDestroyOnLoad() foreach (var sobj in objectsToKeep) { - //In case an object has been set as a child of another object it has to be unchilded in order to be moved from one scene to another. + //In case an object has been set as a child of another object it has to be removed from the parent in order to be moved from one scene to another. if (sobj.gameObject.transform.parent != null) { sobj.gameObject.transform.parent = null; @@ -470,6 +463,37 @@ private void MoveObjectsToDontDestroyOnLoad() } } + /// + /// Should be invoked on both the client and server side after: + /// -- A new scene has been loaded + /// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene. + /// Added the ability to choose not to clear the scene placed objects for additive scene loading. + /// + internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) + { + if (clearScenePlacedObjects) + { + ScenePlacedObjects.Clear(); + } + + var networkObjects = UnityEngine.Object.FindObjectsOfType(); + + // Just add every NetworkObject found that isn't already in the list + // If any "non-in-scene placed NetworkObjects" are added to this list it shouldn't matter + // The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash + foreach (var networkObjectInstance in networkObjects) + { + if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) + { + // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) + if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy) + { + ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, networkObjectInstance); + } + } + } + } + private void MoveObjectsToScene(Scene scene) { // Move ALL NetworkObjects to the temp scene @@ -477,7 +501,7 @@ private void MoveObjectsToScene(Scene scene) foreach (var sobj in objectsToKeep) { - //In case an object has been set as a child of another object it has to be unchilded in order to be moved from one scene to another. + //In case an object has been set as a child of another object it has to be removed from the parent in order to be moved from one scene to another. if (sobj.gameObject.transform.parent != null) { sobj.gameObject.transform.parent = null; @@ -491,5 +515,176 @@ internal void AllClientsReady(ulong[] clientIds, ulong[] timedOutClientIds) { OnNotifyClientAllClientsLoadedScene?.Invoke(clientIds, timedOutClientIds); } + + private void HandleClientSceneEvent(Stream stream) + { + switch (SceneEventData.SceneEventType) + { + case SceneEventData.SceneEventTypes.SWITCH: + { + OnSceneSwitch(stream); + break; + } + default: + { + Debug.LogWarning($"{SceneEventData.SceneEventType} is not currently supported!"); + break; + } + } + } + + private void HandleServerSceneEvent(ulong clientId, Stream stream) + { + switch (SceneEventData.SceneEventType) + { + case SceneEventData.SceneEventTypes.SWITCH_COMPLETE: + { + OnClientSwitchSceneCompleted(clientId,SceneEventData.SwitchSceneGuid); + break; + } + default: + { + Debug.LogWarning($"{SceneEventData.SceneEventType} is not currently supported!"); + break; + } + } + } + + + public void HandleSceneEvent(ulong clientId, Stream stream) + { + if (m_NetworkManager != null) + { + if (stream != null) + { + var reader = NetworkReaderPool.GetReader(stream); + SceneEventData = (SceneEventData)reader.ReadObjectPacked(typeof(SceneEventData)); + if(SceneEventData.IsSceneEventClientSide()) + { + HandleClientSceneEvent(stream); + } + else + { + HandleServerSceneEvent(clientId, stream); + } + } + else + { + Debug.LogError($"Scene Event {nameof(OnSceneSwitch)} was invoked with a null stream!"); + return; + } + } + else + { + Debug.LogError($"{nameof(NetworkSceneManager.HandleSceneEvent)} was invoked but {nameof(NetworkManager)} reference was null!"); + } + } + + } + + [Serializable] + public class SceneEventData : INetworkSerializable,IDisposable + { + public enum SceneEventTypes + { + SWITCH, //Server to client + LOAD, //Server to client + UNLOAD, //Server to client + SWITCH_COMPLETE, //Client to server + LOAD_COMPLETE, //Client to server + UNLOAD_COMPLETE //Client to server + } + + public SceneEventTypes SceneEventType; + public LoadSceneMode LoadSceneMode; + public Guid SwitchSceneGuid; + public uint SceneIndex; + + + internal PooledNetworkBuffer InternalBuffer; + + public bool IsSceneEventClientSide() + { + switch(SceneEventType) + { + case SceneEventTypes.LOAD: + case SceneEventTypes.SWITCH: + case SceneEventTypes.UNLOAD: + { + return true; + } + } + return false; + } + private void OnWrite(NetworkWriter writer) + { + writer.WriteByte((byte)SceneEventType); + writer.WriteByte((byte)LoadSceneMode); + writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); + writer.WriteUInt32Packed(SceneIndex); + } + + private void OnRead(NetworkReader reader) + { + var sceneEventTypeValue = reader.ReadByte(); + + if (Enum.IsDefined(typeof(SceneEventTypes), sceneEventTypeValue)) + { + SceneEventType = (SceneEventTypes)sceneEventTypeValue; + } + else + { + Debug.LogError($"Serialization Read Error: {nameof(SceneEventType)} vale {sceneEventTypeValue} is not within the range of the defined {nameof(SceneEventTypes)} enumerator!"); + } + + var loadSceneModeValue = reader.ReadByte(); + + if (Enum.IsDefined(typeof(LoadSceneMode), loadSceneModeValue)) + { + LoadSceneMode = (LoadSceneMode)loadSceneModeValue; + } + else + { + Debug.LogError($"Serialization Read Error: {nameof(LoadSceneMode)} vale {loadSceneModeValue} is not within the range of the defined {nameof(LoadSceneMode)} enumerator!"); + } + + SwitchSceneGuid = new Guid(reader.ReadByteArray()); + + SceneIndex = reader.ReadUInt32Packed(); + + } + + public void NetworkSerialize(NetworkSerializer serializer) + { + if (serializer.IsReading) + { + OnRead(serializer.Reader); + } + else + { + OnWrite(serializer.Writer); + } + } + + internal void CopyUndreadFromStream(Stream stream) + { + InternalBuffer.Position = 0; + InternalBuffer.CopyUnreadFrom(stream); + InternalBuffer.Position = 0; + } + + public void Dispose() + { + if(InternalBuffer != null) + { + NetworkBufferPool.PutBackInPool(InternalBuffer); + InternalBuffer = null; + } + } + + public SceneEventData() + { + InternalBuffer = NetworkBufferPool.GetBuffer(); + } } } diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs index 4f8eb67372..5a64f58c4a 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs @@ -72,10 +72,10 @@ public void MessageHandlerReceivedMessageServerClient() } // Should not cause log (client only) - using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SWITCH_SCENE, inputBuffer)) - { - networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - } + //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SWITCH_SCENE, inputBuffer)) + //{ + // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); + //} // Should not cause log (client only) using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CHANGE_OWNER, inputBuffer)) @@ -122,12 +122,12 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); } - // Should cause log (server only) - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleClientSwitchSceneCompleted)); - using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, inputBuffer)) - { - networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - } + //// Should cause log (server only) + //LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleClientSwitchSceneCompleted)); + //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, inputBuffer)) + //{ + // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); + //} // Should cause log (server only) LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleNetworkLog)); @@ -190,11 +190,11 @@ public void MessageHandlerReceivedMessageServerClient() } // Should cause log (client only) - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleSwitchScene)); - using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SWITCH_SCENE, inputBuffer)) - { - networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - } + //LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleSwitchScene)); + //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SWITCH_SCENE, inputBuffer)) + //{ + // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); + //} // Should cause log (client only) LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleChangeOwner)); @@ -245,11 +245,11 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); } - // Should not cause log (server only) - using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, inputBuffer)) - { - networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - } + //// Should not cause log (server only) + //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, inputBuffer)) + //{ + // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); + //} // Should not cause log (server only) using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SERVER_LOG, inputBuffer)) From 728764a352a5cd686b2576a364e9d67abbb1e1cd Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 6 Jul 2021 17:48:47 -0500 Subject: [PATCH 006/106] refactor Created the LoadScene method which will only load scenes additively. Broke up some of the commonly shared initializations and checks. This helps to show what the real differences between single and additive scene loading are. (like two method calls) --- .../Messaging/InternalMessageHandler.cs | 2 +- .../SceneManagement/NetworkSceneManager.cs | 128 +++++++++++------- .../SwitchSceneHandlerAdditive.cs | 2 +- 3 files changed, 84 insertions(+), 48 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index ec0ee20989..7e659215ed 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -297,7 +297,7 @@ public void HandleClientSwitchSceneCompleted(ulong clientId, Stream stream) { using (var reader = PooledNetworkReader.Get(stream)) { - m_NetworkManager.SceneManager.OnClientSwitchSceneCompleted(clientId, new Guid(reader.ReadByteArray())); + m_NetworkManager.SceneManager.OnClientSceneLoadingEventCompleted(clientId, new Guid(reader.ReadByteArray())); } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 50f9c54650..4906dad81f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -133,13 +133,7 @@ public void AddRuntimeSceneName(string sceneName, uint index) SceneNameToIndex.Add(sceneName, index); } - /// - /// Switches to a scene with a given name. Can only be called from Server - /// - /// The name of the scene to switch to - /// The mode to load the scene (Additive vs Single) - /// SceneSwitchProgress - public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single, List scenesToUnload = null) + private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) { if (!m_NetworkManager.IsServer) { @@ -171,22 +165,13 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene return null; } - - if (loadSceneMode != LoadSceneMode.Additive) - { - m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects(); //Destroy current scene objects before switching. - } - s_IsSwitching = true; - var switchSceneProgress = new SceneSwitchProgress(m_NetworkManager); SceneSwitchProgresses.Add(switchSceneProgress.Guid, switchSceneProgress); CurrentSceneSwitchProgressGuid = switchSceneProgress.Guid; - - SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; - SceneEventData.SceneIndex = CurrentActiveSceneIndex; - SceneEventData.LoadSceneMode = loadSceneMode; + s_IsSwitching = true; + IsSpawnedObjectsPendingInDontDestroyOnLoad = true; + s_NextSceneName = sceneName; switchSceneProgress.OnClientLoadedScene += clientId => { OnNotifyServerClientLoadedScene?.Invoke(switchSceneProgress, clientId); }; switchSceneProgress.OnComplete += timedOut => @@ -206,28 +191,87 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene } }; - if (loadSceneMode != LoadSceneMode.Additive) + return switchSceneProgress; + } + + /// + /// Additively loads the scene + /// + /// + /// + public SceneSwitchProgress LoadScene(string sceneName) + { + var switchSceneProgress = ValidateServerSceneEvent(sceneName); + if (switchSceneProgress == null) { - // Move ALL NetworkObjects to the temp scene - MoveObjectsToDontDestroyOnLoad(); + return null; } - IsSpawnedObjectsPendingInDontDestroyOnLoad = true; + SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.LOAD; + SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + SceneEventData.LoadSceneMode = LoadSceneMode.Additive; - // Switch scene - AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); + // Begin the scene event + OnBeginSceneEvent(sceneName, switchSceneProgress, LoadSceneMode.Additive); - s_NextSceneName = sceneName; + //Return our scene progress instance + return switchSceneProgress; + } + + /// + /// Switches to a scene with a given name. Can only be called from Server + /// + /// The name of the scene to switch to + /// The mode to load the scene (Additive vs Single) + /// SceneSwitchProgress + public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) + { + // NSS TODO: Remove this once the LoadScene method is completed and all areas in the code that use the loadSceneMode parameter are updated + if(loadSceneMode == LoadSceneMode.Additive) + { + return LoadScene(sceneName); + } + + + var switchSceneProgress = ValidateServerSceneEvent(sceneName); + if (switchSceneProgress == null) + { + return null; + } + + SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; + SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + SceneEventData.LoadSceneMode = LoadSceneMode.Single; + + // Destroy current scene objects before switching. + m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects(); + // Preserve the objects that should not be destroyed during the scene event + MoveObjectsToDontDestroyOnLoad(); + + // Begin the scene event + OnBeginSceneEvent(sceneName, switchSceneProgress, LoadSceneMode.Single); + + //Return our scene progress instance + return switchSceneProgress; + } + + private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchSceneProgress, LoadSceneMode loadSceneMode) + { + // start loading the scene + AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); OnSceneSwitchStarted?.Invoke(sceneLoad); - - return switchSceneProgress; } - // Called on client - internal void OnSceneSwitch(Stream objectStream) + /// + /// Client Side: handles both forms of scene loading + /// + /// Stream data associated with the event + internal void OnSceneLoadingEvent(Stream objectStream) { SceneEventData.CopyUndreadFromStream(objectStream); @@ -258,8 +302,6 @@ internal void OnSceneSwitch(Stream objectStream) } - - internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) { if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) @@ -294,9 +336,6 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) } - - - private void OnSceneLoaded() { var nextScene = SceneManager.GetSceneByName(s_NextSceneName); @@ -355,7 +394,7 @@ private void OnServerLoadedScene() // Tell server that scene load is completed if (m_NetworkManager.IsHost) { - OnClientSwitchSceneCompleted(m_NetworkManager.LocalClientId, SceneEventData.SwitchSceneGuid); + OnClientSceneLoadingEventCompleted(m_NetworkManager.LocalClientId, SceneEventData.SwitchSceneGuid); } s_IsSwitching = false; @@ -380,9 +419,8 @@ private void OnClientLoadedScene() using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH_COMPLETE; + SceneEventData.SceneEventType = SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH ? SceneEventData.SceneEventTypes.SWITCH_COMPLETE: SceneEventData.SceneEventTypes.LOAD_COMPLETE; writer.WriteObjectPacked(SceneEventData); - //writer.WriteByteArray(SceneEventData.SwitchSceneGuid.ToByteArray()); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -396,9 +434,6 @@ private void OnClientLoadedScene() internal void SynchronizeInSceneObjects(ulong clientId, NetworkWriter writer) { writer.WriteObjectPacked(SceneEventData); - //writer.WriteUInt32Packed(CurrentActiveSceneIndex); - //writer.WriteByteArray(switchSceneGuid.ToByteArray()); - //writer.WriteBool(loadSceneMode == LoadSceneMode.Additive); uint sceneObjectsToSpawn = 0; @@ -424,7 +459,7 @@ internal void SynchronizeInSceneObjects(ulong clientId, NetworkWriter writer) internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; // Called on server - internal void OnClientSwitchSceneCompleted(ulong clientId, Guid switchSceneGuid) + internal void OnClientSceneLoadingEventCompleted(ulong clientId, Guid switchSceneGuid) { if (switchSceneGuid == Guid.Empty) { @@ -520,9 +555,11 @@ private void HandleClientSceneEvent(Stream stream) { switch (SceneEventData.SceneEventType) { + // Both events are basically the same with some minor differences case SceneEventData.SceneEventTypes.SWITCH: + case SceneEventData.SceneEventTypes.LOAD: { - OnSceneSwitch(stream); + OnSceneLoadingEvent(stream); break; } default: @@ -539,7 +576,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { case SceneEventData.SceneEventTypes.SWITCH_COMPLETE: { - OnClientSwitchSceneCompleted(clientId,SceneEventData.SwitchSceneGuid); + OnClientSceneLoadingEventCompleted(clientId,SceneEventData.SwitchSceneGuid); break; } default: @@ -550,7 +587,6 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } } - public void HandleSceneEvent(ulong clientId, Stream stream) { if (m_NetworkManager != null) @@ -570,7 +606,7 @@ public void HandleSceneEvent(ulong clientId, Stream stream) } else { - Debug.LogError($"Scene Event {nameof(OnSceneSwitch)} was invoked with a null stream!"); + Debug.LogError($"Scene Event {nameof(OnSceneLoadingEvent)} was invoked with a null stream!"); return; } } diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index 59cdf672b9..d4521c9a33 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -108,7 +108,7 @@ public void OnSwitchScene() OnSceneSwitchBegin?.Invoke(); m_ExitingScene = true; ExitingNow = true; - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.SwitchScene(m_SceneToSwitchTo[m_CurrentSceneIndex], UnityEngine.SceneManagement.LoadSceneMode.Additive); + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]); m_CurrentSceneIndex++; m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; } From 969123a98c25f35cfcfc35c7850a91822fea4ac3 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 6 Jul 2021 18:08:47 -0500 Subject: [PATCH 007/106] refactor Just cleaning up the code, putting place holder notes, and adding some comments --- .../SceneManagement/NetworkSceneManager.cs | 118 +++++++++++++----- 1 file changed, 88 insertions(+), 30 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 4906dad81f..3b08434ed2 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -133,6 +133,13 @@ public void AddRuntimeSceneName(string sceneName, uint index) SceneNameToIndex.Add(sceneName, index); } + + /// + /// Validates the new scene event request by the server-side code. + /// This also initializes some commonly shared values as well as switchSceneProgress + /// + /// + /// SceneSwitchProgress (if null it failed) private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) { if (!m_NetworkManager.IsServer) @@ -165,8 +172,11 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) return null; } + var switchSceneProgress = new SceneSwitchProgress(m_NetworkManager); SceneSwitchProgresses.Add(switchSceneProgress.Guid, switchSceneProgress); + + // NSS TODO: remove any of these values that are no longer needed CurrentSceneSwitchProgressGuid = switchSceneProgress.Guid; CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; s_IsSwitching = true; @@ -198,7 +208,9 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) /// Additively loads the scene /// /// - /// + /// NSS TODO: This could probably stand to have some form of "scene event status" class/structure that will + /// include any error types/messages and the SceneSwitchProgress class associated with the status (if success) + /// SceneSwitchProgress (if null this call failed) public SceneSwitchProgress LoadScene(string sceneName) { var switchSceneProgress = ValidateServerSceneEvent(sceneName); @@ -210,6 +222,8 @@ public SceneSwitchProgress LoadScene(string sceneName) SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.LOAD; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + + // NSS TODO: remove this completely once done with the transition SceneEventData.LoadSceneMode = LoadSceneMode.Additive; // Begin the scene event @@ -224,6 +238,8 @@ public SceneSwitchProgress LoadScene(string sceneName) /// /// The name of the scene to switch to /// The mode to load the scene (Additive vs Single) + /// NSS TODO: This could probably stand to have some form of "scene event status" class/structure that will + /// include any error types/messages and the SceneSwitchProgress class associated with the status (if success) /// SceneSwitchProgress public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) { @@ -233,7 +249,6 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene return LoadScene(sceneName); } - var switchSceneProgress = ValidateServerSceneEvent(sceneName); if (switchSceneProgress == null) { @@ -243,6 +258,8 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + + // NSS TODO: remove this completely once done with the transition SceneEventData.LoadSceneMode = LoadSceneMode.Single; // Destroy current scene objects before switching. @@ -258,6 +275,12 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene return switchSceneProgress; } + /// + /// Commonly shared code between switching and additively loading a scene + /// + /// name of the scene to be loaded + /// SceneSwitchProgress class instance + /// how the scene will be loaded private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchSceneProgress, LoadSceneMode loadSceneMode) { // start loading the scene @@ -301,7 +324,11 @@ internal void OnSceneLoadingEvent(Stream objectStream) OnSceneSwitchStarted?.Invoke(sceneLoad); } - + /// + /// Client approval specific + /// + /// + /// internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) { if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) @@ -335,7 +362,9 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) s_IsSwitching = false; } - + /// + /// Client and Server: Generic on scene loaded callback method to be called upon a scene loading + /// private void OnSceneLoaded() { var nextScene = SceneManager.GetSceneByName(s_NextSceneName); @@ -350,8 +379,10 @@ private void OnSceneLoaded() MoveObjectsToScene(nextScene); } + // NSS TODO: I think this can be determined differently and removed completely IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + // NSS TODO: We might want to set this sooner, what happens if there is a connection during asynchronous scene loading? CurrentSceneIndex = CurrentActiveSceneIndex; if (m_NetworkManager.IsServer) @@ -364,6 +395,10 @@ private void OnSceneLoaded() } } + + /// + /// Server specific on scene loaded callback method invoked by OnSceneLoading only + /// private void OnServerLoadedScene() { // Register in-scene placed NetworkObjects with MLAPI @@ -402,6 +437,40 @@ private void OnServerLoadedScene() OnSceneSwitched?.Invoke(); } + /// + /// NSS TODO: This might go back into the above method + /// + /// + /// + internal void SynchronizeInSceneObjects(ulong clientId, NetworkWriter writer) + { + writer.WriteObjectPacked(SceneEventData); + + uint sceneObjectsToSpawn = 0; + + foreach (var keyValuePair in ScenePlacedObjects) + { + if (keyValuePair.Value.Observers.Contains(clientId)) + { + sceneObjectsToSpawn++; + } + } + + // Write number of scene objects to spawn + writer.WriteUInt32Packed(sceneObjectsToSpawn); + foreach (var keyValuePair in ScenePlacedObjects) + { + if (keyValuePair.Value.Observers.Contains(clientId)) + { + keyValuePair.Value.SerializeSceneObject(writer, clientId); + } + } + } + + + /// + /// Client specific on scene loaded callback method invoked by OnSceneLoading only + /// private void OnClientLoadedScene() { var networkObjects = UnityEngine.Object.FindObjectsOfType(); @@ -431,31 +500,6 @@ private void OnClientLoadedScene() - internal void SynchronizeInSceneObjects(ulong clientId, NetworkWriter writer) - { - writer.WriteObjectPacked(SceneEventData); - - uint sceneObjectsToSpawn = 0; - - foreach (var keyValuePair in ScenePlacedObjects) - { - if (keyValuePair.Value.Observers.Contains(clientId)) - { - sceneObjectsToSpawn++; - } - } - - // Write number of scene objects to spawn - writer.WriteUInt32Packed(sceneObjectsToSpawn); - foreach (var keyValuePair in ScenePlacedObjects) - { - if (keyValuePair.Value.Observers.Contains(clientId)) - { - keyValuePair.Value.SerializeSceneObject(writer, clientId); - } - } - } - internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; // Called on server @@ -551,6 +595,11 @@ internal void AllClientsReady(ulong[] clientIds, ulong[] timedOutClientIds) OnNotifyClientAllClientsLoadedScene?.Invoke(clientIds, timedOutClientIds); } + + /// + /// Client Side: Handles incoming SCENE_EVENT messages + /// + /// data associated with the event private void HandleClientSceneEvent(Stream stream) { switch (SceneEventData.SceneEventType) @@ -570,6 +619,11 @@ private void HandleClientSceneEvent(Stream stream) } } + /// + /// Server Side: Handles incoming scene events + /// + /// client who sent the event + /// data associated with the event private void HandleServerSceneEvent(ulong clientId, Stream stream) { switch (SceneEventData.SceneEventType) @@ -587,6 +641,11 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } } + /// + /// Both Client and Server: Incoming scene event entry point + /// + /// client who sent the scene event + /// data associated with the scene event public void HandleSceneEvent(ulong clientId, Stream stream) { if (m_NetworkManager != null) @@ -615,7 +674,6 @@ public void HandleSceneEvent(ulong clientId, Stream stream) Debug.LogError($"{nameof(NetworkSceneManager.HandleSceneEvent)} was invoked but {nameof(NetworkManager)} reference was null!"); } } - } [Serializable] From c4138b01dd4531e0eaf90bcf17991e5eab360442 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 6 Jul 2021 18:13:21 -0500 Subject: [PATCH 008/106] style Just comment and style adjustments --- .../SceneManagement/NetworkSceneManager.cs | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 3b08434ed2..9902d69e53 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -244,7 +244,7 @@ public SceneSwitchProgress LoadScene(string sceneName) public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) { // NSS TODO: Remove this once the LoadScene method is completed and all areas in the code that use the loadSceneMode parameter are updated - if(loadSceneMode == LoadSceneMode.Additive) + if (loadSceneMode == LoadSceneMode.Additive) { return LoadScene(sceneName); } @@ -418,7 +418,7 @@ private void OnServerLoadedScene() { using (var writer = PooledNetworkWriter.Get(buffer)) { - SynchronizeInSceneObjects(m_NetworkManager.ConnectedClientsList[j].ClientId,writer); + SynchronizeInSceneObjects(m_NetworkManager.ConnectedClientsList[j].ClientId, writer); m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -488,7 +488,7 @@ private void OnClientLoadedScene() using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - SceneEventData.SceneEventType = SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH ? SceneEventData.SceneEventTypes.SWITCH_COMPLETE: SceneEventData.SceneEventTypes.LOAD_COMPLETE; + SceneEventData.SceneEventType = SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH ? SceneEventData.SceneEventTypes.SWITCH_COMPLETE : SceneEventData.SceneEventTypes.LOAD_COMPLETE; writer.WriteObjectPacked(SceneEventData); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -630,7 +630,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { case SceneEventData.SceneEventTypes.SWITCH_COMPLETE: { - OnClientSceneLoadingEventCompleted(clientId,SceneEventData.SwitchSceneGuid); + OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); break; } default: @@ -654,7 +654,7 @@ public void HandleSceneEvent(ulong clientId, Stream stream) { var reader = NetworkReaderPool.GetReader(stream); SceneEventData = (SceneEventData)reader.ReadObjectPacked(typeof(SceneEventData)); - if(SceneEventData.IsSceneEventClientSide()) + if (SceneEventData.IsSceneEventClientSide()) { HandleClientSceneEvent(stream); } @@ -677,7 +677,7 @@ public void HandleSceneEvent(ulong clientId, Stream stream) } [Serializable] - public class SceneEventData : INetworkSerializable,IDisposable + public class SceneEventData : INetworkSerializable, IDisposable { public enum SceneEventTypes { @@ -697,9 +697,13 @@ public enum SceneEventTypes internal PooledNetworkBuffer InternalBuffer; + /// + /// Determines if the scene event type was intended for the client ( or server ) + /// + /// true (client should handle this message) false (server should handle this message) public bool IsSceneEventClientSide() { - switch(SceneEventType) + switch (SceneEventType) { case SceneEventTypes.LOAD: case SceneEventTypes.SWITCH: @@ -710,6 +714,11 @@ public bool IsSceneEventClientSide() } return false; } + + /// + /// Serialize this class instance + /// + /// private void OnWrite(NetworkWriter writer) { writer.WriteByte((byte)SceneEventType); @@ -718,6 +727,10 @@ private void OnWrite(NetworkWriter writer) writer.WriteUInt32Packed(SceneIndex); } + /// + /// Deserialize this class instance + /// + /// private void OnRead(NetworkReader reader) { var sceneEventTypeValue = reader.ReadByte(); @@ -748,6 +761,10 @@ private void OnRead(NetworkReader reader) } + /// + /// INetworkSerializable implementation method + /// + /// serializer passed in during serialization public void NetworkSerialize(NetworkSerializer serializer) { if (serializer.IsReading) @@ -760,6 +777,10 @@ public void NetworkSerialize(NetworkSerializer serializer) } } + /// + /// Used to store data during an asynchronous scene loading event + /// + /// internal void CopyUndreadFromStream(Stream stream) { InternalBuffer.Position = 0; @@ -767,9 +788,12 @@ internal void CopyUndreadFromStream(Stream stream) InternalBuffer.Position = 0; } + /// + /// Used to release the pooled network buffer + /// public void Dispose() { - if(InternalBuffer != null) + if (InternalBuffer != null) { NetworkBufferPool.PutBackInPool(InternalBuffer); InternalBuffer = null; From 4c8a822c8a1e4961dd937df5d0fd189692422a8b Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 7 Jul 2021 13:51:47 -0500 Subject: [PATCH 009/106] refactor and feat Added additive scene unloading into the mix. Currently no events are wired for additive loading complete or additive unloading complete, but console logging on the server side verifies that the client sends the appropriate scene event notification once finished loading or unloading. Cleaned up some of the test project code to make sure the client isn't creating a pool to make sure the client is only replicating what the server is telling it to as well as various other minor test project tweaks for additive scene loading demonstration purposes. --- .../SceneManagement/NetworkSceneManager.cs | 121 ++++++++++++++++-- .../SceneTransitioningBase.unity | 2 +- .../SecondSceneAdditive.unity | 5 +- .../SwitchSceneHandlerAdditive.cs | 28 +++- .../ThirdSceneAdditive.unity | 5 +- .../Scripts/GenericNetworkObjectBehaviour.cs | 5 + .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 11 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 53 ++++++-- 8 files changed, 192 insertions(+), 38 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 9902d69e53..ff3bc7f740 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -133,14 +133,13 @@ public void AddRuntimeSceneName(string sceneName, uint index) SceneNameToIndex.Add(sceneName, index); } - /// /// Validates the new scene event request by the server-side code. /// This also initializes some commonly shared values as well as switchSceneProgress /// /// /// SceneSwitchProgress (if null it failed) - private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) + private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUnloading = false) { if (!m_NetworkManager.IsServer) { @@ -178,10 +177,15 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) // NSS TODO: remove any of these values that are no longer needed CurrentSceneSwitchProgressGuid = switchSceneProgress.Guid; - CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; + if (!isUnloading) + { + CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; + IsSpawnedObjectsPendingInDontDestroyOnLoad = true; + s_NextSceneName = sceneName; + } + s_IsSwitching = true; - IsSpawnedObjectsPendingInDontDestroyOnLoad = true; - s_NextSceneName = sceneName; + switchSceneProgress.OnClientLoadedScene += clientId => { OnNotifyServerClientLoadedScene?.Invoke(switchSceneProgress, clientId); }; switchSceneProgress.OnComplete += timedOut => @@ -197,6 +201,7 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) writer.WriteULongArray(doneClientIds, doneClientIds.Length); writer.WriteULongArray(timedOutClientIds, timedOutClientIds.Length); + // NSS TODO: This will need to be modified for loading and unloading m_NetworkManager.MessageSender.Send(NetworkManager.Singleton.ServerClientId, NetworkConstants.ALL_CLIENTS_LOADED_SCENE, NetworkChannel.Internal, buffer); } }; @@ -204,6 +209,84 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName) return switchSceneProgress; } + public SceneSwitchProgress UnloadScene(string sceneName) + { + // Make sure the scene is actually loaded + var sceneToUnload = SceneManager.GetSceneByName(sceneName); + if (sceneToUnload == null) + { + Debug.LogWarning($"{nameof(UnloadScene)} was called, but the scene {sceneName} is not currently loaded!"); + return null; + } + + var switchSceneProgress = ValidateServerSceneEvent(sceneName,true); + if (switchSceneProgress == null) + { + return null; + } + + SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.UNLOAD; + SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + + for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++) + { + using (var buffer = PooledNetworkBuffer.Get()) + { + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteObjectPacked(SceneEventData); + + m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + } + } + } + + // start loading the scene + AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); + sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; + switchSceneProgress.SetSceneLoadOperation(sceneUnload); + OnSceneSwitchStarted?.Invoke(sceneUnload); + + //Return our scene progress instance + return switchSceneProgress; + } + + private void OnClientUnloadScene() + { + if (!SceneIndexToString.TryGetValue(SceneEventData.SceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning("Server requested a scene switch to a non-registered scene"); + } + + return; + } + s_IsSwitching = true; + + var sceneLoad = SceneManager.UnloadSceneAsync(sceneName); + + sceneLoad.completed += asyncOp2 => OnSceneUnloaded(); + + // NSS TODO: Create the unloaded scene notification + //OnSceneSwitchStarted?.Invoke(sceneLoad); + } + + + private void OnSceneUnloaded() + { + using (var buffer = PooledNetworkBuffer.Get()) + using (var writer = PooledNetworkWriter.Get(buffer)) + { + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.UNLOAD_COMPLETE; + writer.WriteObjectPacked(SceneEventData); + m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + } + + s_IsSwitching = false; + } + /// /// Additively loads the scene /// @@ -294,7 +377,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene /// Client Side: handles both forms of scene loading /// /// Stream data associated with the event - internal void OnSceneLoadingEvent(Stream objectStream) + internal void OnClientSceneLoadingEvent(Stream objectStream) { SceneEventData.CopyUndreadFromStream(objectStream); @@ -368,7 +451,10 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) private void OnSceneLoaded() { var nextScene = SceneManager.GetSceneByName(s_NextSceneName); - SceneManager.SetActiveScene(nextScene); + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + { + SceneManager.SetActiveScene(nextScene); + } //Get all NetworkObjects loaded by the scene PopulateScenePlacedObjects(nextScene); @@ -473,8 +559,6 @@ internal void SynchronizeInSceneObjects(ulong clientId, NetworkWriter writer) /// private void OnClientLoadedScene() { - var networkObjects = UnityEngine.Object.FindObjectsOfType(); - using (var reader = PooledNetworkReader.Get(SceneEventData.InternalBuffer)) { var newObjectsCount = reader.ReadUInt32Packed(); @@ -608,7 +692,12 @@ private void HandleClientSceneEvent(Stream stream) case SceneEventData.SceneEventTypes.SWITCH: case SceneEventData.SceneEventTypes.LOAD: { - OnSceneLoadingEvent(stream); + OnClientSceneLoadingEvent(stream); + break; + } + case SceneEventData.SceneEventTypes.UNLOAD: + { + OnClientUnloadScene(); break; } default: @@ -633,6 +722,16 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); break; } + case SceneEventData.SceneEventTypes.LOAD_COMPLETE: + { + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.LOAD_COMPLETE)}] Client Id {clientId} finished loading additive scene."); + break; + } + case SceneEventData.SceneEventTypes.UNLOAD_COMPLETE: + { + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.UNLOAD_COMPLETE)}] Client Id {clientId} finished unloading additive scene."); + break; + } default: { Debug.LogWarning($"{SceneEventData.SceneEventType} is not currently supported!"); @@ -665,7 +764,7 @@ public void HandleSceneEvent(ulong clientId, Stream stream) } else { - Debug.LogError($"Scene Event {nameof(OnSceneLoadingEvent)} was invoked with a null stream!"); + Debug.LogError($"Scene Event {nameof(OnClientSceneLoadingEvent)} was invoked with a null stream!"); return; } } diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity index abc44e992c..14cd1aad58 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity @@ -1137,7 +1137,7 @@ MonoBehaviour: AutoSpawnEnable: 1 InitialSpawnDelay: 0.2 SpawnsPerSecond: 1 - PoolSize: 128 + PoolSize: 32 ObjectSpeed: 8 EnableHandler: 0 RegisterUsingNetworkObject: 0 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity index eb3eeb3370..af144e3986 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity @@ -214,10 +214,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 398d7c0793567354082687193357a99f, type: 3} m_Name: m_EditorClassIdentifier: + RandomMovement: 0 AutoSpawnEnable: 1 InitialSpawnDelay: 0.2 - SpawnsPerSecond: 3 - PoolSize: 128 + SpawnsPerSecond: 1 + PoolSize: 32 ObjectSpeed: 8 EnableHandler: 0 RegisterUsingNetworkObject: 0 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index d4521c9a33..61e03486f2 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -99,18 +99,38 @@ public override void OnNetworkSpawn() public event OnSceneSwitchBeginDelegateHandler OnSceneSwitchBegin; + private bool m_IsReversing; + public void OnSwitchScene() { - if (m_CurrentSceneIndex < m_SceneToSwitchTo.Count) + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) { - if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) + m_ExitingScene = true; + ExitingNow = true; + + if (!m_IsReversing) { OnSceneSwitchBegin?.Invoke(); - m_ExitingScene = true; - ExitingNow = true; + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]); m_CurrentSceneIndex++; m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; + if(m_CurrentSceneIndex == m_SceneToSwitchTo.Count) + { + m_IsReversing = true; + m_CurrentSceneIndex--; + } + } + else + { + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]); + m_CurrentSceneIndex--; + m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; + if(m_CurrentSceneIndex < 0) + { + m_IsReversing = false; + m_CurrentSceneIndex = 0; + } } } } diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity index f09d219be5..1f5115e4f8 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity @@ -244,10 +244,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 398d7c0793567354082687193357a99f, type: 3} m_Name: m_EditorClassIdentifier: + RandomMovement: 0 AutoSpawnEnable: 1 InitialSpawnDelay: 0.2 - SpawnsPerSecond: 3 - PoolSize: 128 + SpawnsPerSecond: 1 + PoolSize: 32 ObjectSpeed: 8 EnableHandler: 0 RegisterUsingNetworkObject: 0 diff --git a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs index f9dc1ef553..71e021dd9a 100644 --- a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs +++ b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs @@ -21,6 +21,11 @@ private void Start() m_RigidBody = GetComponent(); } + public void ShouldMoveRandomly(bool shouldMoveRandomly) + { + m_MoveRandomly = shouldMoveRandomly; + } + /// /// Sets the object's direction and velocity /// diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index ac792f8760..fddc0a3c7e 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -187,11 +187,14 @@ public void InitializeObjectPool() m_ObjectToSpawn = ClientObjectToPool; } - m_ObjectPool = new List(PoolSize); - - for (int i = 0; i < PoolSize; i++) + if (IsServer) { - AddNewInstance(); + m_ObjectPool = new List(PoolSize); + + for (int i = 0; i < PoolSize; i++) + { + AddNewInstance(); + } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index f943512bea..cc04c481a3 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -10,6 +10,7 @@ namespace TestProject.ManualTests public class NetworkPrefabPoolAdditive : NetworkBehaviour { [Header("General Settings")] + public bool RandomMovement = true; public bool AutoSpawnEnable = true; public float InitialSpawnDelay; public int SpawnsPerSecond; @@ -173,11 +174,14 @@ public void InitializeObjectPool() m_ObjectToSpawn = ClientObjectToPool; } - m_ObjectPool = new List(PoolSize); - - for (int i = 0; i < PoolSize; i++) + if (IsServer) { - AddNewInstance(); + m_ObjectPool = new List(PoolSize); + + for (int i = 0; i < PoolSize; i++) + { + AddNewInstance(); + } } } @@ -189,17 +193,27 @@ public GameObject GetObject() { if (m_ObjectPool != null) { - foreach (var obj in m_ObjectPool) + if (m_IsSpawningObjects) { - if (!obj.activeInHierarchy) + foreach (var obj in m_ObjectPool) { - obj.SetActive(true); - return obj; + if (m_IsSpawningObjects) + { + if (!obj.activeInHierarchy) + { + obj.SetActive(true); + return obj; + } + } + else + { + return null; + } } + var newObj = AddNewInstance(); + newObj.SetActive(true); + return newObj; } - var newObj = AddNewInstance(); - newObj.SetActive(true); - return newObj; } return null; } @@ -212,7 +226,14 @@ private GameObject AddNewInstance() { var obj = Instantiate(m_ObjectToSpawn); var no = obj.GetComponent(); + var genericBehaviour = obj.GetComponent(); + if(genericBehaviour) + { + genericBehaviour.ShouldMoveRandomly(RandomMovement); + } obj.SetActive(false); + + m_ObjectPool.Add(obj); return obj; } @@ -316,9 +337,13 @@ public class MyAdditiveCustomPrefabSpawnHandler : INetworkPrefabInstanceHandler public NetworkObject HandleNetworkPrefabSpawn(ulong ownerClientId, Vector3 position, Quaternion rotation) { var obj = m_PrefabPool.GetObject(); - obj.transform.position = position; - obj.transform.rotation = rotation; - return obj.GetComponent(); + if (obj != null) + { + obj.transform.position = position; + obj.transform.rotation = rotation; + return obj.GetComponent(); + } + return null; } public void HandleNetworkPrefabDestroy(NetworkObject networkObject) { From 9f2c6f41ea767953ff5498b592c595c53ead0d58 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 7 Jul 2021 15:08:32 -0500 Subject: [PATCH 010/106] refactor Swapped out the switch scene cycle hack for actual toggle buttons that allow one to load each sample additive scene or unload it based on the state of the toggle button. --- ...eAdditive.unity => AdditiveSceneOne.unity} | 0 ...unity.meta => AdditiveSceneOne.unity.meta} | 0 .../AdditiveSceneToggleHandler.cs | 129 +++ .../AdditiveSceneToggleHandler.cs.meta | 11 + ...eAdditive.unity => AdditiveSceneTwo.unity} | 0 ...unity.meta => AdditiveSceneTwo.unity.meta} | 0 .../SceneTransitioningBase.unity | 774 +++++++++++++++++- .../ProjectSettings/EditorBuildSettings.asset | 4 +- 8 files changed, 911 insertions(+), 7 deletions(-) rename testproject/Assets/Samples/SceneTransitioningAdditive/{SecondSceneAdditive.unity => AdditiveSceneOne.unity} (100%) rename testproject/Assets/Samples/SceneTransitioningAdditive/{SecondSceneAdditive.unity.meta => AdditiveSceneOne.unity.meta} (100%) create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta rename testproject/Assets/Samples/SceneTransitioningAdditive/{ThirdSceneAdditive.unity => AdditiveSceneTwo.unity} (100%) rename testproject/Assets/Samples/SceneTransitioningAdditive/{ThirdSceneAdditive.unity.meta => AdditiveSceneTwo.unity.meta} (100%) diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity.meta rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs new file mode 100644 index 0000000000..bcc4e3054e --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -0,0 +1,129 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +#if UNITY_EDITOR +using UnityEditor; +#endif + +using MLAPI; +using MLAPI.SceneManagement; + +namespace TestProject.ManualTests +{ + public class AdditiveSceneToggleHandler : NetworkBehaviour + { + public static bool ExitingNow { get; internal set; } + + private Toggle m_ToggleObject; + + [HideInInspector] + [SerializeField] + private string m_SceneToLoad; + +#if UNITY_EDITOR + [SerializeField] + private SceneAsset m_SceneAsset; + private void OnValidate() + { + if (m_SceneAsset != null && m_SceneAsset.name != m_SceneToLoad) + { + m_SceneToLoad = m_SceneAsset.name; + } + } +#endif + private int m_CurrentSceneIndex; + + private void Awake() + { + ExitingNow = false; + } + + private void Start() + { + m_ToggleObject = gameObject.GetComponentInChildren(); + StartCoroutine(CheckForVisibility()); + } + + private bool m_ExitingScene; + private void OnDestroy() + { + m_ExitingScene = true; + StopAllCoroutines(); + } + + private IEnumerator CheckForVisibility() + { + while (!m_ExitingScene) + { + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) + { + if (m_ToggleObject) + { + m_ToggleObject.gameObject.SetActive(true); + } + } + else + { + if (m_ToggleObject) + { + m_ToggleObject.gameObject.SetActive(false); + } + } + + yield return new WaitForSeconds(0.5f); + } + + yield return null; + } + + public override void OnNetworkSpawn() + { + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) + { + if (m_ToggleObject) + { + m_ToggleObject.gameObject.SetActive(true); + } + } + else + { + if (m_ToggleObject) + { + m_ToggleObject.gameObject.SetActive(false); + } + } + base.OnNetworkSpawn(); + } + + private SceneSwitchProgress m_CurrentSceneSwitchProgress; + + + public void OnToggle() + { + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) + { + if (m_ToggleObject) + { + if(m_ToggleObject.isOn) + { + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad); + } + else + { + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); + } + } + } + } + + public delegate void OnSceneSwitchCompletedDelegateHandler(); + + public event OnSceneSwitchCompletedDelegateHandler OnSceneSwitchCompleted; + + private void CurrentSceneSwitchProgress_OnComplete(bool timedOut) + { + OnSceneSwitchCompleted?.Invoke(); + } + } +} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta new file mode 100644 index 0000000000..587e6a2649 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 475de064003ff104fb88b1fbccd0f417 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity.meta rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity index 14cd1aad58..50704b5dbe 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity @@ -139,7 +139,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &34066665 RectTransform: m_ObjectHideFlags: 0 @@ -174,8 +174,8 @@ MonoBehaviour: m_EditorClassIdentifier: m_SwitchSceneButtonObject: {fileID: 1347823141} m_SceneToSwitchTo: - - SecondSceneAdditive - - ThirdSceneAdditive + - AdditiveSceneOne + - AdditiveSceneTwo m_SceneAssets: - {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} - {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} @@ -278,6 +278,161 @@ RectTransform: type: 3} m_PrefabInstance: {fileID: 2848221156282925290} m_PrefabAsset: {fileID: 0} +--- !u!1 &44393279 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 44393280} + - component: {fileID: 44393282} + - component: {fileID: 44393281} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &44393280 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 44393279} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1429502879} + m_Father: {fileID: 362129048} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &44393281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 44393279} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &44393282 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 44393279} + m_CullTransparentMesh: 1 +--- !u!1 &57065841 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 57065842} + - component: {fileID: 57065844} + - component: {fileID: 57065843} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &57065842 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57065841} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 362129048} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &57065843 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57065841} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Additive Scene -2 +--- !u!222 &57065844 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57065841} + m_CullTransparentMesh: 1 --- !u!1 &57392470 GameObject: m_ObjectHideFlags: 0 @@ -571,6 +726,8 @@ RectTransform: - {fileID: 1834318148} - {fileID: 2058276876} - {fileID: 1383741138} + - {fileID: 884557066} + - {fileID: 291820796} m_Father: {fileID: 0} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -579,6 +736,156 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!1 &291820795 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 291820796} + - component: {fileID: 291820797} + m_Layer: 5 + m_Name: ToggleTwoParent + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &291820796 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 291820795} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 362129048} + m_Father: {fileID: 290861172} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &291820797 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 291820795} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SceneToLoad: AdditiveSceneTwo + m_SceneAsset: {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} +--- !u!1 &300124661 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 300124662} + - component: {fileID: 300124663} + m_Layer: 5 + m_Name: AdditiveSceneOne + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &300124662 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 300124661} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1689223598} + - {fileID: 1638885888} + m_Father: {fileID: 884557066} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 236.09998} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &300124663 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 300124661} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1689223599} + toggleTransition: 1 + graphic: {fileID: 1691305197} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 884557067} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 --- !u!1 &336568645 GameObject: m_ObjectHideFlags: 0 @@ -675,6 +982,105 @@ Transform: m_Father: {fileID: 1332123092} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &362129047 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 362129048} + - component: {fileID: 362129049} + m_Layer: 5 + m_Name: AdditiveSceneTwo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &362129048 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 362129047} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 44393280} + - {fileID: 57065842} + m_Father: {fileID: 291820796} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 210} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &362129049 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 362129047} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 44393281} + toggleTransition: 1 + graphic: {fileID: 1429502880} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 291820797} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 --- !u!1 &562991978 GameObject: m_ObjectHideFlags: 0 @@ -914,8 +1320,59 @@ CanvasRenderer: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 599972120} - m_CullTransparentMesh: 1 + m_GameObject: {fileID: 599972120} + m_CullTransparentMesh: 1 +--- !u!1 &884557065 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 884557066} + - component: {fileID: 884557067} + m_Layer: 5 + m_Name: ToggleOneParent + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &884557066 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 884557065} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 300124662} + m_Father: {fileID: 290861172} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &884557067 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 884557065} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SceneToLoad: AdditiveSceneOne + m_SceneAsset: {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} --- !u!850595691 &903034822 LightingSettings: m_ObjectHideFlags: 0 @@ -1017,6 +1474,8 @@ MonoBehaviour: - SceneTransitioningBase - SecondSceneAdditive - ThirdSceneAdditive + - AdditiveSceneOne + - AdditiveSceneTwo RegisteredSceneAssets: - {fileID: 102900000, guid: 780f96a61e8ac8e41b638ae8ec3a3236, type: 3} - {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} @@ -1557,6 +2016,81 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: -20, y: 0} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1429502878 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1429502879} + - component: {fileID: 1429502881} + - component: {fileID: 1429502880} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1429502879 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1429502878} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 44393280} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1429502880 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1429502878} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1429502881 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1429502878} + m_CullTransparentMesh: 1 --- !u!1 &1523424136 GameObject: m_ObjectHideFlags: 0 @@ -1840,6 +2374,236 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1588117327} m_CullTransparentMesh: 1 +--- !u!1 &1638885887 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1638885888} + - component: {fileID: 1638885890} + - component: {fileID: 1638885889} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1638885888 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638885887} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 300124662} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1638885889 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638885887} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Additive Scene - 1 +--- !u!222 &1638885890 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638885887} + m_CullTransparentMesh: 1 +--- !u!1 &1689223597 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1689223598} + - component: {fileID: 1689223600} + - component: {fileID: 1689223599} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1689223598 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1689223597} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1691305196} + m_Father: {fileID: 300124662} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1689223599 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1689223597} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1689223600 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1689223597} + m_CullTransparentMesh: 1 +--- !u!1 &1691305195 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1691305196} + - component: {fileID: 1691305198} + - component: {fileID: 1691305197} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1691305196 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1691305195} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1689223598} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1691305197 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1691305195} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1691305198 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1691305195} + m_CullTransparentMesh: 1 --- !u!1 &1834318145 GameObject: m_ObjectHideFlags: 0 diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index 5b8d35134c..429f0ce78c 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -45,9 +45,9 @@ EditorBuildSettings: path: Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity guid: 780f96a61e8ac8e41b638ae8ec3a3236 - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/SecondSceneAdditive.unity + path: Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity guid: 41a0239b0c49e2047b7063c822f0df8a - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/ThirdSceneAdditive.unity + path: Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity guid: c6a3d883c8253ee43bca4f2b03797d7b m_configObjects: {} From cdc6a5057c61403108a486ce76f8002b6a2d93d7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 7 Jul 2021 15:44:32 -0500 Subject: [PATCH 011/106] feat This includes new assets to demonstrate additive scene loading and scene switching. Known issues are when additive scenes have been loaded that they are not synchronized with clients when they join. --- testproject/Assets/Materials/RedMaterial.mat | 79 + .../Assets/Materials/RedMaterial.mat.meta | 8 + .../Assets/Materials/YellowMaterial.mat | 79 + .../Assets/Materials/YellowMaterial.mat.meta | 8 + .../Assets/Prefabs/SpawnObjectSphere.prefab | 96 + .../Prefabs/SpawnObjectSphere.prefab.meta | 7 + .../Prefabs/SpawnObjectSphereRed.prefab | 81 + .../Prefabs/SpawnObjectSphereRed.prefab.meta | 7 + .../Prefabs/SpawnObjectSphereYellow.prefab | 81 + .../SpawnObjectSphereYellow.prefab.meta | 7 + ...iveSceneOne.unity => AdditiveScene1.unity} | 0 ...e.unity.meta => AdditiveScene1.unity.meta} | 0 ...iveSceneTwo.unity => AdditiveScene2.unity} | 0 ...o.unity.meta => AdditiveScene2.unity.meta} | 0 .../AdditiveScene3.unity | 257 ++ .../AdditiveScene3.unity.meta | 7 + .../AdditiveScene4.unity | 257 ++ .../AdditiveScene4.unity.meta | 7 + ...se.unity => SceneTransitioningBase1.unity} | 57 +- ...eta => SceneTransitioningBase1.unity.meta} | 0 .../SceneTransitioningBase2.unity | 3333 +++++++++++++++++ .../SceneTransitioningBase2.unity.meta | 7 + .../ProjectSettings/EditorBuildSettings.asset | 15 +- 23 files changed, 4372 insertions(+), 21 deletions(-) create mode 100644 testproject/Assets/Materials/RedMaterial.mat create mode 100644 testproject/Assets/Materials/RedMaterial.mat.meta create mode 100644 testproject/Assets/Materials/YellowMaterial.mat create mode 100644 testproject/Assets/Materials/YellowMaterial.mat.meta create mode 100644 testproject/Assets/Prefabs/SpawnObjectSphere.prefab create mode 100644 testproject/Assets/Prefabs/SpawnObjectSphere.prefab.meta create mode 100644 testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab create mode 100644 testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab.meta create mode 100644 testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab create mode 100644 testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab.meta rename testproject/Assets/Samples/SceneTransitioningAdditive/{AdditiveSceneOne.unity => AdditiveScene1.unity} (100%) rename testproject/Assets/Samples/SceneTransitioningAdditive/{AdditiveSceneOne.unity.meta => AdditiveScene1.unity.meta} (100%) rename testproject/Assets/Samples/SceneTransitioningAdditive/{AdditiveSceneTwo.unity => AdditiveScene2.unity} (100%) rename testproject/Assets/Samples/SceneTransitioningAdditive/{AdditiveSceneTwo.unity.meta => AdditiveScene2.unity.meta} (100%) create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity.meta create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity.meta rename testproject/Assets/Samples/SceneTransitioningAdditive/{SceneTransitioningBase.unity => SceneTransitioningBase1.unity} (98%) rename testproject/Assets/Samples/SceneTransitioningAdditive/{SceneTransitioningBase.unity.meta => SceneTransitioningBase1.unity.meta} (100%) create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta diff --git a/testproject/Assets/Materials/RedMaterial.mat b/testproject/Assets/Materials/RedMaterial.mat new file mode 100644 index 0000000000..42b48231d4 --- /dev/null +++ b/testproject/Assets/Materials/RedMaterial.mat @@ -0,0 +1,79 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: RedMaterial + m_Shader: {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 1 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 0.019583862, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/testproject/Assets/Materials/RedMaterial.mat.meta b/testproject/Assets/Materials/RedMaterial.mat.meta new file mode 100644 index 0000000000..5336a0148b --- /dev/null +++ b/testproject/Assets/Materials/RedMaterial.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 712559e4bd05f1942a8fd4bfa4e10c56 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Materials/YellowMaterial.mat b/testproject/Assets/Materials/YellowMaterial.mat new file mode 100644 index 0000000000..a70906b001 --- /dev/null +++ b/testproject/Assets/Materials/YellowMaterial.mat @@ -0,0 +1,79 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: YellowMaterial + m_Shader: {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 1 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 0.97702336, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/testproject/Assets/Materials/YellowMaterial.mat.meta b/testproject/Assets/Materials/YellowMaterial.mat.meta new file mode 100644 index 0000000000..a561f28e0a --- /dev/null +++ b/testproject/Assets/Materials/YellowMaterial.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a8ec12ee27208a42a11d2944b1c1371 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Prefabs/SpawnObjectSphere.prefab b/testproject/Assets/Prefabs/SpawnObjectSphere.prefab new file mode 100644 index 0000000000..f770d478b5 --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectSphere.prefab @@ -0,0 +1,96 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &728691999336062211 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -5591000292386890817, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: GlobalObjectIdHash + value: 951099334 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360811, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_Name + value: SpawnObjectSphere + objectReference: {fileID: 0} + - target: {fileID: 771575417923360817, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalScale.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalScale.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalScale.z + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: c0a45bdb516f341498d933b7a7ed4fc1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: c0a45bdb516f341498d933b7a7ed4fc1, type: 3} diff --git a/testproject/Assets/Prefabs/SpawnObjectSphere.prefab.meta b/testproject/Assets/Prefabs/SpawnObjectSphere.prefab.meta new file mode 100644 index 0000000000..0919a071db --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectSphere.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 894296ee6a442ef49b3637b680c7bdf7 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab b/testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab new file mode 100644 index 0000000000..ac4ceb3127 --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &286521893162279499 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 47844432802121000, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Name + value: SpawnObjectSphereRed + objectReference: {fileID: 0} + - target: {fileID: 47844432802121008, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Materials.Array.data[0] + value: + objectReference: {fileID: 2100000, guid: 712559e4bd05f1942a8fd4bfa4e10c56, type: 2} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4067897634150729404, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: GlobalObjectIdHash + value: 951099334 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 894296ee6a442ef49b3637b680c7bdf7, type: 3} diff --git a/testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab.meta b/testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab.meta new file mode 100644 index 0000000000..f0865d3a7c --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectSphereRed.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0aca3eef90d204641888a517edcc951f +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab b/testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab new file mode 100644 index 0000000000..0e22456845 --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &7857808669632867561 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 47844432802121000, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Name + value: SpawnObjectSphereYellow + objectReference: {fileID: 0} + - target: {fileID: 47844432802121008, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Materials.Array.data[0] + value: + objectReference: {fileID: 2100000, guid: 7a8ec12ee27208a42a11d2944b1c1371, type: 2} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4067897634150729404, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: GlobalObjectIdHash + value: 951099334 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 894296ee6a442ef49b3637b680c7bdf7, type: 3} diff --git a/testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab.meta b/testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab.meta new file mode 100644 index 0000000000..b756c254eb --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectSphereYellow.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9f2cc6554ae21e3498fb4cc28a07108d +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity.meta rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity.meta rename to testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity new file mode 100644 index 0000000000..be684b14a5 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity @@ -0,0 +1,257 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 903034822} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!850595691 &903034822 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &1113539278 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1113539280} + - component: {fileID: 1113539281} + - component: {fileID: 1113539282} + m_Layer: 0 + m_Name: PooledPrefabSpawnHandler + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1113539280 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1113539281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 4279522617 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!114 &1113539282 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 398d7c0793567354082687193357a99f, type: 3} + m_Name: + m_EditorClassIdentifier: + RandomMovement: 0 + AutoSpawnEnable: 1 + InitialSpawnDelay: 0.2 + SpawnsPerSecond: 1 + PoolSize: 32 + ObjectSpeed: 8 + EnableHandler: 0 + RegisterUsingNetworkObject: 0 + ServerObjectToPool: {fileID: 238713212259395427, guid: 0aca3eef90d204641888a517edcc951f, + type: 3} + ClientObjectToPool: {fileID: 0} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity.meta new file mode 100644 index 0000000000..4af8fab024 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7da3dd618f5b5a34db1f6d3c9511e221 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity new file mode 100644 index 0000000000..e3869a6ef9 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity @@ -0,0 +1,257 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 903034822} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!850595691 &903034822 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &1113539278 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1113539280} + - component: {fileID: 1113539281} + - component: {fileID: 1113539282} + m_Layer: 0 + m_Name: PooledPrefabSpawnHandler + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1113539280 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1113539281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 484425646 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!114 &1113539282 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 398d7c0793567354082687193357a99f, type: 3} + m_Name: + m_EditorClassIdentifier: + RandomMovement: 0 + AutoSpawnEnable: 1 + InitialSpawnDelay: 0.2 + SpawnsPerSecond: 1 + PoolSize: 32 + ObjectSpeed: 8 + EnableHandler: 0 + RegisterUsingNetworkObject: 0 + ServerObjectToPool: {fileID: 7900845470658144705, guid: 9f2cc6554ae21e3498fb4cc28a07108d, + type: 3} + ClientObjectToPool: {fileID: 0} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity.meta new file mode 100644 index 0000000000..11941edd35 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: dc7e17c86f5ca81478043be306027c13 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity similarity index 98% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity rename to testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 50704b5dbe..f7424c865f 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -132,14 +132,14 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 34066665} - - component: {fileID: 34066666} + - component: {fileID: 34066667} m_Layer: 5 m_Name: SwitchSceneParent m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &34066665 RectTransform: m_ObjectHideFlags: 0 @@ -160,7 +160,7 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 100} m_SizeDelta: {x: 100, y: 100} m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &34066666 +--- !u!114 &34066667 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -169,16 +169,11 @@ MonoBehaviour: m_GameObject: {fileID: 34066664} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fd82b56fddb35f2418f27a65b6bb3b54, type: 3} + m_Script: {fileID: 11500000, guid: 244f0414ea8419b41ac51adb305d64b0, type: 3} m_Name: m_EditorClassIdentifier: m_SwitchSceneButtonObject: {fileID: 1347823141} - m_SceneToSwitchTo: - - AdditiveSceneOne - - AdditiveSceneTwo - m_SceneAssets: - - {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} - - {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} + m_SceneToSwitchTo: SceneTransitioningBase2 --- !u!1 &37242881 GameObject: m_ObjectHideFlags: 0 @@ -424,7 +419,7 @@ MonoBehaviour: m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: Additive Scene -2 + m_Text: Additive Scene - 2 --- !u!222 &57065844 CanvasRenderer: m_ObjectHideFlags: 0 @@ -785,7 +780,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} m_Name: m_EditorClassIdentifier: - m_SceneToLoad: AdditiveSceneTwo + m_SceneToLoad: AdditiveScene2 m_SceneAsset: {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} --- !u!1 &300124661 GameObject: @@ -1371,7 +1366,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} m_Name: m_EditorClassIdentifier: - m_SceneToLoad: AdditiveSceneOne + m_SceneToLoad: AdditiveScene1 m_SceneAsset: {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} --- !u!850595691 &903034822 LightingSettings: @@ -1476,10 +1471,19 @@ MonoBehaviour: - ThirdSceneAdditive - AdditiveSceneOne - AdditiveSceneTwo + - SceneTransitioningBase1 + - AdditiveScene1 + - AdditiveScene2 + - SceneTransitioningBase2 + - AdditiveScene3 + - AdditiveScene4 RegisteredSceneAssets: - {fileID: 102900000, guid: 780f96a61e8ac8e41b638ae8ec3a3236, type: 3} + - {fileID: 102900000, guid: 9e437cc704801bc47add735d743985f5, type: 3} - {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} - {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} + - {fileID: 102900000, guid: 7da3dd618f5b5a34db1f6d3c9511e221, type: 3} + - {fileID: 102900000, guid: dc7e17c86f5ca81478043be306027c13, type: 3} AllowRuntimeSceneChanges: 0 PlayerPrefab: {fileID: 4079352819444256614, guid: c16f03336b6104576a565ef79ad643c0, type: 3} @@ -1504,6 +1508,24 @@ MonoBehaviour: SourcePrefabToOverride: {fileID: 0} SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 47844432802121000, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 238713212259395427, guid: 0aca3eef90d204641888a517edcc951f, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 7900845470658144705, guid: 9f2cc6554ae21e3498fb4cc28a07108d, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} ReceiveTickrate: 64 NetworkTickIntervalSec: 0.05 MaxReceiveEventsPerTickRate: 500 @@ -1850,9 +1872,8 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 34066666} - m_TargetAssemblyTypeName: TestProject.ManualTests.SwitchSceneHandlerAdditive, - Assembly-CSharp + - m_Target: {fileID: 34066667} + m_TargetAssemblyTypeName: TestProject.ManualTests.SwitchSceneHandler, TestProject.ManualTests m_MethodName: OnSwitchScene m_Mode: 1 m_Arguments: @@ -1961,7 +1982,7 @@ MonoBehaviour: m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 18 - m_FontStyle: 0 + m_FontStyle: 1 m_BestFit: 0 m_MinSize: 1 m_MaxSize: 40 @@ -1971,7 +1992,7 @@ MonoBehaviour: m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: First Scene Loaded + m_Text: Scene Transitioning Base - 1 --- !u!222 &1383741140 CanvasRenderer: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity.meta rename to testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity new file mode 100644 index 0000000000..eb31a3a172 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity @@ -0,0 +1,3333 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 903034822} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &34066664 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 34066665} + - component: {fileID: 34066667} + m_Layer: 5 + m_Name: SwitchSceneParent + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &34066665 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 34066664} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1347823142} + m_Father: {fileID: 290861172} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0} + m_AnchorMax: {x: 0.5, y: 0} + m_AnchoredPosition: {x: 0, y: 100} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &34066667 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 34066664} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 244f0414ea8419b41ac51adb305d64b0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SwitchSceneButtonObject: {fileID: 1347823141} + m_SceneToSwitchTo: SceneTransitioningBase1 +--- !u!1 &37242881 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 37242883} + - component: {fileID: 37242882} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &37242882 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 37242881} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &37242883 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 37242881} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!224 &42803802 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + m_PrefabInstance: {fileID: 2848221156282925290} + m_PrefabAsset: {fileID: 0} +--- !u!1 &44393279 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 44393280} + - component: {fileID: 44393282} + - component: {fileID: 44393281} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &44393280 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 44393279} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1429502879} + m_Father: {fileID: 362129048} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &44393281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 44393279} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &44393282 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 44393279} + m_CullTransparentMesh: 1 +--- !u!1 &57065841 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 57065842} + - component: {fileID: 57065844} + - component: {fileID: 57065843} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &57065842 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57065841} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 362129048} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &57065843 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57065841} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Additive Scene - 4 +--- !u!222 &57065844 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57065841} + m_CullTransparentMesh: 1 +--- !u!1 &57392470 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 57392474} + - component: {fileID: 57392473} + - component: {fileID: 57392472} + - component: {fileID: 57392471} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &57392471 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &57392472 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &57392473 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &57392474 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 57392470} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -30.5, y: 0.49999994, z: 0} + m_LocalScale: {x: 1, y: 3, z: 62} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &167044830 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 167044834} + - component: {fileID: 167044833} + - component: {fileID: 167044832} + - component: {fileID: 167044831} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &167044831 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &167044832 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &167044833 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &167044834 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167044830} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1865409449} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &290861168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 290861172} + - component: {fileID: 290861171} + - component: {fileID: 290861170} + - component: {fileID: 290861169} + m_Layer: 5 + m_Name: UI + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &290861169 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &290861170 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1024, y: 768} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &290861171 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 575203309} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &290861172 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 290861168} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1588117328} + - {fileID: 34066665} + - {fileID: 42803802} + - {fileID: 1834318148} + - {fileID: 2058276876} + - {fileID: 1383741138} + - {fileID: 884557066} + - {fileID: 291820796} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &291820795 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 291820796} + - component: {fileID: 291820797} + m_Layer: 5 + m_Name: ToggleTwoParent + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &291820796 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 291820795} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 362129048} + m_Father: {fileID: 290861172} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &291820797 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 291820795} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SceneToLoad: AdditiveScene4 + m_SceneAsset: {fileID: 102900000, guid: dc7e17c86f5ca81478043be306027c13, type: 3} +--- !u!1 &300124661 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 300124662} + - component: {fileID: 300124663} + m_Layer: 5 + m_Name: AdditiveSceneOne + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &300124662 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 300124661} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1689223598} + - {fileID: 1638885888} + m_Father: {fileID: 884557066} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 236.09998} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &300124663 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 300124661} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1689223599} + toggleTransition: 1 + graphic: {fileID: 1691305197} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 884557067} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 +--- !u!1 &336568645 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 336568649} + - component: {fileID: 336568648} + - component: {fileID: 336568647} + - component: {fileID: 336568646} + m_Layer: 0 + m_Name: Floor + m_TagString: Floor + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &336568646 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &336568647 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &336568648 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &336568649 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 336568645} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -0.50000006, z: 0} + m_LocalScale: {x: 60, y: 1, z: 60} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &362129047 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 362129048} + - component: {fileID: 362129049} + m_Layer: 5 + m_Name: AdditiveSceneTwo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &362129048 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 362129047} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 44393280} + - {fileID: 57065842} + m_Father: {fileID: 291820796} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 210} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &362129049 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 362129047} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 44393281} + toggleTransition: 1 + graphic: {fileID: 1429502880} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 291820797} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 +--- !u!1 &562991978 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 562991979} + - component: {fileID: 562991981} + - component: {fileID: 562991980} + m_Layer: 5 + m_Name: BoxGeneratorCount + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &562991979 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2058276876} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 3, y: 20} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &562991980 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.9811321, g: 0.9811321, b: 0.9811321, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 0 +--- !u!222 &562991981 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_CullTransparentMesh: 1 +--- !u!1 &575203307 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 575203310} + - component: {fileID: 575203309} + - component: {fileID: 575203308} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &575203308 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 575203307} + m_Enabled: 1 +--- !u!20 &575203309 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 575203307} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &575203310 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 575203307} + m_LocalRotation: {x: 0.41890106, y: -0, z: -0, w: 0.9080319} + m_LocalPosition: {x: 0, y: 42, z: -46} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 49.530003, y: 0, z: 0} +--- !u!1 &599972120 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 599972121} + - component: {fileID: 599972123} + - component: {fileID: 599972122} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &599972121 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 599972120} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1588117328} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &599972122 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 599972120} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.5058824, b: 0.003921569, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Show Server Stats +--- !u!222 &599972123 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 599972120} + m_CullTransparentMesh: 1 +--- !u!1 &884557065 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 884557066} + - component: {fileID: 884557067} + m_Layer: 5 + m_Name: ToggleOneParent + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &884557066 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 884557065} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 300124662} + m_Father: {fileID: 290861172} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &884557067 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 884557065} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SceneToLoad: AdditiveScene3 + m_SceneAsset: {fileID: 102900000, guid: 7da3dd618f5b5a34db1f6d3c9511e221, type: 3} +--- !u!850595691 &903034822 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &1113539278 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1113539280} + - component: {fileID: 1113539281} + - component: {fileID: 1113539279} + m_Layer: 0 + m_Name: PooledPrefabSpawnHandler + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1113539279 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8c48ea35c67e64f7fac22a3f6831ca88, type: 3} + m_Name: + m_EditorClassIdentifier: + AutoSpawnEnable: 1 + InitialSpawnDelay: 0.2 + SpawnsPerSecond: 1 + PoolSize: 32 + ObjectSpeed: 8 + EnableHandler: 0 + RegisterUsingNetworkObject: 0 + ServerObjectToPool: {fileID: 47844432802121000, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + ClientObjectToPool: {fileID: 0} + SwitchScene: {fileID: 0} + SpawnSlider: {fileID: 2058276877} + SpawnSliderValueText: {fileID: 562991980} +--- !u!4 &1113539280 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1113539281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 3420537353 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!1 &1332123091 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1332123092} + m_Layer: 0 + m_Name: Level + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1332123092 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1332123091} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.000000059604645, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 336568649} + - {fileID: 2028091272} + - {fileID: 1857685347} + - {fileID: 57392474} + - {fileID: 1336081255} + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1336081251 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1336081255} + - component: {fileID: 1336081254} + - component: {fileID: 1336081253} + - component: {fileID: 1336081252} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1336081252 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1336081253 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1336081254 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1336081255 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336081251} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 30.5, y: 0.49999994, z: 0} + m_LocalScale: {x: 1, y: 3, z: 62} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1347823141 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1347823142} + - component: {fileID: 1347823145} + - component: {fileID: 1347823144} + - component: {fileID: 1347823143} + m_Layer: 5 + m_Name: SwitchSceneButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1347823142 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1846334315} + m_Father: {fileID: 34066665} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0} + m_AnchorMax: {x: 0.5, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1347823143 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1347823144} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 34066667} + m_TargetAssemblyTypeName: TestProject.ManualTests.SwitchSceneHandler, TestProject.ManualTests + m_MethodName: OnSwitchScene + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &1347823144 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.1981132, g: 0.1981132, b: 0.1981132, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1347823145 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1347823141} + m_CullTransparentMesh: 1 +--- !u!1 &1383741137 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1383741138} + - component: {fileID: 1383741140} + - component: {fileID: 1383741139} + m_Layer: 5 + m_Name: SceneName + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1383741138 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383741137} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 290861172} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -139.70001, y: 45.59999} + m_SizeDelta: {x: 250, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1383741139 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383741137} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.990566, g: 0.990566, b: 0.990566, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Scene Transitioning Base - 2 +--- !u!222 &1383741140 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383741137} + m_CullTransparentMesh: 0 +--- !u!1 &1387688804 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1387688805} + m_Layer: 5 + m_Name: Handle Slide Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1387688805 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1387688804} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1523424137} + m_Father: {fileID: 2058276876} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1429502878 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1429502879} + - component: {fileID: 1429502881} + - component: {fileID: 1429502880} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1429502879 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1429502878} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 44393280} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1429502880 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1429502878} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1429502881 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1429502878} + m_CullTransparentMesh: 1 +--- !u!1 &1523424136 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1523424137} + - component: {fileID: 1523424139} + - component: {fileID: 1523424138} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1523424137 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523424136} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1387688805} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1523424138 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523424136} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1523424139 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523424136} + m_CullTransparentMesh: 1 +--- !u!1 &1549858058 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1549858059} + - component: {fileID: 1549858061} + - component: {fileID: 1549858060} + m_Layer: 5 + m_Name: Fill + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1549858059 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549858058} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1889006547} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 10, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1549858060 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549858058} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1549858061 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549858058} + m_CullTransparentMesh: 1 +--- !u!1 &1588117327 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1588117328} + - component: {fileID: 1588117331} + - component: {fileID: 1588117330} + - component: {fileID: 1588117329} + m_Layer: 5 + m_Name: ToggleClientServerStats + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1588117328 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 599972121} + m_Father: {fileID: 290861172} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 130, y: 51} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1588117329 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1588117330} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 2107482022} + m_TargetAssemblyTypeName: StatsDisplay, Assembly-CSharp + m_MethodName: ToggleClientSever + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &1588117330 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.1981132, g: 0.1981132, b: 0.1981132, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1588117331 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1588117327} + m_CullTransparentMesh: 1 +--- !u!1 &1638885887 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1638885888} + - component: {fileID: 1638885890} + - component: {fileID: 1638885889} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1638885888 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638885887} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 300124662} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1638885889 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638885887} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Additive Scene - 3 +--- !u!222 &1638885890 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638885887} + m_CullTransparentMesh: 1 +--- !u!1 &1689223597 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1689223598} + - component: {fileID: 1689223600} + - component: {fileID: 1689223599} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1689223598 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1689223597} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1691305196} + m_Father: {fileID: 300124662} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1689223599 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1689223597} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1689223600 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1689223597} + m_CullTransparentMesh: 1 +--- !u!1 &1691305195 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1691305196} + - component: {fileID: 1691305198} + - component: {fileID: 1691305197} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1691305196 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1691305195} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1689223598} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1691305197 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1691305195} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1691305198 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1691305195} + m_CullTransparentMesh: 1 +--- !u!1 &1834318145 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1834318148} + - component: {fileID: 1834318147} + - component: {fileID: 1834318146} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1834318146 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1834318145} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1834318147 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1834318145} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &1834318148 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1834318145} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -431, y: -242.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 290861172} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1846334314 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1846334315} + - component: {fileID: 1846334317} + - component: {fileID: 1846334316} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1846334315 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1846334314} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1347823142} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1846334316 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1846334314} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.5058824, b: 0.003921569, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Switch Scene +--- !u!222 &1846334317 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1846334314} + m_CullTransparentMesh: 1 +--- !u!1 &1857685343 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1857685347} + - component: {fileID: 1857685346} + - component: {fileID: 1857685345} + - component: {fileID: 1857685344} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1857685344 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1857685345 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1857685346 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1857685347 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1857685343} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.49999994, z: -30.5} + m_LocalScale: {x: 60, y: 3, z: 1} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1865409448 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 167044834} + m_Modifications: + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_SizeDelta.x + value: -952 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_SizeDelta.y + value: -344 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6963777608485144162, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Name + value: ConnectionModeButtons + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: d725b5588e1b956458798319e6541d84, type: 3} +--- !u!224 &1865409449 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + m_PrefabInstance: {fileID: 1865409448} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1889006546 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1889006547} + m_Layer: 5 + m_Name: Fill Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1889006547 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889006546} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1549858059} + m_Father: {fileID: 2058276876} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.25} + m_AnchorMax: {x: 1, y: 0.75} + m_AnchoredPosition: {x: -5, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &2021718438 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2021718439} + - component: {fileID: 2021718441} + - component: {fileID: 2021718440} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2021718439 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021718438} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2058276876} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.25} + m_AnchorMax: {x: 1, y: 0.75} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2021718440 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021718438} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2021718441 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021718438} + m_CullTransparentMesh: 1 +--- !u!1 &2028091268 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2028091272} + - component: {fileID: 2028091271} + - component: {fileID: 2028091270} + - component: {fileID: 2028091269} + m_Layer: 0 + m_Name: Side + m_TagString: Boundary + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &2028091269 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2028091270 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 00cf8ac777c8c42e8967157f70fbfcbf, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2028091271 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &2028091272 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2028091268} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.49999994, z: 30.5} + m_LocalScale: {x: 60, y: 3, z: 1} + m_Children: [] + m_Father: {fileID: 1332123092} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2058276875 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2058276876} + - component: {fileID: 2058276877} + m_Layer: 5 + m_Name: BoxGeneratorSlider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2058276876 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058276875} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2021718439} + - {fileID: 1889006547} + - {fileID: 1387688805} + - {fileID: 562991979} + m_Father: {fileID: 290861172} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0.99993896, y: -63.99997} + m_SizeDelta: {x: 400, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2058276877 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058276875} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 67db9e8f0e2ae9c40bc1e2b64352a6b4, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1523424138} + m_FillRect: {fileID: 1549858059} + m_HandleRect: {fileID: 1523424137} + m_Direction: 0 + m_MinValue: 0 + m_MaxValue: 550 + m_WholeNumbers: 1 + m_Value: 2 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1113539279} + m_TargetAssemblyTypeName: ServerBoxGenerator, Core + m_MethodName: UpdateSpawnsPerSecond + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &2107482020 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2107482021} + - component: {fileID: 2107482022} + - component: {fileID: 2107482023} + m_Layer: 0 + m_Name: Stats + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2107482021 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2107482020} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 318.45444, y: 110.697815, z: 216.79077} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2107482022 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2107482020} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cb5f3e55f5dd247129d8a4979b80ebbb, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ClientServerToggle: {fileID: 1588117327} +--- !u!114 &2107482023 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2107482020} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 515328755 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!1001 &2848221156282925290 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 290861172} + m_Modifications: + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMin.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMin.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SizeDelta.x + value: 20 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SizeDelta.y + value: 25 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchoredPosition.x + value: -23.40039 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -23.800293 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247795, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Name + value: ExitButton + objectReference: {fileID: 0} + - target: {fileID: 5266522511616468950, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SceneMenuToLoad + value: + objectReference: {fileID: 11400000, guid: c10d995498e0c514a853c3506031d3fb, + type: 2} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 3200770c16e3b2b4ebe7f604154faac7, type: 3} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta new file mode 100644 index 0000000000..bc0e9d74fa --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9e437cc704801bc47add735d743985f5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index 429f0ce78c..93a26d9805 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -42,12 +42,21 @@ EditorBuildSettings: path: Assets/Tests/Manual/NetworkSceneManagerCallbacks/SceneWeAreSwitchingFrom.unity guid: 073bd2111475c0643be45b7abe6a97ad - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase.unity + path: Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity guid: 780f96a61e8ac8e41b638ae8ec3a3236 - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/AdditiveSceneOne.unity + path: Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity + guid: 9e437cc704801bc47add735d743985f5 + - enabled: 1 + path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity guid: 41a0239b0c49e2047b7063c822f0df8a - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/AdditiveSceneTwo.unity + path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity guid: c6a3d883c8253ee43bca4f2b03797d7b + - enabled: 1 + path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity + guid: 7da3dd618f5b5a34db1f6d3c9511e221 + - enabled: 1 + path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity + guid: dc7e17c86f5ca81478043be306027c13 m_configObjects: {} From ec62ae50e86c57951fcbebf076af1773268185d1 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 9 Jul 2021 10:18:04 -0500 Subject: [PATCH 012/106] wip A wip commit that is progress towards removing scene and object synchronization from the approval process. --- .../Runtime/Core/NetworkManager.cs | 169 +++++--- .../Messaging/InternalMessageHandler.cs | 98 ++--- .../SceneManagement/NetworkSceneManager.cs | 375 +++++++++++++++++- 3 files changed, 521 insertions(+), 121 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 17bef1baf6..ef37b97fb1 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -1652,6 +1652,11 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? if (ownerClientId != ServerClientId) { + // Testing purposes currently + if (NetworkConfig.EnableSceneManagement) + { + SceneManager.SynchronizeNetworkObjects(ownerClientId); + } // Don't send any data over the wire if the host "connected" using (var buffer = PooledNetworkBuffer.Get()) @@ -1659,22 +1664,23 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? { writer.WriteUInt64Packed(ownerClientId); - if (NetworkConfig.EnableSceneManagement) - { - writer.WriteUInt32Packed(NetworkSceneManager.CurrentSceneIndex); - writer.WriteByteArray(NetworkSceneManager.CurrentSceneSwitchProgressGuid.ToByteArray()); - } + //if (NetworkConfig.EnableSceneManagement) + //{ + // writer.WriteUInt32Packed(NetworkSceneManager.CurrentSceneIndex); + // writer.WriteByteArray(NetworkSceneManager.CurrentSceneSwitchProgressGuid.ToByteArray()); + //} writer.WriteSinglePacked(Time.realtimeSinceStartup); - writer.WriteUInt32Packed((uint)m_ObservedObjects.Count); + //writer.WriteUInt32Packed((uint)m_ObservedObjects.Count); - for (int i = 0; i < m_ObservedObjects.Count; i++) - { - m_ObservedObjects[i].SerializeSceneObject(writer, ownerClientId); - } + //for (int i = 0; i < m_ObservedObjects.Count; i++) + //{ + // m_ObservedObjects[i].SerializeSceneObject(writer, ownerClientId); + //} MessageSender.Send(ownerClientId, NetworkConstants.CONNECTION_APPROVED, NetworkChannel.Internal, buffer); } + } OnClientConnectedCallback?.Invoke(ownerClientId); @@ -1685,62 +1691,117 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? } // Inform old clients of the new player - foreach (KeyValuePair clientPair in ConnectedClients) + //foreach (KeyValuePair clientPair in ConnectedClients) + //{ + // if (clientPair.Key == ownerClientId || + // ConnectedClients[ownerClientId].PlayerObject == null || + // !ConnectedClients[ownerClientId].PlayerObject.Observers.Contains(clientPair.Key)) + // { + // continue; //The new client. + // } + + // using (var buffer = PooledNetworkBuffer.Get()) + // using (var writer = PooledNetworkWriter.Get(buffer)) + // { + // writer.WriteBool(true); + // writer.WriteUInt64Packed(ConnectedClients[ownerClientId].PlayerObject.NetworkObjectId); + // writer.WriteUInt64Packed(ownerClientId); + + // //Does not have a parent + // writer.WriteBool(false); + + // // This is not a scene object + // writer.WriteBool(false); + + // writer.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + + // if (ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning(ownerClientId)) + // { + // writer.WriteBool(true); + // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.x); + // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.y); + // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.z); + + // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.x); + // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.y); + // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.z); + // } + // else + // { + // writer.WriteBool(false); + // } + + // writer.WriteBool(false); //No payload data + + // if (NetworkConfig.EnableNetworkVariable) + // { + // ConnectedClients[ownerClientId].PlayerObject.WriteNetworkVariableData(buffer, clientPair.Key); + // } + + // MessageSender.Send(clientPair.Key, NetworkConstants.ADD_OBJECT, NetworkChannel.Internal, buffer); + // } + //} + } + else + { + PendingClients.Remove(ownerClientId); + NetworkConfig.NetworkTransport.DisconnectRemoteClient(ownerClientId); + } + } + + + internal void NotifyPlayerConnected(ulong clientId, uint playerPrefabHash) + { + foreach (KeyValuePair clientPair in ConnectedClients) + { + if (clientPair.Key == clientId || + ConnectedClients[clientId].PlayerObject == null || + !ConnectedClients[clientId].PlayerObject.Observers.Contains(clientPair.Key)) { - if (clientPair.Key == ownerClientId || - ConnectedClients[ownerClientId].PlayerObject == null || - !ConnectedClients[ownerClientId].PlayerObject.Observers.Contains(clientPair.Key)) - { - continue; //The new client. - } + continue; //The new client. + } - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) - { - writer.WriteBool(true); - writer.WriteUInt64Packed(ConnectedClients[ownerClientId].PlayerObject.NetworkObjectId); - writer.WriteUInt64Packed(ownerClientId); + using (var buffer = PooledNetworkBuffer.Get()) + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteBool(true); + writer.WriteUInt64Packed(ConnectedClients[clientId].PlayerObject.NetworkObjectId); + writer.WriteUInt64Packed(clientId); - //Does not have a parent - writer.WriteBool(false); + //Does not have a parent + writer.WriteBool(false); - // This is not a scene object - writer.WriteBool(false); + // This is not a scene object + writer.WriteBool(false); - writer.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + writer.WriteUInt32Packed(playerPrefabHash); - if (ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning(ownerClientId)) - { - writer.WriteBool(true); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.x); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.y); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.z); - - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.x); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.y); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.z); - } - else - { - writer.WriteBool(false); - } + if (ConnectedClients[clientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[clientId].PlayerObject.IncludeTransformWhenSpawning(clientId)) + { + writer.WriteBool(true); + writer.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.x); + writer.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.y); + writer.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.z); - writer.WriteBool(false); //No payload data + writer.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.x); + writer.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.y); + writer.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.z); + } + else + { + writer.WriteBool(false); + } - if (NetworkConfig.EnableNetworkVariable) - { - ConnectedClients[ownerClientId].PlayerObject.WriteNetworkVariableData(buffer, clientPair.Key); - } + writer.WriteBool(false); //No payload data - MessageSender.Send(clientPair.Key, NetworkConstants.ADD_OBJECT, NetworkChannel.Internal, buffer); + if (NetworkConfig.EnableNetworkVariable) + { + ConnectedClients[clientId].PlayerObject.WriteNetworkVariableData(buffer, clientPair.Key); } + + MessageSender.Send(clientPair.Key, NetworkConstants.ADD_OBJECT, NetworkChannel.Internal, buffer); } } - else - { - PendingClients.Remove(ownerClientId); - NetworkConfig.NetworkTransport.DisconnectRemoteClient(ownerClientId); - } } private IInternalMessageHandler CreateMessageHandler() diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 7e659215ed..dfd97a989e 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -76,7 +76,7 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv sceneSwitchProgressGuid = new Guid(reader.ReadByteArray()); } - bool sceneSwitch = NetworkManager.NetworkConfig.EnableSceneManagement && NetworkManager.SceneManager.HasSceneMismatch(sceneIndex); + //bool sceneSwitch = NetworkManager.NetworkConfig.EnableSceneManagement && NetworkManager.SceneManager.HasSceneMismatch(sceneIndex); float netTime = reader.ReadSinglePacked(); NetworkManager.UpdateNetworkTime(clientId, netTime, receiveTime, true); @@ -84,54 +84,54 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); - void DelayedSpawnAction(Stream continuationStream) - { - - using (var continuationReader = PooledNetworkReader.Get(continuationStream)) - { - if (!NetworkManager.NetworkConfig.EnableSceneManagement) - { - NetworkManager.SpawnManager.DestroySceneObjects(); - } - else - { - NetworkManager.SceneManager.PopulateScenePlacedObjects(SceneManager.GetActiveScene()); - } - - var objectCount = continuationReader.ReadUInt32Packed(); - for (int i = 0; i < objectCount; i++) - { - NetworkObject.DeserializeSceneObject(continuationStream as NetworkBuffer, continuationReader, m_NetworkManager); - } - - NetworkManager.IsConnectedClient = true; - NetworkManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId); - } - } - - if (sceneSwitch) - { - UnityAction onSceneLoaded = null; - - var continuationBuffer = new NetworkBuffer(); - continuationBuffer.CopyUnreadFrom(stream); - continuationBuffer.Position = 0; - - void OnSceneLoadComplete() - { - SceneManager.activeSceneChanged -= onSceneLoaded; - NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad = false; - DelayedSpawnAction(continuationBuffer); - } - - onSceneLoaded = (oldScene, newScene) => { OnSceneLoadComplete(); }; - SceneManager.activeSceneChanged += onSceneLoaded; - m_NetworkManager.SceneManager.OnFirstSceneSwitchSync(sceneIndex, sceneSwitchProgressGuid); - } - else - { - DelayedSpawnAction(stream); - } + //void DelayedSpawnAction(Stream continuationStream) + //{ + + // using (var continuationReader = PooledNetworkReader.Get(continuationStream)) + // { + // if (!NetworkManager.NetworkConfig.EnableSceneManagement) + // { + // NetworkManager.SpawnManager.DestroySceneObjects(); + // } + // else + // { + // NetworkManager.SceneManager.PopulateScenePlacedObjects(SceneManager.GetActiveScene()); + // } + + // var objectCount = continuationReader.ReadUInt32Packed(); + // for (int i = 0; i < objectCount; i++) + // { + // NetworkObject.DeserializeSceneObject(continuationStream as NetworkBuffer, continuationReader, m_NetworkManager); + // } + + // NetworkManager.IsConnectedClient = true; + // NetworkManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId); + // } + //} + + //if (sceneSwitch) + //{ + // UnityAction onSceneLoaded = null; + + // var continuationBuffer = new NetworkBuffer(); + // continuationBuffer.CopyUnreadFrom(stream); + // continuationBuffer.Position = 0; + + // void OnSceneLoadComplete() + // { + // SceneManager.activeSceneChanged -= onSceneLoaded; + // NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + // DelayedSpawnAction(continuationBuffer); + // } + + // onSceneLoaded = (oldScene, newScene) => { OnSceneLoadComplete(); }; + // SceneManager.activeSceneChanged += onSceneLoaded; + // m_NetworkManager.SceneManager.OnFirstSceneSwitchSync(sceneIndex, sceneSwitchProgressGuid); + //} + //else + //{ + // DelayedSpawnAction(stream); + //} } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index ff3bc7f740..19104e7f17 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -90,29 +90,57 @@ public class NetworkSceneManager internal SceneEventData SceneEventData; + internal SceneEventData ClientSynchEventData; // For approval/late joining purposes + private NetworkManager m_NetworkManager { get; } internal NetworkSceneManager(NetworkManager networkManager) { m_NetworkManager = networkManager; SceneEventData = new SceneEventData(); + ClientSynchEventData = new SceneEventData(); } internal void SetCurrentSceneIndex() { - if (!SceneNameToIndex.TryGetValue(SceneManager.GetActiveScene().name, out CurrentSceneIndex)) + var sceneIndex = GetMLAPISceneIndex(SceneManager.GetActiveScene()); + if (sceneIndex != uint.MaxValue) + { + CurrentActiveSceneIndex = CurrentSceneIndex = sceneIndex; + } + } + + + private uint GetMLAPISceneIndex(Scene scene) + { + uint index = 0; + if (!SceneNameToIndex.TryGetValue(scene.name, out index)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { - NetworkLog.LogWarning($"The current scene ({SceneManager.GetActiveScene().name}) is not regisered as a network scene."); + NetworkLog.LogWarning($"The current scene ({scene.name}) is not registered as a network scene."); } - - return; + //MaxValue denotes an error + return uint.MaxValue; } + return index; + } + - CurrentActiveSceneIndex = CurrentSceneIndex; + private string GetMLAPISceneNameFromIndex(uint sceneIndex) + { + var sceneName = string.Empty; + if (!SceneIndexToString.TryGetValue(sceneIndex, out sceneName)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"The current scene index ({sceneIndex}) is not registered as a network scene."); + } + } + return sceneName; } + internal uint CurrentActiveSceneIndex { get; private set; } = 0; /// @@ -219,7 +247,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) return null; } - var switchSceneProgress = ValidateServerSceneEvent(sceneName,true); + var switchSceneProgress = ValidateServerSceneEvent(sceneName, true); if (switchSceneProgress == null) { return null; @@ -342,7 +370,7 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; - // NSS TODO: remove this completely once done with the transition + // NSS TODO: remove this completely once done with the transition? SceneEventData.LoadSceneMode = LoadSceneMode.Single; // Destroy current scene objects before switching. @@ -426,7 +454,7 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) if (SceneManager.GetActiveScene().name == sceneName) { - return; //This scene is already loaded. This usually happends at first load + return; //This scene is already loaded. This usually happens at first load } s_NextSceneName = sceneName; @@ -584,6 +612,138 @@ private void OnClientLoadedScene() + + + private readonly List m_ObservedObjects = new List(); + internal void SynchronizeNetworkObjects(ulong ownerClientId) + { + m_ObservedObjects.Clear(); + + foreach (var sobj in m_NetworkManager.SpawnManager.SpawnedObjectsList) + { + if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(ownerClientId)) + { + m_ObservedObjects.Add(sobj); + sobj.Observers.Add(ownerClientId); + } + } + + ClientSynchEventData.InitializeForSynch(); + ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; + var activeScene = SceneManager.GetActiveScene(); + ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC; + //var networkObjectsFound = UnityEngine.Object.FindObjectsOfType(); + + for (int i = 0; i < SceneManager.sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + uint malpiSceneIndex = GetMLAPISceneIndex(scene); + + // This would depend upon whether we are additive or note + if (activeScene == scene) + { + ClientSynchEventData.SceneIndex = malpiSceneIndex; + } + else + { + if (!ClientSynchEventData.AdditiveScenes.Contains(malpiSceneIndex)) + { + ClientSynchEventData.AdditiveScenes.Add(malpiSceneIndex); + } + } + + foreach (var networkObject in m_ObservedObjects) + { + if (networkObject.gameObject.scene != scene) + { + continue; + } + ClientSynchEventData.AddNetworkObjectForSynch(malpiSceneIndex, networkObject); + } + } + + // Send the scene event + using (var buffer = PooledNetworkBuffer.Get()) + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteObjectPacked(ClientSynchEventData); + m_NetworkManager.MessageSender.Send(ownerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + } + + } + + private void OnClientBeginSynch(uint sceneIndex) + { + + if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning("Server requested a scene switch to a non-registered scene"); + } + + return; + } + + + var activeScene = SceneManager.GetActiveScene(); + + IsSpawnedObjectsPendingInDontDestroyOnLoad = true; + + if (sceneName != activeScene.name) + { + //if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + //{ + // // Move ALL NetworkObjects to the temp scene + // MoveObjectsToDontDestroyOnLoad(); + //} + var sceneLoad = SceneManager.LoadSceneAsync(sceneName, sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode:LoadSceneMode.Additive); + s_NextSceneName = sceneName; + sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex); + } + else + { + ClientLoadedSynchronization(sceneIndex); + } + } + + + private void ClientLoadedSynchronization(uint sceneIndex) + { + var nextScene = SceneManager.GetSceneByName(GetMLAPISceneNameFromIndex(sceneIndex)); + if(nextScene == null) + { + return; + } + + if ((sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) + { + SceneManager.SetActiveScene(nextScene); + } + + //Get all NetworkObjects loaded by the scene + PopulateScenePlacedObjects(nextScene); + + SceneEventData.SynchronizeSceneNetworkObjects(sceneIndex, m_NetworkManager); + + if(!SceneEventData.IsDoneWithSynchronization()) + { + HandleClientSceneEvent(null); + } + else + { + m_NetworkManager.IsConnectedClient = true; + m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId); + using (var buffer = PooledNetworkBuffer.Get()) + using (var writer = PooledNetworkWriter.Get(buffer)) + { + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC_COMPLETE; + writer.WriteObjectPacked(SceneEventData); + m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + } + } + } + internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; // Called on server @@ -700,6 +860,11 @@ private void HandleClientSceneEvent(Stream stream) OnClientUnloadScene(); break; } + case SceneEventData.SceneEventTypes.SYNC: + { + OnClientBeginSynch(SceneEventData.GetNextSceneSynchronizationIndex()); + break; + } default: { Debug.LogWarning($"{SceneEventData.SceneEventType} is not currently supported!"); @@ -732,6 +897,33 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.UNLOAD_COMPLETE)}] Client Id {clientId} finished unloading additive scene."); break; } + case SceneEventData.SceneEventTypes.SYNC_COMPLETE: + { + //using (var buffer = PooledNetworkBuffer.Get()) + //using (var writer = PooledNetworkWriter.Get(buffer)) + //{ + // writer.WriteUInt64Packed(ownerClientId); + + // if (NetworkConfig.EnableSceneManagement) + // { + // writer.WriteUInt32Packed(NetworkSceneManager.CurrentSceneIndex); + // writer.WriteByteArray(NetworkSceneManager.CurrentSceneSwitchProgressGuid.ToByteArray()); + // } + + // writer.WriteSinglePacked(Time.realtimeSinceStartup); + // writer.WriteUInt32Packed((uint)m_ObservedObjects.Count); + + // for (int i = 0; i < m_ObservedObjects.Count; i++) + // { + // m_ObservedObjects[i].SerializeSceneObject(writer, ownerClientId); + // } + + // MessageSender.Send(ownerClientId, NetworkConstants.CONNECTION_APPROVED, NetworkChannel.Internal, buffer); + //} + + m_NetworkManager.NotifyPlayerConnected(clientId, m_NetworkManager.NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + break; + } default: { Debug.LogWarning($"{SceneEventData.SceneEventType} is not currently supported!"); @@ -780,22 +972,65 @@ public class SceneEventData : INetworkSerializable, IDisposable { public enum SceneEventTypes { - SWITCH, //Server to client - LOAD, //Server to client - UNLOAD, //Server to client + SWITCH, //Server to client full scene switch (i.e. single mode and destroy everything) + LOAD, //Server to client load additive scene + UNLOAD, //Server to client unload additive scene + SYNC, //Server to client late join approval synchronization SWITCH_COMPLETE, //Client to server LOAD_COMPLETE, //Client to server - UNLOAD_COMPLETE //Client to server + UNLOAD_COMPLETE, //Client to server + SYNC_COMPLETE, //Client to server } public SceneEventTypes SceneEventType; public LoadSceneMode LoadSceneMode; public Guid SwitchSceneGuid; - public uint SceneIndex; + public List AdditiveScenes; + + public uint SceneIndex; + public ulong TargetClientId; + private Dictionary> m_SceneNetworkObjects; + private Dictionary m_SceneNetworkObjectDataOffsets; internal PooledNetworkBuffer InternalBuffer; + public void InitializeForSynch() + { + if(m_SceneNetworkObjects == null) + { + m_SceneNetworkObjects = new Dictionary>(); + } + else + { + m_SceneNetworkObjects.Clear(); + } + } + + public uint GetNextSceneSynchronizationIndex() + { + if(m_SceneNetworkObjectDataOffsets.ContainsKey(SceneIndex)) + { + return SceneIndex; + } + return m_SceneNetworkObjectDataOffsets.First().Key; + } + + public bool IsDoneWithSynchronization() + { + return (m_SceneNetworkObjectDataOffsets.Count == 0); + } + + public void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObject) + { + if(!m_SceneNetworkObjects.ContainsKey(sceneIndex)) + { + m_SceneNetworkObjects.Add(sceneIndex, new List()); + } + + m_SceneNetworkObjects[sceneIndex].Add(networkObject); + } + /// /// Determines if the scene event type was intended for the client ( or server ) /// @@ -807,9 +1042,10 @@ public bool IsSceneEventClientSide() case SceneEventTypes.LOAD: case SceneEventTypes.SWITCH: case SceneEventTypes.UNLOAD: - { - return true; - } + case SceneEventTypes.SYNC: + { + return true; + } } return false; } @@ -821,9 +1057,49 @@ public bool IsSceneEventClientSide() private void OnWrite(NetworkWriter writer) { writer.WriteByte((byte)SceneEventType); + + writer.WriteByte((byte)LoadSceneMode); - writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); + if (SceneEventType != SceneEventTypes.SYNC) + { + writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); + } + writer.WriteUInt32Packed(SceneIndex); + + if (SceneEventType == SceneEventTypes.SYNC) + { + writer.WriteArrayPacked(AdditiveScenes.ToArray()); + Debug.Log($"Wrote:{AdditiveScenes.Count} additive scenes to be loaded."); + writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); + + if (m_SceneNetworkObjects.Count() > 0) + { + string msg = "Scene Associated NetworkObjects Write:\n"; + foreach (var keypair in m_SceneNetworkObjects) + { + writer.WriteUInt32Packed(keypair.Key); + msg += $"Scene ID [{keypair.Key}] NumNetworkObjects:[{keypair.Value.Count}]\n"; + writer.WriteUInt32Packed((uint)keypair.Value.Count); + var positionStart = writer.GetStream().Position; + // Size Place Holder + writer.WriteUInt64(0); + foreach (var networkObject in keypair.Value) + { + networkObject.SerializeSceneObject(writer, TargetClientId); + } + var positionEnd = writer.GetStream().Position; + var bytesWritten = (ulong)(positionEnd - positionStart); + writer.GetStream().Position = positionStart; + // Write the total size written to the stream by NetworkObjects being serialized + writer.WriteUInt64(bytesWritten); + writer.GetStream().Position = positionEnd; + msg += $"Wrote [{bytesWritten}] bytes of NetworkObject data.\n"; + } + + Debug.Log(msg); + } + } } /// @@ -854,10 +1130,71 @@ private void OnRead(NetworkReader reader) Debug.LogError($"Serialization Read Error: {nameof(LoadSceneMode)} vale {loadSceneModeValue} is not within the range of the defined {nameof(LoadSceneMode)} enumerator!"); } - SwitchSceneGuid = new Guid(reader.ReadByteArray()); + if (SceneEventType != SceneEventTypes.SYNC) + { + SwitchSceneGuid = new Guid(reader.ReadByteArray()); + } SceneIndex = reader.ReadUInt32Packed(); + if (SceneEventType == SceneEventTypes.SYNC) + { + var array = reader.ReadUIntArrayPacked(); + AdditiveScenes = new List(array); + var keyPairCount = reader.ReadInt32Packed(); + + if (keyPairCount > 0) + { + if (m_SceneNetworkObjectDataOffsets == null) + { + m_SceneNetworkObjectDataOffsets = new Dictionary(); + } + else + { + m_SceneNetworkObjectDataOffsets.Clear(); + } + + InternalBuffer.Position = 0; + + using (var writer = PooledNetworkWriter.Get(InternalBuffer)) + { + for (int i = 0; i < keyPairCount; i++) + { + var key = reader.ReadUInt32Packed(); + var count = reader.ReadUInt32Packed(); + var bytesToRead = reader.ReadUInt64Packed(); // So we know how much to read + // We store off the current position of the stream as it pertains to the scene relative NetworkObjects + m_SceneNetworkObjectDataOffsets.Add(key, InternalBuffer.Position); + writer.WriteUInt32Packed(count); + var networkObjectsBuffer = reader.ReadByteArray(null, (long)bytesToRead); + writer.WriteByteArray(networkObjectsBuffer); + } + } + } + } + } + + public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkManager) + { + if (m_SceneNetworkObjectDataOffsets.ContainsKey(sceneId)) + { + // Point to the appropriate offset + InternalBuffer.Position = m_SceneNetworkObjectDataOffsets[sceneId]; + + using (var reader = PooledNetworkReader.Get(InternalBuffer)) + { + // Process all NetworkObjects for this scene + var newObjectsCount = reader.ReadUInt32Packed(); + + for (int i = 0; i < newObjectsCount; i++) + { + NetworkObject.DeserializeSceneObject(InternalBuffer as NetworkBuffer, reader, networkManager); + } + } + + // Remove this entry + m_SceneNetworkObjectDataOffsets.Remove(sceneId); + } } /// @@ -902,6 +1239,8 @@ public void Dispose() public SceneEventData() { InternalBuffer = NetworkBufferPool.GetBuffer(); + AdditiveScenes = new List(); + } } } From 69b43dc236696cfb76e700425dcf548785f3a78b Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 9 Jul 2021 12:16:04 -0500 Subject: [PATCH 013/106] refactor naming issue and making sure we scope the pooled network reader. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 12 ++++++++---- .../AdditiveSceneToggleHandler.cs | 1 - 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index ff3bc7f740..e15f3ecfbc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -370,6 +370,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); + // NSS TODO: Make a single unified notification callback OnSceneSwitchStarted?.Invoke(sceneLoad); } @@ -379,7 +380,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene /// Stream data associated with the event internal void OnClientSceneLoadingEvent(Stream objectStream) { - SceneEventData.CopyUndreadFromStream(objectStream); + SceneEventData.CopyUnreadFromStream(objectStream); if (!SceneIndexToString.TryGetValue(SceneEventData.SceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) { @@ -751,8 +752,11 @@ public void HandleSceneEvent(ulong clientId, Stream stream) { if (stream != null) { - var reader = NetworkReaderPool.GetReader(stream); - SceneEventData = (SceneEventData)reader.ReadObjectPacked(typeof(SceneEventData)); + using (var reader = NetworkReaderPool.GetReader(stream)) + { + SceneEventData = (SceneEventData)reader.ReadObjectPacked(typeof(SceneEventData)); + } + if (SceneEventData.IsSceneEventClientSide()) { HandleClientSceneEvent(stream); @@ -880,7 +884,7 @@ public void NetworkSerialize(NetworkSerializer serializer) /// Used to store data during an asynchronous scene loading event /// /// - internal void CopyUndreadFromStream(Stream stream) + internal void CopyUnreadFromStream(Stream stream) { InternalBuffer.Position = 0; InternalBuffer.CopyUnreadFrom(stream); diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index bcc4e3054e..e88b4194c5 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; #if UNITY_EDITOR From bda84f3c9c657039a303cfaf8ad85c1729c11c1d Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 9 Jul 2021 14:48:00 -0500 Subject: [PATCH 014/106] wip Closer to decoupling approval from the approval process. There are still some issues with regards to the active scene and this could potentially be fixed by doing client synchronization first and then finalizing the approval. Fixed an issue with NetworkManager replicating upon exiting. --- .../Runtime/Core/NetworkManager.cs | 133 +++++++++--------- .../Messaging/InternalMessageHandler.cs | 10 +- .../SceneManagement/NetworkSceneManager.cs | 43 ++---- .../Assets/Scripts/ExitButtonScript.cs | 14 +- .../Tests/Manual/Scripts/RandomMovement.cs | 9 +- 5 files changed, 100 insertions(+), 109 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index ef37b97fb1..053b4f821f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -1638,26 +1638,21 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? ConnectedClients[ownerClientId].PlayerObject = networkObject; } - m_ObservedObjects.Clear(); + //m_ObservedObjects.Clear(); - foreach (var sobj in SpawnManager.SpawnedObjectsList) - { - if (ownerClientId == ServerClientId || sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(ownerClientId)) - { - m_ObservedObjects.Add(sobj); - sobj.Observers.Add(ownerClientId); - } - } + //foreach (var sobj in SpawnManager.SpawnedObjectsList) + //{ + // if (ownerClientId == ServerClientId || sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(ownerClientId)) + // { + // m_ObservedObjects.Add(sobj); + // sobj.Observers.Add(ownerClientId); + // } + //} - if (ownerClientId != ServerClientId) - { - // Testing purposes currently - if (NetworkConfig.EnableSceneManagement) - { - SceneManager.SynchronizeNetworkObjects(ownerClientId); - } + if (ownerClientId != ServerClientId) + { // Don't send any data over the wire if the host "connected" using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) @@ -1681,6 +1676,12 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? MessageSender.Send(ownerClientId, NetworkConstants.CONNECTION_APPROVED, NetworkChannel.Internal, buffer); } + // Now send the client synchronization scene event + if (NetworkConfig.EnableSceneManagement) + { + SceneManager.SynchronizeNetworkObjects(ownerClientId); + } + } OnClientConnectedCallback?.Invoke(ownerClientId); @@ -1690,57 +1691,57 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? return; } - // Inform old clients of the new player - //foreach (KeyValuePair clientPair in ConnectedClients) - //{ - // if (clientPair.Key == ownerClientId || - // ConnectedClients[ownerClientId].PlayerObject == null || - // !ConnectedClients[ownerClientId].PlayerObject.Observers.Contains(clientPair.Key)) - // { - // continue; //The new client. - // } + //Inform old clients of the new player + foreach (KeyValuePair clientPair in ConnectedClients) + { + if (clientPair.Key == ownerClientId || + ConnectedClients[ownerClientId].PlayerObject == null || + !ConnectedClients[ownerClientId].PlayerObject.Observers.Contains(clientPair.Key)) + { + continue; //The new client. + } - // using (var buffer = PooledNetworkBuffer.Get()) - // using (var writer = PooledNetworkWriter.Get(buffer)) - // { - // writer.WriteBool(true); - // writer.WriteUInt64Packed(ConnectedClients[ownerClientId].PlayerObject.NetworkObjectId); - // writer.WriteUInt64Packed(ownerClientId); - - // //Does not have a parent - // writer.WriteBool(false); - - // // This is not a scene object - // writer.WriteBool(false); - - // writer.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); - - // if (ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning(ownerClientId)) - // { - // writer.WriteBool(true); - // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.x); - // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.y); - // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.z); - - // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.x); - // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.y); - // writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.z); - // } - // else - // { - // writer.WriteBool(false); - // } - - // writer.WriteBool(false); //No payload data - - // if (NetworkConfig.EnableNetworkVariable) - // { - // ConnectedClients[ownerClientId].PlayerObject.WriteNetworkVariableData(buffer, clientPair.Key); - // } - - // MessageSender.Send(clientPair.Key, NetworkConstants.ADD_OBJECT, NetworkChannel.Internal, buffer); - // } - //} + using (var buffer = PooledNetworkBuffer.Get()) + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteBool(true); + writer.WriteUInt64Packed(ConnectedClients[ownerClientId].PlayerObject.NetworkObjectId); + writer.WriteUInt64Packed(ownerClientId); + + //Does not have a parent + writer.WriteBool(false); + + // This is not a scene object + writer.WriteBool(false); + + writer.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + + if (ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning(ownerClientId)) + { + writer.WriteBool(true); + writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.x); + writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.y); + writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.z); + + writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.x); + writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.y); + writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.z); + } + else + { + writer.WriteBool(false); + } + + writer.WriteBool(false); //No payload data + + if (NetworkConfig.EnableNetworkVariable) + { + ConnectedClients[ownerClientId].PlayerObject.WriteNetworkVariableData(buffer, clientPair.Key); + } + + MessageSender.Send(clientPair.Key, NetworkConstants.ADD_OBJECT, NetworkChannel.Internal, buffer); + } + } } else { diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index dfd97a989e..15d114e4a8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -70,11 +70,11 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv uint sceneIndex = 0; var sceneSwitchProgressGuid = new Guid(); - if (NetworkManager.NetworkConfig.EnableSceneManagement) - { - sceneIndex = reader.ReadUInt32Packed(); - sceneSwitchProgressGuid = new Guid(reader.ReadByteArray()); - } + //if (NetworkManager.NetworkConfig.EnableSceneManagement) + //{ + // sceneIndex = reader.ReadUInt32Packed(); + // sceneSwitchProgressGuid = new Guid(reader.ReadByteArray()); + //} //bool sceneSwitch = NetworkManager.NetworkConfig.EnableSceneManagement && NetworkManager.SceneManager.HasSceneMismatch(sceneIndex); diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index fbb8e5bbd2..3a17cabc85 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -633,7 +633,6 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC; - //var networkObjectsFound = UnityEngine.Object.FindObjectsOfType(); for (int i = 0; i < SceneManager.sceneCount; i++) { @@ -717,7 +716,7 @@ private void ClientLoadedSynchronization(uint sceneIndex) return; } - if ((sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) + if ( (sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) { SceneManager.SetActiveScene(nextScene); } @@ -727,7 +726,12 @@ private void ClientLoadedSynchronization(uint sceneIndex) SceneEventData.SynchronizeSceneNetworkObjects(sceneIndex, m_NetworkManager); - if(!SceneEventData.IsDoneWithSynchronization()) + if ((sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) + { + MoveObjectsToScene(nextScene); + } + + if (!SceneEventData.IsDoneWithSynchronization()) { HandleClientSceneEvent(null); } @@ -900,29 +904,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } case SceneEventData.SceneEventTypes.SYNC_COMPLETE: { - //using (var buffer = PooledNetworkBuffer.Get()) - //using (var writer = PooledNetworkWriter.Get(buffer)) - //{ - // writer.WriteUInt64Packed(ownerClientId); - - // if (NetworkConfig.EnableSceneManagement) - // { - // writer.WriteUInt32Packed(NetworkSceneManager.CurrentSceneIndex); - // writer.WriteByteArray(NetworkSceneManager.CurrentSceneSwitchProgressGuid.ToByteArray()); - // } - - // writer.WriteSinglePacked(Time.realtimeSinceStartup); - // writer.WriteUInt32Packed((uint)m_ObservedObjects.Count); - - // for (int i = 0; i < m_ObservedObjects.Count; i++) - // { - // m_ObservedObjects[i].SerializeSceneObject(writer, ownerClientId); - // } - - // MessageSender.Send(ownerClientId, NetworkConstants.CONNECTION_APPROVED, NetworkChannel.Internal, buffer); - //} - - m_NetworkManager.NotifyPlayerConnected(clientId, m_NetworkManager.NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + //m_NetworkManager.NotifyPlayerConnected(clientId, m_NetworkManager.NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); break; } default: @@ -1087,16 +1069,17 @@ private void OnWrite(NetworkWriter writer) writer.WriteUInt32Packed((uint)keypair.Value.Count); var positionStart = writer.GetStream().Position; // Size Place Holder - writer.WriteUInt64(0); + writer.WriteUInt64Packed(0); foreach (var networkObject in keypair.Value) { + msg += $"Included: {networkObject.name} \n"; networkObject.SerializeSceneObject(writer, TargetClientId); } var positionEnd = writer.GetStream().Position; - var bytesWritten = (ulong)(positionEnd - positionStart); + var bytesWritten = (ulong)(positionEnd - (positionStart + sizeof(ulong))); writer.GetStream().Position = positionStart; // Write the total size written to the stream by NetworkObjects being serialized - writer.WriteUInt64(bytesWritten); + writer.WriteUInt64Packed(bytesWritten); writer.GetStream().Position = positionEnd; msg += $"Wrote [{bytesWritten}] bytes of NetworkObject data.\n"; } @@ -1171,7 +1154,7 @@ private void OnRead(NetworkReader reader) m_SceneNetworkObjectDataOffsets.Add(key, InternalBuffer.Position); writer.WriteUInt32Packed(count); var networkObjectsBuffer = reader.ReadByteArray(null, (long)bytesToRead); - writer.WriteByteArray(networkObjectsBuffer); + writer.WriteByteArray(networkObjectsBuffer, (long)bytesToRead); } } } diff --git a/testproject/Assets/Scripts/ExitButtonScript.cs b/testproject/Assets/Scripts/ExitButtonScript.cs index e89f0e91cc..f1cfe9f641 100644 --- a/testproject/Assets/Scripts/ExitButtonScript.cs +++ b/testproject/Assets/Scripts/ExitButtonScript.cs @@ -14,29 +14,29 @@ public void OnExitScene() { if (NetworkManager.Singleton) { - if (NetworkManager.Singleton.IsClient) + if (NetworkManager.Singleton.IsHost) { - NetworkManager.Singleton.StopClient(); + NetworkManager.Singleton.StopHost(); } - else if (NetworkManager.Singleton.IsHost) + else if (NetworkManager.Singleton.IsClient) { - NetworkManager.Singleton.StopHost(); + NetworkManager.Singleton.StopClient(); } else if (NetworkManager.Singleton.IsServer) { NetworkManager.Singleton.StopServer(); } - Destroy(NetworkManager.Singleton); + Destroy(NetworkManager.Singleton.gameObject); } if (m_SceneMenuToLoad != null && m_SceneMenuToLoad.GetReferencedScenes()[0] != string.Empty) - { + { SceneManager.LoadSceneAsync(m_SceneMenuToLoad.GetReferencedScenes()[0], LoadSceneMode.Single); } else { SceneManager.LoadSceneAsync(0, LoadSceneMode.Single); } - + } } diff --git a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs index 166456d1ba..dbae3fda7b 100644 --- a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs +++ b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs @@ -27,7 +27,14 @@ public override void OnNetworkSpawn() public void Move(int speed) { - m_Rigidbody.MovePosition(transform.position + m_Direction * (speed * Time.fixedDeltaTime)); + if(m_Rigidbody == null) + { + m_Rigidbody = GetComponent(); + } + if (m_Rigidbody != null) + { + m_Rigidbody.MovePosition(transform.position + m_Direction * (speed * Time.fixedDeltaTime)); + } } private void OnCollisionStay(Collision collision) From de0012ece6d41156a28774dbe5ce104c54a1534d Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 9 Jul 2021 19:00:24 -0500 Subject: [PATCH 015/106] feat Joining with additive scenes already loaded as well as late joining players are synchronizing properly. The handle approval process is no longer directly tied to scene loading. Minor updates to stats display to show the currently active scene --- .../Runtime/Core/NetworkManager.cs | 24 +++++-- .../Messaging/InternalMessageHandler.cs | 61 +---------------- .../SceneManagement/NetworkSceneManager.cs | 66 +++++++------------ .../NetworkManagerMonitor.cs | 24 +++++++ .../NetworkManagerMonitor.cs.meta | 11 ++++ .../SceneTransitioningBase1.unity | 13 ++++ .../Tests/Manual/Scripts/StatsDisplay.cs | 6 +- 7 files changed, 96 insertions(+), 109 deletions(-) create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs create mode 100644 testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 053b4f821f..9f20149f22 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -823,11 +823,13 @@ public void Shutdown() NetworkTickSystem.Dispose(); NetworkTickSystem = null; } - + // NSS TODO: Remove this once MTT-860 is addressed or before PR #if !UNITY_2020_2_OR_NEWER - NetworkProfiler.Stop(); + if (IsListening) + { + NetworkProfiler.Stop(); + } #endif - IsListening = false; IsServer = false; IsClient = false; NetworkConfig.NetworkTransport.OnTransportEvent -= HandleRawTransportPoll; @@ -870,8 +872,14 @@ public void Shutdown() BehaviourUpdater = null; } - //The Transport is set during Init time, thus it is possible for the Transport to be null - NetworkConfig?.NetworkTransport?.Shutdown(); + // NSS TODO: Remove this once MTT-860 is addressed or before PR + if (IsListening) + { + //The Transport is set during initialization, thus it is possible for the Transport to be null + NetworkConfig?.NetworkTransport?.Shutdown(); + } + + IsListening = false; } // INetworkUpdateSystem @@ -1649,8 +1657,6 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? // } //} - - if (ownerClientId != ServerClientId) { // Don't send any data over the wire if the host "connected" @@ -1677,6 +1683,9 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? } // Now send the client synchronization scene event + // NSS TODO: This might be something we want to be optional and can be "user controlled" + // as well we might want to have the client receive the approved connection and then request to be synchronized which + // would kick this process off. if (NetworkConfig.EnableSceneManagement) { SceneManager.SynchronizeNetworkObjects(ownerClientId); @@ -1691,6 +1700,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? return; } + // NSS TODO: We might want to delay this notification until the client is completely synchronized //Inform old clients of the new player foreach (KeyValuePair clientPair in ConnectedClients) { diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 15d114e4a8..d15fe4ad13 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -67,71 +67,12 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv { NetworkManager.LocalClientId = reader.ReadUInt64Packed(); - uint sceneIndex = 0; - var sceneSwitchProgressGuid = new Guid(); - - //if (NetworkManager.NetworkConfig.EnableSceneManagement) - //{ - // sceneIndex = reader.ReadUInt32Packed(); - // sceneSwitchProgressGuid = new Guid(reader.ReadByteArray()); - //} - - //bool sceneSwitch = NetworkManager.NetworkConfig.EnableSceneManagement && NetworkManager.SceneManager.HasSceneMismatch(sceneIndex); - float netTime = reader.ReadSinglePacked(); NetworkManager.UpdateNetworkTime(clientId, netTime, receiveTime, true); NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); - - //void DelayedSpawnAction(Stream continuationStream) - //{ - - // using (var continuationReader = PooledNetworkReader.Get(continuationStream)) - // { - // if (!NetworkManager.NetworkConfig.EnableSceneManagement) - // { - // NetworkManager.SpawnManager.DestroySceneObjects(); - // } - // else - // { - // NetworkManager.SceneManager.PopulateScenePlacedObjects(SceneManager.GetActiveScene()); - // } - - // var objectCount = continuationReader.ReadUInt32Packed(); - // for (int i = 0; i < objectCount; i++) - // { - // NetworkObject.DeserializeSceneObject(continuationStream as NetworkBuffer, continuationReader, m_NetworkManager); - // } - - // NetworkManager.IsConnectedClient = true; - // NetworkManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId); - // } - //} - - //if (sceneSwitch) - //{ - // UnityAction onSceneLoaded = null; - - // var continuationBuffer = new NetworkBuffer(); - // continuationBuffer.CopyUnreadFrom(stream); - // continuationBuffer.Position = 0; - - // void OnSceneLoadComplete() - // { - // SceneManager.activeSceneChanged -= onSceneLoaded; - // NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad = false; - // DelayedSpawnAction(continuationBuffer); - // } - - // onSceneLoaded = (oldScene, newScene) => { OnSceneLoadComplete(); }; - // SceneManager.activeSceneChanged += onSceneLoaded; - // m_NetworkManager.SceneManager.OnFirstSceneSwitchSync(sceneIndex, sceneSwitchProgressGuid); - //} - //else - //{ - // DelayedSpawnAction(stream); - //} + // NSS TODO: We might have the client send a request to be synchronized (could be optional setting?) } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 3a17cabc85..059779649a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -612,10 +612,12 @@ private void OnClientLoadedScene() } - - - private readonly List m_ObservedObjects = new List(); + + /// + /// Server Side: This is used for late joining players and players that have just had their connection approved + /// + /// internal void SynchronizeNetworkObjects(ulong ownerClientId) { m_ObservedObjects.Clear(); @@ -644,13 +646,6 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) { ClientSynchEventData.SceneIndex = malpiSceneIndex; } - else - { - if (!ClientSynchEventData.AdditiveScenes.Contains(malpiSceneIndex)) - { - ClientSynchEventData.AdditiveScenes.Add(malpiSceneIndex); - } - } foreach (var networkObject in m_ObservedObjects) { @@ -685,18 +680,10 @@ private void OnClientBeginSynch(uint sceneIndex) return; } - var activeScene = SceneManager.GetActiveScene(); - IsSpawnedObjectsPendingInDontDestroyOnLoad = true; - if (sceneName != activeScene.name) { - //if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) - //{ - // // Move ALL NetworkObjects to the temp scene - // MoveObjectsToDontDestroyOnLoad(); - //} var sceneLoad = SceneManager.LoadSceneAsync(sceneName, sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode:LoadSceneMode.Additive); s_NextSceneName = sceneName; sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex); @@ -972,8 +959,6 @@ public enum SceneEventTypes public LoadSceneMode LoadSceneMode; public Guid SwitchSceneGuid; - public List AdditiveScenes; - public uint SceneIndex; public ulong TargetClientId; @@ -1055,8 +1040,6 @@ private void OnWrite(NetworkWriter writer) if (SceneEventType == SceneEventTypes.SYNC) { - writer.WriteArrayPacked(AdditiveScenes.ToArray()); - Debug.Log($"Wrote:{AdditiveScenes.Count} additive scenes to be loaded."); writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); if (m_SceneNetworkObjects.Count() > 0) @@ -1066,22 +1049,27 @@ private void OnWrite(NetworkWriter writer) { writer.WriteUInt32Packed(keypair.Key); msg += $"Scene ID [{keypair.Key}] NumNetworkObjects:[{keypair.Value.Count}]\n"; - writer.WriteUInt32Packed((uint)keypair.Value.Count); + writer.WriteInt32Packed(keypair.Value.Count); var positionStart = writer.GetStream().Position; - // Size Place Holder - writer.WriteUInt64Packed(0); + // Size Place Holder (For offset purposes, needs to not be packed) + writer.WriteUInt32(0); + var totalBytes = 0; foreach (var networkObject in keypair.Value) { - msg += $"Included: {networkObject.name} \n"; + var noStart = writer.GetStream().Position; + networkObject.SerializeSceneObject(writer, TargetClientId); + var noStop = writer.GetStream().Position; + totalBytes += (int)(noStop - noStart); + msg += $"Included: {networkObject.name} Bytes: {totalBytes} \n"; } var positionEnd = writer.GetStream().Position; - var bytesWritten = (ulong)(positionEnd - (positionStart + sizeof(ulong))); + var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); writer.GetStream().Position = positionStart; // Write the total size written to the stream by NetworkObjects being serialized - writer.WriteUInt64Packed(bytesWritten); + writer.WriteUInt32(bytesWritten); writer.GetStream().Position = positionEnd; - msg += $"Wrote [{bytesWritten}] bytes of NetworkObject data.\n"; + msg += $"Wrote [{bytesWritten}] bytes of NetworkObject data. Verification: {totalBytes}\n"; } Debug.Log(msg); @@ -1126,8 +1114,6 @@ private void OnRead(NetworkReader reader) if (SceneEventType == SceneEventTypes.SYNC) { - var array = reader.ReadUIntArrayPacked(); - AdditiveScenes = new List(array); var keyPairCount = reader.ReadInt32Packed(); if (keyPairCount > 0) @@ -1148,13 +1134,13 @@ private void OnRead(NetworkReader reader) for (int i = 0; i < keyPairCount; i++) { var key = reader.ReadUInt32Packed(); - var count = reader.ReadUInt32Packed(); - var bytesToRead = reader.ReadUInt64Packed(); // So we know how much to read + var count = reader.ReadInt32Packed(); + // how many bytes to read for this scene set + var bytesToRead = (ulong)reader.ReadUInt32(); // We store off the current position of the stream as it pertains to the scene relative NetworkObjects m_SceneNetworkObjectDataOffsets.Add(key, InternalBuffer.Position); - writer.WriteUInt32Packed(count); - var networkObjectsBuffer = reader.ReadByteArray(null, (long)bytesToRead); - writer.WriteByteArray(networkObjectsBuffer, (long)bytesToRead); + writer.WriteInt32Packed(count); + writer.ReadAndWrite(reader, (long)bytesToRead); } } } @@ -1171,15 +1157,15 @@ public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkM using (var reader = PooledNetworkReader.Get(InternalBuffer)) { // Process all NetworkObjects for this scene - var newObjectsCount = reader.ReadUInt32Packed(); + var newObjectsCount = reader.ReadInt32Packed(); for (int i = 0; i < newObjectsCount; i++) { - NetworkObject.DeserializeSceneObject(InternalBuffer as NetworkBuffer, reader, networkManager); + NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); } } - // Remove this entry + // Remove each entry after it is processed so we know when we are done m_SceneNetworkObjectDataOffsets.Remove(sceneId); } } @@ -1226,8 +1212,6 @@ public void Dispose() public SceneEventData() { InternalBuffer = NetworkBufferPool.GetBuffer(); - AdditiveScenes = new List(); - } } } diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs new file mode 100644 index 0000000000..3ea3f9fd0a --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs @@ -0,0 +1,24 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using MLAPI; + +public class NetworkManagerMonitor : MonoBehaviour +{ + // Start is called before the first frame update + private void Start() + { + var networkManagerInstances = FindObjectsOfType(); + foreach (var instance in networkManagerInstances) + { + if (instance.IsListening) + { + if (gameObject != instance.gameObject) + { + var networkManager = GetComponent(); + Destroy(gameObject); + } + } + } + } +} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta b/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta new file mode 100644 index 0000000000..36a192a307 --- /dev/null +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e6f8504d891fc44881b2d9703017d03 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity index f7424c865f..7171b2986f 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -1440,6 +1440,7 @@ GameObject: - component: {fileID: 1024114720} - component: {fileID: 1024114719} - component: {fileID: 1024114718} + - component: {fileID: 1024114721} m_Layer: 0 m_Name: NetworkManager m_TagString: Untagged @@ -1585,6 +1586,18 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1024114721 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1024114717} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0e6f8504d891fc44881b2d9703017d03, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &1113539278 GameObject: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs index 6633e410eb..c50ff0cc78 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; +using UnityEngine.SceneManagement; using UnityEngine.UI; using MLAPI; using MLAPI.Messaging; @@ -129,7 +130,7 @@ private void ReceiveStatsClientRPC(StatsInfoContainer statsinfo) m_LastStatsDump += "\ndeltaTime: [" + Time.deltaTime.ToString() + "]"; if (ProfilerStatManager.AllStats.Count != statsinfo.StatValues.Count) { - Debug.LogError("[StatsDisplay-Error][Mismatch] Recieved " + statsinfo.StatValues.Count.ToString() + " values and have " + ProfilerStatManager.AllStats.Count.ToString() + " profiler stats entries!"); + Debug.LogError("[StatsDisplay-Error][Mismatch] Received " + statsinfo.StatValues.Count.ToString() + " values and have " + ProfilerStatManager.AllStats.Count.ToString() + " profiler stats entries!"); } else { @@ -143,6 +144,7 @@ private void ReceiveStatsClientRPC(StatsInfoContainer statsinfo) m_LastStatsDump += p.PrettyPrintName + ": " + statsinfo.StatValues[statsCounter].ToString(("0.0")); statsCounter++; } + m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}"; } } @@ -185,6 +187,8 @@ private IEnumerator UpdateTextStatus() } m_LastStatsDump += p.PrettyPrintName + ": " + p.SampleRate().ToString("0.0"); } + m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}"; + } if (NetworkManager.Singleton.IsServer && m_ClientsToUpdate.Count > 0) { From d41b8d37d88949a111428616c9b189578b0bcada Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 10 Jul 2021 17:19:28 -0500 Subject: [PATCH 016/106] refactor Removed static properties in NetworkSceneManager that were no longer being used. Fixed issue with host not having the proper network prefab override (i.e. running as host vs running as server). Moved SceneEventData into its own .cs file. Excluding the local notification events, this is very close to being finalized, with the exception of custom prefab overrides and additive scene loading. This still has issues with late joining (most likely this has to do with the NetworkPrefabPool code base, still investigating this issue). --- .../Runtime/Connection/NetworkClient.cs | 1 + .../Runtime/Core/NetworkManager.cs | 140 ++--- .../Messaging/InternalMessageHandler.cs | 82 +-- .../SceneManagement/NetworkSceneManager.cs | 553 ++++-------------- .../Runtime/SceneManagement/SceneEventData.cs | 312 ++++++++++ .../SceneManagement/SceneEventData.cs.meta | 11 + .../Runtime/Spawning/NetworkSpawnManager.cs | 5 + .../SpawnObjectRedSphereServerOnly.prefab | 81 +++ ...SpawnObjectRedSphereServerOnly.prefab.meta | 7 + .../SpawnObjectYellowSphereServerOnly.prefab | 81 +++ ...wnObjectYellowSphereServerOnly.prefab.meta | 7 + .../AdditiveScene3.unity | 7 +- .../AdditiveScene4.unity | 7 +- .../SceneTransitioningBase1.unity | 12 + .../SceneTransitioningTest.unity | 21 + .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 31 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 36 +- 17 files changed, 774 insertions(+), 620 deletions(-) create mode 100644 com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs create mode 100644 com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs.meta create mode 100644 testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab create mode 100644 testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab.meta create mode 100644 testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab create mode 100644 testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs b/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs index 8a7723e390..18fa0249fc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs @@ -7,6 +7,7 @@ namespace MLAPI.Connection /// public class NetworkClient { + public bool IsReadyToReceiveMessages; /// /// The ClientId of the NetworkClient /// diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 9f20149f22..9e0c2dd86a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -78,6 +78,35 @@ public NetworkPrefabHandler PrefabHandler } } + /// + /// Returns the GameObject to use as the override as could be defined within the NetworkPrefab list + /// Note: This should be used to create GameObject pools (with NetworkObject components) under the + /// scenario where a Host is being used as the Host spawns everything locally and as such the override + /// will not be applied during the typical client side invocation of CreateLocalNetworkObject for the + /// CREATE_OBECT command. + /// + /// + /// + public GameObject GetNetworkPrefabOverride(GameObject gameObject) + { + var networkObject = gameObject.GetComponent(); + if (networkObject != null) + { + if (NetworkConfig.NetworkPrefabOverrideLinks.ContainsKey(networkObject.GlobalObjectIdHash)) + { + switch (NetworkConfig.NetworkPrefabOverrideLinks[networkObject.GlobalObjectIdHash].Override) + { + case NetworkPrefabOverride.Hash: + case NetworkPrefabOverride.Prefab: + { + return NetworkConfig.NetworkPrefabOverrideLinks[networkObject.GlobalObjectIdHash].OverridingTargetPrefab; + } + } + } + } + return gameObject; + } + /// /// A synchronized time, represents the time in seconds since the server application started. Is replicated across all clients /// @@ -427,8 +456,6 @@ private void Initialize(bool server) SceneManager.SceneIndexToString.Add((uint)i, NetworkConfig.RegisteredScenes[i]); SceneManager.SceneNameToIndex.Add(NetworkConfig.RegisteredScenes[i], (uint)i); } - - SceneManager.SetCurrentSceneIndex(); } // This is used to remove entries not needed or invalid @@ -1619,8 +1646,15 @@ private void SyncTime() #endif } - private readonly List m_ObservedObjects = new List(); - + /// + /// Server Side: Handles the approval of a client + /// + /// + /// + /// + /// + /// + /// internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? playerPrefabHash, bool approved, Vector3? position, Quaternion? rotation) { if (approved) @@ -1646,51 +1680,27 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? ConnectedClients[ownerClientId].PlayerObject = networkObject; } - //m_ObservedObjects.Clear(); - - //foreach (var sobj in SpawnManager.SpawnedObjectsList) - //{ - // if (ownerClientId == ServerClientId || sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(ownerClientId)) - // { - // m_ObservedObjects.Add(sobj); - // sobj.Observers.Add(ownerClientId); - // } - //} - + // Don't send the CONNECTION_APPROVED message if this is the host that connected locally if (ownerClientId != ServerClientId) { - // Don't send any data over the wire if the host "connected" + using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { writer.WriteUInt64Packed(ownerClientId); - - //if (NetworkConfig.EnableSceneManagement) - //{ - // writer.WriteUInt32Packed(NetworkSceneManager.CurrentSceneIndex); - // writer.WriteByteArray(NetworkSceneManager.CurrentSceneSwitchProgressGuid.ToByteArray()); - //} - writer.WriteSinglePacked(Time.realtimeSinceStartup); - //writer.WriteUInt32Packed((uint)m_ObservedObjects.Count); - - //for (int i = 0; i < m_ObservedObjects.Count; i++) - //{ - // m_ObservedObjects[i].SerializeSceneObject(writer, ownerClientId); - //} - MessageSender.Send(ownerClientId, NetworkConstants.CONNECTION_APPROVED, NetworkChannel.Internal, buffer); } - // Now send the client synchronization scene event - // NSS TODO: This might be something we want to be optional and can be "user controlled" - // as well we might want to have the client receive the approved connection and then request to be synchronized which - // would kick this process off. + // Now inform the newly joined client of the scenes to be loaded as well as synchronize it with all relevant in-scene and dynamically spawned NetworkObjects if (NetworkConfig.EnableSceneManagement) { SceneManager.SynchronizeNetworkObjects(ownerClientId); } - + } + else + { + ConnectedClients[ownerClientId].IsReadyToReceiveMessages = true; } OnClientConnectedCallback?.Invoke(ownerClientId); @@ -1700,58 +1710,8 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? return; } - // NSS TODO: We might want to delay this notification until the client is completely synchronized - //Inform old clients of the new player - foreach (KeyValuePair clientPair in ConnectedClients) - { - if (clientPair.Key == ownerClientId || - ConnectedClients[ownerClientId].PlayerObject == null || - !ConnectedClients[ownerClientId].PlayerObject.Observers.Contains(clientPair.Key)) - { - continue; //The new client. - } - - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) - { - writer.WriteBool(true); - writer.WriteUInt64Packed(ConnectedClients[ownerClientId].PlayerObject.NetworkObjectId); - writer.WriteUInt64Packed(ownerClientId); - - //Does not have a parent - writer.WriteBool(false); - - // This is not a scene object - writer.WriteBool(false); - - writer.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); - - if (ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning(ownerClientId)) - { - writer.WriteBool(true); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.x); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.y); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.z); - - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.x); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.y); - writer.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.z); - } - else - { - writer.WriteBool(false); - } - - writer.WriteBool(false); //No payload data - - if (NetworkConfig.EnableNetworkVariable) - { - ConnectedClients[ownerClientId].PlayerObject.WriteNetworkVariableData(buffer, clientPair.Key); - } - - MessageSender.Send(clientPair.Key, NetworkConstants.ADD_OBJECT, NetworkChannel.Internal, buffer); - } - } + // Separating this into a contained function call for potential further future separation of when this notification is sent. + NotifyPlayerConnected(ownerClientId, playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); } else { @@ -1760,7 +1720,11 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? } } - + /// + /// Notifies all existing clients that a new player has joined + /// + /// new player client identifier + /// the prefab GlobalObjectIdHash value for this player internal void NotifyPlayerConnected(ulong clientId, uint playerPrefabHash) { foreach (KeyValuePair clientPair in ConnectedClients) diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index d15fe4ad13..b1d95503b4 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -61,6 +61,12 @@ public void HandleConnectionRequest(ulong clientId, Stream stream) } } + /// + /// Client Side: handles the connection approved message + /// + /// + /// + /// public void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime) { using (var reader = PooledNetworkReader.Get(stream)) @@ -71,82 +77,6 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv NetworkManager.UpdateNetworkTime(clientId, netTime, receiveTime, true); NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); - - // NSS TODO: We might have the client send a request to be synchronized (could be optional setting?) - } - } - - public void HandleConnectionApprovedOriginal(ulong clientId, Stream stream, float receiveTime) - { - using (var reader = PooledNetworkReader.Get(stream)) - { - NetworkManager.LocalClientId = reader.ReadUInt64Packed(); - - uint sceneIndex = 0; - var sceneSwitchProgressGuid = new Guid(); - - if (NetworkManager.NetworkConfig.EnableSceneManagement) - { - sceneIndex = reader.ReadUInt32Packed(); - sceneSwitchProgressGuid = new Guid(reader.ReadByteArray()); - } - - bool sceneSwitch = NetworkManager.NetworkConfig.EnableSceneManagement && NetworkManager.SceneManager.HasSceneMismatch(sceneIndex); - - float netTime = reader.ReadSinglePacked(); - NetworkManager.UpdateNetworkTime(clientId, netTime, receiveTime, true); - - NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); - - - void DelayedSpawnAction(Stream continuationStream) - { - - using (var continuationReader = PooledNetworkReader.Get(continuationStream)) - { - if (!NetworkManager.NetworkConfig.EnableSceneManagement) - { - NetworkManager.SpawnManager.DestroySceneObjects(); - } - else - { - NetworkManager.SceneManager.PopulateScenePlacedObjects(SceneManager.GetActiveScene()); - } - - var objectCount = continuationReader.ReadUInt32Packed(); - for (int i = 0; i < objectCount; i++) - { - NetworkObject.DeserializeSceneObject(continuationStream as NetworkBuffer, continuationReader, m_NetworkManager); - } - - NetworkManager.IsConnectedClient = true; - NetworkManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId); - } - } - - if (sceneSwitch) - { - UnityAction onSceneLoaded = null; - - var continuationBuffer = new NetworkBuffer(); - continuationBuffer.CopyUnreadFrom(stream); - continuationBuffer.Position = 0; - - void OnSceneLoadComplete() - { - SceneManager.activeSceneChanged -= onSceneLoaded; - NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad = false; - DelayedSpawnAction(continuationBuffer); - } - - onSceneLoaded = (oldScene, newScene) => { OnSceneLoadComplete(); }; - SceneManager.activeSceneChanged += onSceneLoaded; - m_NetworkManager.SceneManager.OnFirstSceneSwitchSync(sceneIndex, sceneSwitchProgressGuid); - } - else - { - DelayedSpawnAction(stream); - } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 059779649a..fd6a3194aa 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -5,12 +5,9 @@ using MLAPI.Configuration; using MLAPI.Exceptions; using MLAPI.Logging; -using MLAPI.Messaging; using MLAPI.Serialization.Pooled; -using MLAPI.Spawning; using UnityEngine; using UnityEngine.SceneManagement; -using MLAPI.Messaging.Buffering; using MLAPI.Serialization; using MLAPI.Transports; @@ -82,15 +79,17 @@ public class NetworkSceneManager internal readonly Dictionary SceneSwitchProgresses = new Dictionary(); internal readonly Dictionary ScenePlacedObjects = new Dictionary(); - private static string s_NextSceneName; - private static bool s_IsSwitching = false; - internal static uint CurrentSceneIndex = 0; - internal static Guid CurrentSceneSwitchProgressGuid = new Guid(); + // Used for observed object synchronization + private readonly List m_ObservedObjects = new List(); + + private static bool s_IsSceneEventActive = false; internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + //Client and Server: used for all scene event processing exception for client synchronization internal SceneEventData SceneEventData; - internal SceneEventData ClientSynchEventData; // For approval/late joining purposes + //Server Side: Used specifically for scene synchronization (late joining and newly approved client connections) + internal SceneEventData ClientSynchEventData; private NetworkManager m_NetworkManager { get; } @@ -101,17 +100,12 @@ internal NetworkSceneManager(NetworkManager networkManager) ClientSynchEventData = new SceneEventData(); } - internal void SetCurrentSceneIndex() - { - var sceneIndex = GetMLAPISceneIndex(SceneManager.GetActiveScene()); - if (sceneIndex != uint.MaxValue) - { - CurrentActiveSceneIndex = CurrentSceneIndex = sceneIndex; - } - } - - - private uint GetMLAPISceneIndex(Scene scene) + /// + /// Returns the MLAPI scene index from a scene + /// + /// + /// MLAPI Scene Index + private uint GetMLAPISceneIndexFromScene(Scene scene) { uint index = 0; if (!SceneNameToIndex.TryGetValue(scene.name, out index)) @@ -126,8 +120,13 @@ private uint GetMLAPISceneIndex(Scene scene) return index; } - - private string GetMLAPISceneNameFromIndex(uint sceneIndex) + /// + /// Returns the scene name from the MLAPI scene index + /// Note: This is not the same as the Build Settings Scenes in Build index + /// + /// MLAPI Scene Index + /// scene name + private string GetSceneNameFromMLAPISceneIndex(uint sceneIndex) { var sceneName = string.Empty; if (!SceneIndexToString.TryGetValue(sceneIndex, out sceneName)) @@ -140,9 +139,6 @@ private string GetMLAPISceneNameFromIndex(uint sceneIndex) return sceneName; } - - internal uint CurrentActiveSceneIndex { get; private set; } = 0; - /// /// Adds a scene during runtime. /// The index is REQUIRED to be unique AND the same across all instances. @@ -164,6 +160,7 @@ public void AddRuntimeSceneName(string sceneName, uint index) /// /// Validates the new scene event request by the server-side code. /// This also initializes some commonly shared values as well as switchSceneProgress + /// NSS TODO: Should switchSceneProgress /// /// /// SceneSwitchProgress (if null it failed) @@ -180,11 +177,11 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn 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 this method."); } - if (s_IsSwitching) + if (s_IsSceneEventActive) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { - NetworkLog.LogWarning("Scene switch already in progress"); + NetworkLog.LogWarning("Scene event is already in progress"); } return null; @@ -203,17 +200,17 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn var switchSceneProgress = new SceneSwitchProgress(m_NetworkManager); SceneSwitchProgresses.Add(switchSceneProgress.Guid, switchSceneProgress); - // NSS TODO: remove any of these values that are no longer needed - CurrentSceneSwitchProgressGuid = switchSceneProgress.Guid; if (!isUnloading) { - CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; + // NSS TODO: This either needs a better name or there has to be a better way of detecting this condition + // The Condition: While a scene is asynchronously loaded, if any new NetworkObjects are spawned they need to be moved into the do not destroy temporary scene + // When it is set: Just before starting the asynchronous loading call + // When it is unset: + // after the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene IsSpawnedObjectsPendingInDontDestroyOnLoad = true; - s_NextSceneName = sceneName; } - s_IsSwitching = true; - + s_IsSceneEventActive = true; switchSceneProgress.OnClientLoadedScene += clientId => { OnNotifyServerClientLoadedScene?.Invoke(switchSceneProgress, clientId); }; switchSceneProgress.OnComplete += timedOut => @@ -237,6 +234,11 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn return switchSceneProgress; } + /// + /// Unloads an additively loaded scene + /// + /// scene name to unload + /// public SceneSwitchProgress UnloadScene(string sceneName) { // Make sure the scene is actually loaded @@ -291,7 +293,7 @@ private void OnClientUnloadScene() return; } - s_IsSwitching = true; + s_IsSceneEventActive = true; var sceneLoad = SceneManager.UnloadSceneAsync(sceneName); @@ -301,7 +303,9 @@ private void OnClientUnloadScene() //OnSceneSwitchStarted?.Invoke(sceneLoad); } - + /// + /// Invoked when the additively loaded scene is unloaded + /// private void OnSceneUnloaded() { using (var buffer = PooledNetworkBuffer.Get()) @@ -312,7 +316,7 @@ private void OnSceneUnloaded() m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } - s_IsSwitching = false; + s_IsSceneEventActive = false; } /// @@ -366,6 +370,14 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene return null; } + foreach (var client in m_NetworkManager.ConnectedClientsList) + { + if (client.ClientId != m_NetworkManager.ServerClientId) + { + client.IsReadyToReceiveMessages = false; + } + } + SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; @@ -396,7 +408,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene { // start loading the scene AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); - sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(); }; + sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); // NSS TODO: Make a single unified notification callback OnSceneSwitchStarted?.Invoke(sceneLoad); @@ -426,60 +438,24 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) MoveObjectsToDontDestroyOnLoad(); } + // NSS TODO: This either needs a better name or there has to be a better way of detecting this condition + // The Condition: While a scene is asynchronously loaded, if any new NetworkObjects are spawned they need to be moved into the do not destroy temporary scene + // When it is set: Just before starting the asynchronous loading call + // When it is unset: + // after the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene IsSpawnedObjectsPendingInDontDestroyOnLoad = true; var sceneLoad = SceneManager.LoadSceneAsync(sceneName, SceneEventData.LoadSceneMode); - - s_NextSceneName = sceneName; - - sceneLoad.completed += asyncOp2 => OnSceneLoaded(); + sceneLoad.completed += asyncOp2 => OnSceneLoaded(sceneName); OnSceneSwitchStarted?.Invoke(sceneLoad); } - /// - /// Client approval specific - /// - /// - /// - internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid) - { - if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning("Server requested a scene switch to a non-registered scene"); - } - - return; - } - - if (SceneManager.GetActiveScene().name == sceneName) - { - return; //This scene is already loaded. This usually happens at first load - } - - s_NextSceneName = sceneName; - CurrentActiveSceneIndex = SceneNameToIndex[sceneName]; - - IsSpawnedObjectsPendingInDontDestroyOnLoad = true; - SceneManager.LoadScene(sceneName); - - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) - { - writer.WriteByteArray(switchSceneGuid.ToByteArray()); - m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, NetworkChannel.Internal, buffer); - } - - s_IsSwitching = false; - } - /// /// Client and Server: Generic on scene loaded callback method to be called upon a scene loading /// - private void OnSceneLoaded() + private void OnSceneLoaded(string sceneName) { - var nextScene = SceneManager.GetSceneByName(s_NextSceneName); + var nextScene = SceneManager.GetSceneByName(sceneName); if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { SceneManager.SetActiveScene(nextScene); @@ -494,12 +470,13 @@ private void OnSceneLoaded() MoveObjectsToScene(nextScene); } - // NSS TODO: I think this can be determined differently and removed completely + // NSS TODO: This either needs a better name or there has to be a better way of detecting this condition + // The Condition: While a scene is asynchronously loaded, if any new NetworkObjects are spawned they need to be moved into the do not destroy temporary scene + // When it is set: Just before starting the asynchronous loading call + // When it is unset: + // after the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene IsSpawnedObjectsPendingInDontDestroyOnLoad = false; - // NSS TODO: We might want to set this sooner, what happens if there is a connection during asynchronous scene loading? - CurrentSceneIndex = CurrentActiveSceneIndex; - if (m_NetworkManager.IsServer) { OnServerLoadedScene(); @@ -510,7 +487,6 @@ private void OnSceneLoaded() } } - /// /// Server specific on scene loaded callback method invoked by OnSceneLoading only /// @@ -527,13 +503,34 @@ private void OnServerLoadedScene() for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++) { - if (m_NetworkManager.ConnectedClientsList[j].ClientId != m_NetworkManager.ServerClientId) + var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId; + if (clientId != m_NetworkManager.ServerClientId) { using (var buffer = PooledNetworkBuffer.Get()) { using (var writer = PooledNetworkWriter.Get(buffer)) { - SynchronizeInSceneObjects(m_NetworkManager.ConnectedClientsList[j].ClientId, writer); + writer.WriteObjectPacked(SceneEventData); + + uint sceneObjectsToSpawn = 0; + + foreach (var keyValuePair in ScenePlacedObjects) + { + if (keyValuePair.Value.Observers.Contains(clientId)) + { + sceneObjectsToSpawn++; + } + } + + // Write number of scene objects to spawn + writer.WriteUInt32Packed(sceneObjectsToSpawn); + foreach (var keyValuePair in ScenePlacedObjects) + { + if (keyValuePair.Value.Observers.Contains(clientId)) + { + keyValuePair.Value.SerializeSceneObject(writer, clientId); + } + } m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -547,42 +544,11 @@ private void OnServerLoadedScene() OnClientSceneLoadingEventCompleted(m_NetworkManager.LocalClientId, SceneEventData.SwitchSceneGuid); } - s_IsSwitching = false; + s_IsSceneEventActive = false; OnSceneSwitched?.Invoke(); } - /// - /// NSS TODO: This might go back into the above method - /// - /// - /// - internal void SynchronizeInSceneObjects(ulong clientId, NetworkWriter writer) - { - writer.WriteObjectPacked(SceneEventData); - - uint sceneObjectsToSpawn = 0; - - foreach (var keyValuePair in ScenePlacedObjects) - { - if (keyValuePair.Value.Observers.Contains(clientId)) - { - sceneObjectsToSpawn++; - } - } - - // Write number of scene objects to spawn - writer.WriteUInt32Packed(sceneObjectsToSpawn); - foreach (var keyValuePair in ScenePlacedObjects) - { - if (keyValuePair.Value.Observers.Contains(clientId)) - { - keyValuePair.Value.SerializeSceneObject(writer, clientId); - } - } - } - - /// /// Client specific on scene loaded callback method invoked by OnSceneLoading only /// @@ -606,18 +572,16 @@ private void OnClientLoadedScene() m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } - s_IsSwitching = false; + s_IsSceneEventActive = false; OnSceneSwitched?.Invoke(); } - - private readonly List m_ObservedObjects = new List(); - /// - /// Server Side: This is used for late joining players and players that have just had their connection approved + /// Server Side Only: + /// This is used for late joining players and players that have just had their connection approved /// - /// + /// newly joined client identifier internal void SynchronizeNetworkObjects(ulong ownerClientId) { m_ObservedObjects.Clear(); @@ -639,7 +603,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) for (int i = 0; i < SceneManager.sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); - uint malpiSceneIndex = GetMLAPISceneIndex(scene); + uint malpiSceneIndex = GetMLAPISceneIndexFromScene(scene); // This would depend upon whether we are additive or note if (activeScene == scene) @@ -667,9 +631,13 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) } + /// + /// This is called when the client receives the SCENE_EVENT of type SceneEventData.SceneEventTypes.SYNC + /// Note: This can be invoked several times recursively by the client (depending upon how many additive scenes need to be loaded + /// + /// MLAPI sceneIndex to load private void OnClientBeginSynch(uint sceneIndex) { - if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) @@ -684,8 +652,7 @@ private void OnClientBeginSynch(uint sceneIndex) if (sceneName != activeScene.name) { - var sceneLoad = SceneManager.LoadSceneAsync(sceneName, sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode:LoadSceneMode.Additive); - s_NextSceneName = sceneName; + var sceneLoad = SceneManager.LoadSceneAsync(sceneName, sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive); sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex); } else @@ -694,36 +661,45 @@ private void OnClientBeginSynch(uint sceneIndex) } } - + /// + /// Once a scene is loaded ( or if it was already loaded) this gets called. + /// This handles all of the in-scene and dynamically spawned NetworkObject synchronization + /// + /// MLAPI scene index that was loaded private void ClientLoadedSynchronization(uint sceneIndex) { - var nextScene = SceneManager.GetSceneByName(GetMLAPISceneNameFromIndex(sceneIndex)); - if(nextScene == null) + var nextScene = SceneManager.GetSceneByName(GetSceneNameFromMLAPISceneIndex(sceneIndex)); + if (nextScene == null) { return; } - if ( (sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) + // NSS TODO: Add to proposal's MTT discussion topics: Should we cover switching the active scene for V1.0.0? + if ((sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) { SceneManager.SetActiveScene(nextScene); } - //Get all NetworkObjects loaded by the scene + // Get all NetworkObjects loaded by the scene (in-scene NetworkObjects) PopulateScenePlacedObjects(nextScene); + // Synchronize the NetworkObjects for this scene SceneEventData.SynchronizeSceneNetworkObjects(sceneIndex, m_NetworkManager); + // If this was the base scene (i.e. active scene) then move all NetworkObjects to this scene (if they were moved out) if ((sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) { MoveObjectsToScene(nextScene); } + // Check to see if we still have scenes to load and synchronize with if (!SceneEventData.IsDoneWithSynchronization()) { HandleClientSceneEvent(null); } else { + // All scenes are synchronized, let the server know we are done synchronizing m_NetworkManager.IsConnectedClient = true; m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId); using (var buffer = PooledNetworkBuffer.Get()) @@ -738,21 +714,6 @@ private void ClientLoadedSynchronization(uint sceneIndex) internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; - // Called on server - internal void OnClientSceneLoadingEventCompleted(ulong clientId, Guid switchSceneGuid) - { - if (switchSceneGuid == Guid.Empty) - { - // If Guid is empty it means the client has loaded the start scene of the server and the server would never have a switchSceneProgresses created for the start scene. - return; - } - - if (SceneSwitchProgresses.TryGetValue(switchSceneGuid, out SceneSwitchProgress progress)) - { - SceneSwitchProgresses[switchSceneGuid].AddClientAsDone(clientId); - } - } - internal void RemoveClientFromSceneSwitchProgresses(ulong clientId) { foreach (var switchSceneProgress in SceneSwitchProgresses.Values) @@ -831,6 +792,20 @@ internal void AllClientsReady(ulong[] clientIds, ulong[] timedOutClientIds) OnNotifyClientAllClientsLoadedScene?.Invoke(clientIds, timedOutClientIds); } + // Called on server + internal void OnClientSceneLoadingEventCompleted(ulong clientId, Guid switchSceneGuid) + { + if (switchSceneGuid == Guid.Empty) + { + // If Guid is empty it means the client has loaded the start scene of the server and the server would never have a switchSceneProgresses created for the start scene. + return; + } + + if (SceneSwitchProgresses.TryGetValue(switchSceneGuid, out SceneSwitchProgress progress)) + { + SceneSwitchProgresses[switchSceneGuid].AddClientAsDone(clientId); + } + } /// /// Client Side: Handles incoming SCENE_EVENT messages @@ -877,6 +852,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) case SceneEventData.SceneEventTypes.SWITCH_COMPLETE: { OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); + m_NetworkManager.ConnectedClients[clientId].IsReadyToReceiveMessages = true; break; } case SceneEventData.SceneEventTypes.LOAD_COMPLETE: @@ -891,7 +867,9 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } case SceneEventData.SceneEventTypes.SYNC_COMPLETE: { - //m_NetworkManager.NotifyPlayerConnected(clientId, m_NetworkManager.NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + m_NetworkManager.ConnectedClients[clientId].IsReadyToReceiveMessages = true; + // NSS TOOD: The scene event local notification needs to be called + //m_NetworkManager.NotifyPlayerConnected(clientId); break; } default: @@ -939,279 +917,4 @@ public void HandleSceneEvent(ulong clientId, Stream stream) } } } - - [Serializable] - public class SceneEventData : INetworkSerializable, IDisposable - { - public enum SceneEventTypes - { - SWITCH, //Server to client full scene switch (i.e. single mode and destroy everything) - LOAD, //Server to client load additive scene - UNLOAD, //Server to client unload additive scene - SYNC, //Server to client late join approval synchronization - SWITCH_COMPLETE, //Client to server - LOAD_COMPLETE, //Client to server - UNLOAD_COMPLETE, //Client to server - SYNC_COMPLETE, //Client to server - } - - public SceneEventTypes SceneEventType; - public LoadSceneMode LoadSceneMode; - public Guid SwitchSceneGuid; - - public uint SceneIndex; - public ulong TargetClientId; - - private Dictionary> m_SceneNetworkObjects; - private Dictionary m_SceneNetworkObjectDataOffsets; - internal PooledNetworkBuffer InternalBuffer; - - public void InitializeForSynch() - { - if(m_SceneNetworkObjects == null) - { - m_SceneNetworkObjects = new Dictionary>(); - } - else - { - m_SceneNetworkObjects.Clear(); - } - } - - public uint GetNextSceneSynchronizationIndex() - { - if(m_SceneNetworkObjectDataOffsets.ContainsKey(SceneIndex)) - { - return SceneIndex; - } - return m_SceneNetworkObjectDataOffsets.First().Key; - } - - public bool IsDoneWithSynchronization() - { - return (m_SceneNetworkObjectDataOffsets.Count == 0); - } - - public void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObject) - { - if(!m_SceneNetworkObjects.ContainsKey(sceneIndex)) - { - m_SceneNetworkObjects.Add(sceneIndex, new List()); - } - - m_SceneNetworkObjects[sceneIndex].Add(networkObject); - } - - /// - /// Determines if the scene event type was intended for the client ( or server ) - /// - /// true (client should handle this message) false (server should handle this message) - public bool IsSceneEventClientSide() - { - switch (SceneEventType) - { - case SceneEventTypes.LOAD: - case SceneEventTypes.SWITCH: - case SceneEventTypes.UNLOAD: - case SceneEventTypes.SYNC: - { - return true; - } - } - return false; - } - - /// - /// Serialize this class instance - /// - /// - private void OnWrite(NetworkWriter writer) - { - writer.WriteByte((byte)SceneEventType); - - - writer.WriteByte((byte)LoadSceneMode); - if (SceneEventType != SceneEventTypes.SYNC) - { - writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); - } - - writer.WriteUInt32Packed(SceneIndex); - - if (SceneEventType == SceneEventTypes.SYNC) - { - writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); - - if (m_SceneNetworkObjects.Count() > 0) - { - string msg = "Scene Associated NetworkObjects Write:\n"; - foreach (var keypair in m_SceneNetworkObjects) - { - writer.WriteUInt32Packed(keypair.Key); - msg += $"Scene ID [{keypair.Key}] NumNetworkObjects:[{keypair.Value.Count}]\n"; - writer.WriteInt32Packed(keypair.Value.Count); - var positionStart = writer.GetStream().Position; - // Size Place Holder (For offset purposes, needs to not be packed) - writer.WriteUInt32(0); - var totalBytes = 0; - foreach (var networkObject in keypair.Value) - { - var noStart = writer.GetStream().Position; - - networkObject.SerializeSceneObject(writer, TargetClientId); - var noStop = writer.GetStream().Position; - totalBytes += (int)(noStop - noStart); - msg += $"Included: {networkObject.name} Bytes: {totalBytes} \n"; - } - var positionEnd = writer.GetStream().Position; - var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); - writer.GetStream().Position = positionStart; - // Write the total size written to the stream by NetworkObjects being serialized - writer.WriteUInt32(bytesWritten); - writer.GetStream().Position = positionEnd; - msg += $"Wrote [{bytesWritten}] bytes of NetworkObject data. Verification: {totalBytes}\n"; - } - - Debug.Log(msg); - } - } - } - - /// - /// Deserialize this class instance - /// - /// - private void OnRead(NetworkReader reader) - { - var sceneEventTypeValue = reader.ReadByte(); - - if (Enum.IsDefined(typeof(SceneEventTypes), sceneEventTypeValue)) - { - SceneEventType = (SceneEventTypes)sceneEventTypeValue; - } - else - { - Debug.LogError($"Serialization Read Error: {nameof(SceneEventType)} vale {sceneEventTypeValue} is not within the range of the defined {nameof(SceneEventTypes)} enumerator!"); - } - - var loadSceneModeValue = reader.ReadByte(); - - if (Enum.IsDefined(typeof(LoadSceneMode), loadSceneModeValue)) - { - LoadSceneMode = (LoadSceneMode)loadSceneModeValue; - } - else - { - Debug.LogError($"Serialization Read Error: {nameof(LoadSceneMode)} vale {loadSceneModeValue} is not within the range of the defined {nameof(LoadSceneMode)} enumerator!"); - } - - if (SceneEventType != SceneEventTypes.SYNC) - { - SwitchSceneGuid = new Guid(reader.ReadByteArray()); - } - - SceneIndex = reader.ReadUInt32Packed(); - - if (SceneEventType == SceneEventTypes.SYNC) - { - var keyPairCount = reader.ReadInt32Packed(); - - if (keyPairCount > 0) - { - if (m_SceneNetworkObjectDataOffsets == null) - { - m_SceneNetworkObjectDataOffsets = new Dictionary(); - } - else - { - m_SceneNetworkObjectDataOffsets.Clear(); - } - - InternalBuffer.Position = 0; - - using (var writer = PooledNetworkWriter.Get(InternalBuffer)) - { - for (int i = 0; i < keyPairCount; i++) - { - var key = reader.ReadUInt32Packed(); - var count = reader.ReadInt32Packed(); - // how many bytes to read for this scene set - var bytesToRead = (ulong)reader.ReadUInt32(); - // We store off the current position of the stream as it pertains to the scene relative NetworkObjects - m_SceneNetworkObjectDataOffsets.Add(key, InternalBuffer.Position); - writer.WriteInt32Packed(count); - writer.ReadAndWrite(reader, (long)bytesToRead); - } - } - } - } - } - - public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkManager) - { - if (m_SceneNetworkObjectDataOffsets.ContainsKey(sceneId)) - { - // Point to the appropriate offset - InternalBuffer.Position = m_SceneNetworkObjectDataOffsets[sceneId]; - - using (var reader = PooledNetworkReader.Get(InternalBuffer)) - { - // Process all NetworkObjects for this scene - var newObjectsCount = reader.ReadInt32Packed(); - - for (int i = 0; i < newObjectsCount; i++) - { - NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); - } - } - - // Remove each entry after it is processed so we know when we are done - m_SceneNetworkObjectDataOffsets.Remove(sceneId); - } - } - - /// - /// INetworkSerializable implementation method - /// - /// serializer passed in during serialization - public void NetworkSerialize(NetworkSerializer serializer) - { - if (serializer.IsReading) - { - OnRead(serializer.Reader); - } - else - { - OnWrite(serializer.Writer); - } - } - - /// - /// Used to store data during an asynchronous scene loading event - /// - /// - internal void CopyUnreadFromStream(Stream stream) - { - InternalBuffer.Position = 0; - InternalBuffer.CopyUnreadFrom(stream); - InternalBuffer.Position = 0; - } - - /// - /// Used to release the pooled network buffer - /// - public void Dispose() - { - if (InternalBuffer != null) - { - NetworkBufferPool.PutBackInPool(InternalBuffer); - InternalBuffer = null; - } - } - - public SceneEventData() - { - InternalBuffer = NetworkBufferPool.GetBuffer(); - } - } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs new file mode 100644 index 0000000000..880f7d2983 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -0,0 +1,312 @@ +using System.Collections.Generic; +using System; +using System.IO; +using System.Linq; +using MLAPI.Serialization.Pooled; +using UnityEngine; +using UnityEngine.SceneManagement; +using MLAPI.Serialization; + + +namespace MLAPI.SceneManagement +{ + [Serializable] + public class SceneEventData : INetworkSerializable, IDisposable + { + public enum SceneEventTypes + { + SWITCH, //Server to client full scene switch (i.e. single mode and destroy everything) + LOAD, //Server to client load additive scene + UNLOAD, //Server to client unload additive scene + SYNC, //Server to client late join approval synchronization + SWITCH_COMPLETE, //Client to server + LOAD_COMPLETE, //Client to server + UNLOAD_COMPLETE, //Client to server + SYNC_COMPLETE, //Client to server + } + + public SceneEventTypes SceneEventType; + public LoadSceneMode LoadSceneMode; + public Guid SwitchSceneGuid; + + public uint SceneIndex; + public ulong TargetClientId; + + private Dictionary> m_SceneNetworkObjects; + private Dictionary m_SceneNetworkObjectDataOffsets; + internal PooledNetworkBuffer InternalBuffer; + + /// + /// Client Side: + /// Gets the next scene index to be loaded for approval and/or late joining + /// + /// + public uint GetNextSceneSynchronizationIndex() + { + if (m_SceneNetworkObjectDataOffsets.ContainsKey(SceneIndex)) + { + return SceneIndex; + } + return m_SceneNetworkObjectDataOffsets.First().Key; + } + + /// + /// Client Side: + /// Determines if all scenes have been processed during the synchronization process + /// + /// true/false + public bool IsDoneWithSynchronization() + { + return (m_SceneNetworkObjectDataOffsets.Count == 0); + } + + /// + /// Server Side: + /// Called just before the synchronization process + /// + public void InitializeForSynch() + { + if (m_SceneNetworkObjects == null) + { + m_SceneNetworkObjects = new Dictionary>(); + } + else + { + m_SceneNetworkObjects.Clear(); + } + } + + /// + /// Server Side: + /// Used during the synchronization process to associate NetworkObjects with scenes + /// + /// + /// + public void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObject) + { + if (!m_SceneNetworkObjects.ContainsKey(sceneIndex)) + { + m_SceneNetworkObjects.Add(sceneIndex, new List()); + } + + m_SceneNetworkObjects[sceneIndex].Add(networkObject); + } + + /// + /// Determines if the scene event type was intended for the client ( or server ) + /// + /// true (client should handle this message) false (server should handle this message) + public bool IsSceneEventClientSide() + { + switch (SceneEventType) + { + case SceneEventTypes.LOAD: + case SceneEventTypes.SWITCH: + case SceneEventTypes.UNLOAD: + case SceneEventTypes.SYNC: + { + return true; + } + } + return false; + } + + /// + /// Serializes this class instance + /// + /// + private void OnWrite(NetworkWriter writer) + { + writer.WriteByte((byte)SceneEventType); + + writer.WriteByte((byte)LoadSceneMode); + + if (SceneEventType != SceneEventTypes.SYNC) + { + writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); + } + + writer.WriteUInt32Packed(SceneIndex); + + if (SceneEventType == SceneEventTypes.SYNC) + { + writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); + + if (m_SceneNetworkObjects.Count() > 0) + { + string msg = "Scene Associated NetworkObjects Write:\n"; + foreach (var keypair in m_SceneNetworkObjects) + { + writer.WriteUInt32Packed(keypair.Key); + msg += $"Scene ID [{keypair.Key}] NumNetworkObjects:[{keypair.Value.Count}]\n"; + writer.WriteInt32Packed(keypair.Value.Count); + var positionStart = writer.GetStream().Position; + // Size Place Holder (For offset purposes, needs to not be packed) + writer.WriteUInt32(0); + var totalBytes = 0; + foreach (var networkObject in keypair.Value) + { + var noStart = writer.GetStream().Position; + + networkObject.SerializeSceneObject(writer, TargetClientId); + var noStop = writer.GetStream().Position; + totalBytes += (int)(noStop - noStart); + msg += $"Included: {networkObject.name} Bytes: {totalBytes} \n"; + } + var positionEnd = writer.GetStream().Position; + var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); + writer.GetStream().Position = positionStart; + // Write the total size written to the stream by NetworkObjects being serialized + writer.WriteUInt32(bytesWritten); + writer.GetStream().Position = positionEnd; + msg += $"Wrote [{bytesWritten}] bytes of NetworkObject data. Verification: {totalBytes}\n"; + } + + Debug.Log(msg); + } + } + } + + /// + /// Deserialize this class instance + /// + /// + private void OnRead(NetworkReader reader) + { + var sceneEventTypeValue = reader.ReadByte(); + + if (Enum.IsDefined(typeof(SceneEventTypes), sceneEventTypeValue)) + { + SceneEventType = (SceneEventTypes)sceneEventTypeValue; + } + else + { + Debug.LogError($"Serialization Read Error: {nameof(SceneEventType)} vale {sceneEventTypeValue} is not within the range of the defined {nameof(SceneEventTypes)} enumerator!"); + // NSS TODO: Add to proposal's MTT discussion topics: Should we assert here? + } + + var loadSceneModeValue = reader.ReadByte(); + + if (Enum.IsDefined(typeof(LoadSceneMode), loadSceneModeValue)) + { + LoadSceneMode = (LoadSceneMode)loadSceneModeValue; + } + else + { + Debug.LogError($"Serialization Read Error: {nameof(LoadSceneMode)} vale {loadSceneModeValue} is not within the range of the defined {nameof(LoadSceneMode)} enumerator!"); + // NSS TODO: Add to proposal's MTT discussion topics: Should we assert here? + } + + if (SceneEventType != SceneEventTypes.SYNC) + { + SwitchSceneGuid = new Guid(reader.ReadByteArray()); + } + + SceneIndex = reader.ReadUInt32Packed(); + + if (SceneEventType == SceneEventTypes.SYNC) + { + var keyPairCount = reader.ReadInt32Packed(); + + if (keyPairCount > 0) + { + if (m_SceneNetworkObjectDataOffsets == null) + { + m_SceneNetworkObjectDataOffsets = new Dictionary(); + } + else + { + m_SceneNetworkObjectDataOffsets.Clear(); + } + + InternalBuffer.Position = 0; + + using (var writer = PooledNetworkWriter.Get(InternalBuffer)) + { + for (int i = 0; i < keyPairCount; i++) + { + var key = reader.ReadUInt32Packed(); + var count = reader.ReadInt32Packed(); + // how many bytes to read for this scene set + var bytesToRead = (ulong)reader.ReadUInt32(); + // We store off the current position of the stream as it pertains to the scene relative NetworkObjects + m_SceneNetworkObjectDataOffsets.Add(key, InternalBuffer.Position); + writer.WriteInt32Packed(count); + writer.ReadAndWrite(reader, (long)bytesToRead); + } + } + } + } + } + + public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkManager) + { + if (m_SceneNetworkObjectDataOffsets.ContainsKey(sceneId)) + { + // Point to the appropriate offset + InternalBuffer.Position = m_SceneNetworkObjectDataOffsets[sceneId]; + + using (var reader = PooledNetworkReader.Get(InternalBuffer)) + { + // Process all NetworkObjects for this scene + var newObjectsCount = reader.ReadInt32Packed(); + + for (int i = 0; i < newObjectsCount; i++) + { + NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); + } + } + + // Remove each entry after it is processed so we know when we are done + m_SceneNetworkObjectDataOffsets.Remove(sceneId); + } + } + + /// + /// INetworkSerializable implementation method for SceneEventData + /// + /// serializer passed in during serialization + public void NetworkSerialize(NetworkSerializer serializer) + { + if (serializer.IsReading) + { + OnRead(serializer.Reader); + } + else + { + OnWrite(serializer.Writer); + } + } + + /// + /// Used to store data during an asynchronous scene loading event + /// + /// + internal void CopyUnreadFromStream(Stream stream) + { + InternalBuffer.Position = 0; + InternalBuffer.CopyUnreadFrom(stream); + InternalBuffer.Position = 0; + } + + /// + /// Used to release the pooled network buffer + /// + public void Dispose() + { + if (InternalBuffer != null) + { + NetworkBufferPool.PutBackInPool(InternalBuffer); + InternalBuffer = null; + } + } + + /// + /// Constructor + /// + public SceneEventData() + { + InternalBuffer = NetworkBufferPool.GetBuffer(); + } + } +} diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs.meta b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs.meta new file mode 100644 index 0000000000..1d7827c50a --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4e25f9d4b699684183b2a06c55349fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs index 871556170b..0f18031ba1 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs @@ -362,6 +362,11 @@ internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject return; } + if (!NetworkManager.ConnectedClients[clientId].IsReadyToReceiveMessages) + { + return; + } + var rpcQueueContainer = NetworkManager.RpcQueueContainer; var buffer = PooledNetworkBuffer.Get(); diff --git a/testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab b/testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab new file mode 100644 index 0000000000..767a6dc929 --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &5806514443914707465 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 47844432802121000, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Name + value: SpawnObjectRedSphereServerOnly + objectReference: {fileID: 0} + - target: {fileID: 47844432802121010, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4067897634150729404, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: GlobalObjectIdHash + value: 951099334 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 894296ee6a442ef49b3637b680c7bdf7, type: 3} diff --git a/testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab.meta b/testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab.meta new file mode 100644 index 0000000000..0617bb4ec2 --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectRedSphereServerOnly.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 27c27433e278c054dbe3bae7d411cfab +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab b/testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab new file mode 100644 index 0000000000..1fbef0acb3 --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &5082954928110471088 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 47844432802121000, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Name + value: SpawnObjectYellowSphereServerOnly + objectReference: {fileID: 0} + - target: {fileID: 47844432802121010, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 47844432802121013, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4067897634150729404, guid: 894296ee6a442ef49b3637b680c7bdf7, + type: 3} + propertyPath: GlobalObjectIdHash + value: 951099334 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 894296ee6a442ef49b3637b680c7bdf7, type: 3} diff --git a/testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab.meta b/testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab.meta new file mode 100644 index 0000000000..047874a71f --- /dev/null +++ b/testproject/Assets/Prefabs/SpawnObjectYellowSphereServerOnly.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e04a7aaf01a4ad145a945161a18bf49c +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity index be684b14a5..06d7eb747c 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity @@ -250,8 +250,9 @@ MonoBehaviour: SpawnsPerSecond: 1 PoolSize: 32 ObjectSpeed: 8 - EnableHandler: 0 + EnableHandler: 1 RegisterUsingNetworkObject: 0 - ServerObjectToPool: {fileID: 238713212259395427, guid: 0aca3eef90d204641888a517edcc951f, + ServerObjectToPool: {fileID: 5781804285550429985, guid: 27c27433e278c054dbe3bae7d411cfab, + type: 3} + ClientObjectToPool: {fileID: 238713212259395427, guid: 0aca3eef90d204641888a517edcc951f, type: 3} - ClientObjectToPool: {fileID: 0} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity index e3869a6ef9..3eeae9adc4 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity @@ -250,8 +250,9 @@ MonoBehaviour: SpawnsPerSecond: 1 PoolSize: 32 ObjectSpeed: 8 - EnableHandler: 0 + EnableHandler: 1 RegisterUsingNetworkObject: 0 - ServerObjectToPool: {fileID: 7900845470658144705, guid: 9f2cc6554ae21e3498fb4cc28a07108d, + ServerObjectToPool: {fileID: 5054079819822531224, guid: e04a7aaf01a4ad145a945161a18bf49c, + type: 3} + ClientObjectToPool: {fileID: 7900845470658144705, guid: 9f2cc6554ae21e3498fb4cc28a07108d, type: 3} - ClientObjectToPool: {fileID: 0} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 7171b2986f..5575eddf79 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -1527,6 +1527,18 @@ MonoBehaviour: SourcePrefabToOverride: {fileID: 0} SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 5781804285550429985, guid: 27c27433e278c054dbe3bae7d411cfab, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 5054079819822531224, guid: e04a7aaf01a4ad145a945161a18bf49c, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} ReceiveTickrate: 64 NetworkTickIntervalSec: 0.05 MaxReceiveEventsPerTickRate: 500 diff --git a/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity b/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity index de97ee675c..4a2d8dd9ae 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity @@ -1012,6 +1012,7 @@ MonoBehaviour: - SceneTransitioningTest - SecondSceneToLoad - ThirdSceneToLoad + RegisteredSceneAssets: [] AllowRuntimeSceneChanges: 0 PlayerPrefab: {fileID: 4079352819444256614, guid: c16f03336b6104576a565ef79ad643c0, type: 3} @@ -1024,6 +1025,24 @@ MonoBehaviour: SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 8059323910720821982, guid: d143db9172e1f234e99d450286b77695, type: 3} + - Override: 0 + Prefab: {fileID: 1086419352113069865, guid: ed799d5402b325c4f8f41918224a1ba7, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 1086419352113069865, guid: 8a415e2c2210434458836779302bc4d3, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 8059323910720821982, guid: d143db9172e1f234e99d450286b77695, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} ReceiveTickrate: 64 NetworkTickIntervalSec: 0.05 MaxReceiveEventsPerTickRate: 500 @@ -1155,6 +1174,7 @@ MonoBehaviour: GlobalObjectIdHash: 2782806529 AlwaysReplicateAsRoot: 0 DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 --- !u!1 &1332123091 GameObject: m_ObjectHideFlags: 0 @@ -2553,6 +2573,7 @@ MonoBehaviour: GlobalObjectIdHash: 2267100048 AlwaysReplicateAsRoot: 0 DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 --- !u!1001 &2848221156282925290 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index fddc0a3c7e..f8d839f64c 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -108,9 +108,16 @@ private void OnDestroy() if (SwitchScene) { SwitchScene.OnSceneSwitchBegin -= OnSceneSwitchBegin; + + SwitchScene.OnSceneSwitchCompleted -= SwitchScene_OnSceneSwitchCompleted; } } + private void SwitchScene_OnSceneSwitchCompleted() + { + InitializeObjectPool(); + } + // Start is called before the first frame update private void Start() { @@ -122,6 +129,7 @@ private void Start() if (SwitchScene) { SwitchScene.OnSceneSwitchBegin += OnSceneSwitchBegin; + SwitchScene.OnSceneSwitchCompleted += SwitchScene_OnSceneSwitchCompleted; } //Call this again in case we didn't have access to the NetworkManager already (i.e. first scene loaded) @@ -180,14 +188,21 @@ public override void OnNetworkSpawn() /// public void InitializeObjectPool() { - + // Start by defining the server only network prefab for pooling m_ObjectToSpawn = ServerObjectToPool; - if (!IsServer && EnableHandler) + + // If we are a host, then we have to get the NetworkPrefab Override (if one exists) + if (IsHost && !EnableHandler) + { + m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(ServerObjectToPool); + } + // If we are a client and we are using the custom prefab override handler, then we need to use that for our pool + else if (IsClient && EnableHandler) { m_ObjectToSpawn = ClientObjectToPool; } - if (IsServer) + if (EnableHandler || IsServer) { m_ObjectPool = new List(PoolSize); @@ -343,9 +358,13 @@ public class MyCustomPrefabSpawnHandler : INetworkPrefabInstanceHandler public NetworkObject HandleNetworkPrefabSpawn(ulong ownerClientId, Vector3 position, Quaternion rotation) { var obj = m_PrefabPool.GetObject(); - obj.transform.position = position; - obj.transform.rotation = rotation; - return obj.GetComponent(); + if (obj != null) + { + obj.transform.position = position; + obj.transform.rotation = rotation; + return obj.GetComponent(); + } + return null; } public void HandleNetworkPrefabDestroy(NetworkObject networkObject) { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index cc04c481a3..8176f03564 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -168,13 +168,21 @@ public override void OnNetworkSpawn() /// public void InitializeObjectPool() { + // Start by defining the server only network prefab for pooling m_ObjectToSpawn = ServerObjectToPool; - if (!IsServer && EnableHandler) + + // If we are a host, then we have to get the NetworkPrefab Override (if one exists) + if (IsHost && !EnableHandler) + { + m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); + } + // If we are a client and we are using the custom prefab override handler, then we need to use that for our pool + else if (IsClient && EnableHandler) { m_ObjectToSpawn = ClientObjectToPool; } - if (IsServer) + if (EnableHandler || IsServer) { m_ObjectPool = new List(PoolSize); @@ -193,27 +201,17 @@ public GameObject GetObject() { if (m_ObjectPool != null) { - if (m_IsSpawningObjects) + foreach (var obj in m_ObjectPool) { - foreach (var obj in m_ObjectPool) + if (!obj.activeInHierarchy) { - if (m_IsSpawningObjects) - { - if (!obj.activeInHierarchy) - { - obj.SetActive(true); - return obj; - } - } - else - { - return null; - } + obj.SetActive(true); + return obj; } - var newObj = AddNewInstance(); - newObj.SetActive(true); - return newObj; } + var newObj = AddNewInstance(); + newObj.SetActive(true); + return newObj; } return null; } From 88617d2e56586e63917936fc8216ce45e1862f8a Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 11 Jul 2021 15:06:37 -0500 Subject: [PATCH 017/106] feat Improved the NetworkPrefabPool and NetworkPrefabPoolAdditive implementations to properly handle the various potential use case scenarios. These changes assure that both the client and the server leverage from the pool. This also takes into account NetworkManager defined NetworkPrefab overrides. Added temporary LoadSceneEvent callback that notifies if an additive scene is being loaded or unloaded. Updated the NetworkPrefabPoolAdditive sample to demonstrate how a user can assure their NetworkObjects are "spawned" in the same additive scene as their spawn generator. This pattern will always destroy all NetworkObjects if the additive scene is unloaded. Added sorting of NetworkObjects just prior to the server side serializing them in order to assure INetworkPrefabInstanceHandler implementations are registered first, this is required in order to assure that the handler is registered before any NetworkObjects associated with it are spawned. --- .../SceneManagement/NetworkSceneManager.cs | 23 +++++++-- .../Runtime/SceneManagement/SceneEventData.cs | 36 ++++++++++++- .../AdditiveScene1.unity | 3 +- .../AdditiveScene2.unity | 3 +- .../SceneTransitioningBase1.unity | 2 +- .../SceneTransitioningBase2.unity | 2 +- .../SceneTransitioningTest.unity | 2 +- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 18 ++++++- .../Scripts/NetworkPrefabPoolAdditive.cs | 51 +++++++++++++++++-- 9 files changed, 122 insertions(+), 18 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index fd6a3194aa..5a81474301 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -28,6 +28,8 @@ public class NetworkSceneManager /// public delegate void SceneSwitchStartedDelegate(AsyncOperation operation); + public delegate void AdditiveSceneEventDelegate(AsyncOperation operation, string sceneName, bool isLoading); + /// /// Delegate for when a client has reported to the server that it has completed scene transition /// @@ -51,6 +53,8 @@ public class NetworkSceneManager /// public event SceneSwitchedDelegate OnSceneSwitched; + public event AdditiveSceneEventDelegate OnAdditiveSceneEvent; + /// /// Event that is invoked when a local scene switch has started /// @@ -105,7 +109,7 @@ internal NetworkSceneManager(NetworkManager networkManager) /// /// /// MLAPI Scene Index - private uint GetMLAPISceneIndexFromScene(Scene scene) + internal uint GetMLAPISceneIndexFromScene(Scene scene) { uint index = 0; if (!SceneNameToIndex.TryGetValue(scene.name, out index)) @@ -126,7 +130,7 @@ private uint GetMLAPISceneIndexFromScene(Scene scene) /// /// MLAPI Scene Index /// scene name - private string GetSceneNameFromMLAPISceneIndex(uint sceneIndex) + internal string GetSceneNameFromMLAPISceneIndex(uint sceneIndex) { var sceneName = string.Empty; if (!SceneIndexToString.TryGetValue(sceneIndex, out sceneName)) @@ -276,7 +280,8 @@ public SceneSwitchProgress UnloadScene(string sceneName) AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneUnload); - OnSceneSwitchStarted?.Invoke(sceneUnload); + + OnAdditiveSceneEvent?.Invoke(sceneUnload, sceneName, false); //Return our scene progress instance return switchSceneProgress; @@ -411,7 +416,14 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); // NSS TODO: Make a single unified notification callback - OnSceneSwitchStarted?.Invoke(sceneLoad); + if (loadSceneMode == LoadSceneMode.Single) + { + OnSceneSwitchStarted?.Invoke(sceneLoad); + } + else + { + OnAdditiveSceneEvent?.Invoke(sceneLoad, sceneName, true); + } } /// @@ -577,6 +589,7 @@ private void OnClientLoadedScene() OnSceneSwitched?.Invoke(); } + /// /// Server Side Only: /// This is used for late joining players and players that have just had their connection approved @@ -595,7 +608,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) } } - ClientSynchEventData.InitializeForSynch(); + ClientSynchEventData.InitializeForSynch(m_NetworkManager); ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC; diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 880f7d2983..83bd8ac395 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -36,6 +36,8 @@ public enum SceneEventTypes private Dictionary m_SceneNetworkObjectDataOffsets; internal PooledNetworkBuffer InternalBuffer; + private NetworkManager m_NetworkManager; + /// /// Client Side: /// Gets the next scene index to be loaded for approval and/or late joining @@ -64,8 +66,9 @@ public bool IsDoneWithSynchronization() /// Server Side: /// Called just before the synchronization process /// - public void InitializeForSynch() + public void InitializeForSynch(NetworkManager networkManager) { + m_NetworkManager = networkManager; if (m_SceneNetworkObjects == null) { m_SceneNetworkObjects = new Dictionary>(); @@ -111,6 +114,31 @@ public bool IsSceneEventClientSide() return false; } + /// + /// Sorts the NetworkObjects to assure proper order of operations for custom Network Prefab handlers + /// that implement the INetworkPrefabInstanceHandler interface. + /// + /// + /// + /// + private int SortNetworkObjects(NetworkObject first, NetworkObject second) + { + var doesFirstHaveHandler = m_NetworkManager.PrefabHandler.ContainsHandler(first); + var doesSecondHaveHandler = m_NetworkManager.PrefabHandler.ContainsHandler(second); + if (doesFirstHaveHandler != doesSecondHaveHandler) + { + if (doesFirstHaveHandler) + { + return 1; + } + else + { + return -1; + } + } + return 0; + } + /// /// Serializes this class instance /// @@ -144,6 +172,12 @@ private void OnWrite(NetworkWriter writer) // Size Place Holder (For offset purposes, needs to not be packed) writer.WriteUInt32(0); var totalBytes = 0; + + // Sort NetworkObjects so any NetworkObjects with a PrefabHandler are sorted to be after all other NetworkObjects + // This will assure the INetworkPrefabInstanceHandler instance is registered before we try to spawn the NetworkObjects + // on the client side. + keypair.Value.Sort(SortNetworkObjects); + foreach (var networkObject in keypair.Value) { var noStart = writer.GetStream().Position; diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity index af144e3986..9733430271 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity @@ -216,11 +216,12 @@ MonoBehaviour: m_EditorClassIdentifier: RandomMovement: 0 AutoSpawnEnable: 1 + SpawnInSourceScene: 1 InitialSpawnDelay: 0.2 SpawnsPerSecond: 1 PoolSize: 32 ObjectSpeed: 8 - EnableHandler: 0 + EnableHandler: 1 RegisterUsingNetworkObject: 0 ServerObjectToPool: {fileID: 1086419352113069865, guid: ed799d5402b325c4f8f41918224a1ba7, type: 3} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity index 1f5115e4f8..f8bbd5b603 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity @@ -246,11 +246,12 @@ MonoBehaviour: m_EditorClassIdentifier: RandomMovement: 0 AutoSpawnEnable: 1 + SpawnInSourceScene: 1 InitialSpawnDelay: 0.2 SpawnsPerSecond: 1 PoolSize: 32 ObjectSpeed: 8 - EnableHandler: 0 + EnableHandler: 1 RegisterUsingNetworkObject: 0 ServerObjectToPool: {fileID: 1086419352113069865, guid: 8a415e2c2210434458836779302bc4d3, type: 3} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 5575eddf79..9ded7e1a66 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -1645,7 +1645,7 @@ MonoBehaviour: SpawnsPerSecond: 1 PoolSize: 32 ObjectSpeed: 8 - EnableHandler: 0 + EnableHandler: 1 RegisterUsingNetworkObject: 0 ServerObjectToPool: {fileID: 771575417923360811, guid: c0a45bdb516f341498d933b7a7ed4fc1, type: 3} diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity index eb31a3a172..f1b2558852 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity @@ -1464,7 +1464,7 @@ MonoBehaviour: SpawnsPerSecond: 1 PoolSize: 32 ObjectSpeed: 8 - EnableHandler: 0 + EnableHandler: 1 RegisterUsingNetworkObject: 0 ServerObjectToPool: {fileID: 47844432802121000, guid: 894296ee6a442ef49b3637b680c7bdf7, type: 3} diff --git a/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity b/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity index 4a2d8dd9ae..966d5a17e1 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity @@ -1137,7 +1137,7 @@ MonoBehaviour: SpawnsPerSecond: 5 PoolSize: 128 ObjectSpeed: 8 - EnableHandler: 0 + EnableHandler: 1 RegisterUsingNetworkObject: 0 ServerObjectToPool: {fileID: 771575417923360811, guid: c0a45bdb516f341498d933b7a7ed4fc1, type: 3} diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index f8d839f64c..87e87e28fc 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -197,13 +197,27 @@ public void InitializeObjectPool() m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(ServerObjectToPool); } // If we are a client and we are using the custom prefab override handler, then we need to use that for our pool - else if (IsClient && EnableHandler) + // This also checks to see if the ClientObjectToPool is set, if not then we are just using the custom prefab override handler + // to assure the client-side uses the NetworkObject pool as opposed to always spawning and destroying NetworkObjects. + else if (IsClient && EnableHandler && ClientObjectToPool != null) { m_ObjectToSpawn = ClientObjectToPool; } + // If we are enabling the handler, then we can control which NetworkObject will be used for spawning. + // If we are the server but do not have a handler, then we use a less efficient server-side only pool (clients will instantiate and destroy on their side) if (EnableHandler || IsServer) { + // In order to account for any NetworkPrefab override defined within the NetworkManager, we do one last check to assure we are creating a pool + // of the right NetworkPrefab objects, otherwise GetNetworkPrefabOverride will return back the same m_ObjectToSpawn + // NOTE: We filter out the case where we are a server, as the server will send the original NetworkPrefab GlobalObjectIdHash. + // If we enable this for dedicated server, then the server would spawn the override prefab which will cause the client to create a pool that is + // never used and the client(s) will spawn and destroy GameObjects outside of the pool. + if (EnableHandler && IsClient) + { + m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); + } + m_ObjectPool = new List(PoolSize); for (int i = 0; i < PoolSize; i++) @@ -250,7 +264,7 @@ private GameObject AddNewInstance() } /// - /// Starts the + /// Starts spawning /// private void StartSpawningBoxes() { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 8176f03564..e55d29e7a4 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; -using UnityEngine.UI; +using UnityEngine.SceneManagement; using MLAPI; using MLAPI.Spawning; @@ -12,6 +12,7 @@ public class NetworkPrefabPoolAdditive : NetworkBehaviour [Header("General Settings")] public bool RandomMovement = true; public bool AutoSpawnEnable = true; + public bool SpawnInSourceScene = true; public float InitialSpawnDelay; public int SpawnsPerSecond; public int PoolSize; @@ -99,6 +100,7 @@ private void OnDestroy() if (NetworkManager != null && NetworkManager.SceneManager != null) { NetworkManager.SceneManager.OnSceneSwitchStarted -= SceneManager_OnSceneSwitchStarted; + NetworkManager.SceneManager.OnAdditiveSceneEvent -= OnAdditiveSceneEvent; } } @@ -107,21 +109,40 @@ private void Start() { SpawnsPerSecond = 3; NetworkManager.SceneManager.OnSceneSwitchStarted += SceneManager_OnSceneSwitchStarted; + NetworkManager.SceneManager.OnAdditiveSceneEvent += OnAdditiveSceneEvent; //Call this again in case we didn't have access to the NetworkManager already (i.e. first scene loaded) RegisterCustomPrefabHandler(); } + /// + /// 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. + /// + /// + /// + private void OnAdditiveSceneEvent(AsyncOperation operation, string sceneName, bool isLoading) + { + if(!isLoading && gameObject.scene.name == sceneName && SpawnInSourceScene) + { + OnUnloadScene(); + } + } + + /// + /// For additive scenes, we always clear out our pooled NetworkObjects + /// + /// private void SceneManager_OnSceneSwitchStarted(AsyncOperation operation) { - //OnSceneSwitchBegin(); + OnUnloadScene(); } /// /// Detect when we are switching scenes in order /// to assure we stop spawning objects /// - private void OnSceneSwitchBegin() + private void OnUnloadScene() { if (IsServer) { @@ -177,13 +198,27 @@ public void InitializeObjectPool() m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); } // If we are a client and we are using the custom prefab override handler, then we need to use that for our pool - else if (IsClient && EnableHandler) + // This also checks to see if the ClientObjectToPool is set, if not then we are just using the custom prefab override handler + // to assure the client-side uses the NetworkObject pool as opposed to always spawning and destroying NetworkObjects. + else if (IsClient && EnableHandler && ClientObjectToPool != null) { m_ObjectToSpawn = ClientObjectToPool; } + // If we are enabling the handler, then we can control which NetworkObject will be used for spawning. + // If we are the server but do not have a handler, then we use a less efficient server-side only pool (clients will instantiate and destroy on their side) if (EnableHandler || IsServer) { + // In order to account for any NetworkPrefab override defined within the NetworkManager, we do one last check to assure we are creating a pool + // of the right NetworkPrefab objects, otherwise GetNetworkPrefabOverride will return back the same m_ObjectToSpawn + // NOTE: We filter out the case where we are a server, as the server will send the original NetworkPrefab GlobalObjectIdHash. + // If we enable this for dedicated server, then the server would spawn the override prefab which will cause the client to create a pool that is + // never used and the client(s) will spawn and destroy GameObjects outside of the pool. + if (EnableHandler && IsClient) + { + m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); + } + m_ObjectPool = new List(PoolSize); for (int i = 0; i < PoolSize; i++) @@ -229,6 +264,12 @@ private GameObject AddNewInstance() { genericBehaviour.ShouldMoveRandomly(RandomMovement); } + + if(SpawnInSourceScene && gameObject.scene != null) + { + SceneManager.MoveGameObjectToScene(obj, gameObject.scene); + } + obj.SetActive(false); @@ -237,7 +278,7 @@ private GameObject AddNewInstance() } /// - /// Starts the + /// Starts spawning /// private void StartSpawningBoxes() { From df9bc79286977e56c04890d80bf0c8bec913e72b Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 11 Jul 2021 21:24:49 -0500 Subject: [PATCH 018/106] feat and refactor Some temporary fixes for message ordering and buffering during late-join until this gets merged with the message ordering updates and we add the ability to buffer messages while a player is late-joining. Primarily in the GenericNetworkObjectBehaviour class. Added a proposed solution to handle order of operations if user decides to allow all NetworkObjects from a prefab pool to be spawned in the active scene. This requires the ability to separate NetworkObjects (server side) by associated scene so that they will be synchronized properly with their spawn generator's pool. Now tracking which additive scenes have been loaded to be assured they are unloaded when doing a full scene switch (i.e. singlemode loading), otherwise there can be issues that arise with currently loaded additive scenes. --- .../Runtime/Core/NetworkObject.cs | 14 +++ .../SceneManagement/NetworkSceneManager.cs | 76 +++++++++-- .../AdditiveScene1.unity | 2 +- .../AdditiveScene2.unity | 2 +- .../AdditiveSceneToggleHandler.cs | 15 ++- .../Scripts/GenericNetworkObjectBehaviour.cs | 35 +++++- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 117 +++++++++++------ .../Scripts/NetworkPrefabPoolAdditive.cs | 118 ++++++++++++------ 8 files changed, 285 insertions(+), 94 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs index 180f9c979c..1710c91100 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs @@ -10,6 +10,7 @@ using MLAPI.Transports; using MLAPI.Serialization; using UnityEngine; +using UnityEngine.SceneManagement; namespace MLAPI { @@ -178,6 +179,19 @@ internal set /// public bool DontDestroyWithOwner; + internal Scene SourceAdditiveScene; + + /// + /// Proposed Solution for custom Network Prefab Handlers and client late-join synchronization + /// This will provide the users with a way to associate a NetworkObject with a "source scene" in order + /// to make sure the NetworkObjects are synchronized when the additive source scene is loaded and not before + /// + /// + public void SetSourceAdditiveScene(Scene scene) + { + SourceAdditiveScene = scene; + } + /// /// Whether or not to enable automatic NetworkObject parent synchronization. /// diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 5a81474301..4ae72cf483 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -280,7 +280,10 @@ public SceneSwitchProgress UnloadScene(string sceneName) AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneUnload); - + if(m_AdditiveScenesLoaded.Contains(sceneName)) + { + m_AdditiveScenesLoaded.Remove(sceneName); + } OnAdditiveSceneEvent?.Invoke(sceneUnload, sceneName, false); //Return our scene progress instance @@ -300,12 +303,16 @@ private void OnClientUnloadScene() } s_IsSceneEventActive = true; - var sceneLoad = SceneManager.UnloadSceneAsync(sceneName); + var sceneUnload = SceneManager.UnloadSceneAsync(sceneName); + + if (m_AdditiveScenesLoaded.Contains(sceneName)) + { + m_AdditiveScenesLoaded.Remove(sceneName); + } - sceneLoad.completed += asyncOp2 => OnSceneUnloaded(); + sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); - // NSS TODO: Create the unloaded scene notification - //OnSceneSwitchStarted?.Invoke(sceneLoad); + OnAdditiveSceneEvent?.Invoke(sceneUnload, sceneName, false); } /// @@ -324,6 +331,9 @@ private void OnSceneUnloaded() s_IsSceneEventActive = false; } + + private List m_AdditiveScenesLoaded = new List(); + /// /// Additively loads the scene /// @@ -418,10 +428,25 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene // NSS TODO: Make a single unified notification callback if (loadSceneMode == LoadSceneMode.Single) { + foreach (var additiveSceneName in m_AdditiveScenesLoaded) + { + SceneManager.UnloadSceneAsync(additiveSceneName); + } + m_AdditiveScenesLoaded.Clear(); + OnSceneSwitchStarted?.Invoke(sceneLoad); } else { + if(!m_AdditiveScenesLoaded.Contains(sceneName)) + { + m_AdditiveScenesLoaded.Add(sceneName); + } + else + { + throw new Exception($"{sceneName} is being loaded twice?!"); + } + OnAdditiveSceneEvent?.Invoke(sceneLoad, sceneName, true); } } @@ -444,11 +469,29 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) return; } + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { + foreach(var additiveSceneName in m_AdditiveScenesLoaded) + { + SceneManager.UnloadSceneAsync(additiveSceneName); + } + m_AdditiveScenesLoaded.Clear(); + // Move ALL NetworkObjects to the temp scene MoveObjectsToDontDestroyOnLoad(); } + else + { + if (!m_AdditiveScenesLoaded.Contains(sceneName)) + { + m_AdditiveScenesLoaded.Add(sceneName); + } + else + { + throw new Exception($"{sceneName} is being loaded twice?!"); + } + } // NSS TODO: This either needs a better name or there has to be a better way of detecting this condition // The Condition: While a scene is asynchronously loaded, if any new NetworkObjects are spawned they need to be moved into the do not destroy temporary scene @@ -459,7 +502,15 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) var sceneLoad = SceneManager.LoadSceneAsync(sceneName, SceneEventData.LoadSceneMode); sceneLoad.completed += asyncOp2 => OnSceneLoaded(sceneName); - OnSceneSwitchStarted?.Invoke(sceneLoad); + + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + { + OnSceneSwitchStarted?.Invoke(sceneLoad); + } + else + { + OnAdditiveSceneEvent?.Invoke(sceneLoad, sceneName, true); + } } /// @@ -628,7 +679,11 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) { if (networkObject.gameObject.scene != scene) { - continue; + // If it is not associated with an additive scene or if the additive scene is not the current scene we are processing then continue + if (networkObject.SourceAdditiveScene == null || networkObject.SourceAdditiveScene != scene) + { + continue; + } } ClientSynchEventData.AddNetworkObjectForSynch(malpiSceneIndex, networkObject); } @@ -904,10 +959,9 @@ public void HandleSceneEvent(ulong clientId, Stream stream) { if (stream != null) { - using (var reader = NetworkReaderPool.GetReader(stream)) - { - SceneEventData = (SceneEventData)reader.ReadObjectPacked(typeof(SceneEventData)); - } + var reader = NetworkReaderPool.GetReader(stream); + SceneEventData = (SceneEventData)reader.ReadObjectPacked(typeof(SceneEventData)); + NetworkReaderPool.PutBackInPool(reader); if (SceneEventData.IsSceneEventClientSide()) { diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity index 9733430271..9e1a37e424 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity @@ -216,7 +216,7 @@ MonoBehaviour: m_EditorClassIdentifier: RandomMovement: 0 AutoSpawnEnable: 1 - SpawnInSourceScene: 1 + SpawnInSourceScene: 0 InitialSpawnDelay: 0.2 SpawnsPerSecond: 1 PoolSize: 32 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity index f8bbd5b603..20858cb73a 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity @@ -246,7 +246,7 @@ MonoBehaviour: m_EditorClassIdentifier: RandomMovement: 0 AutoSpawnEnable: 1 - SpawnInSourceScene: 1 + SpawnInSourceScene: 0 InitialSpawnDelay: 0.2 SpawnsPerSecond: 1 PoolSize: 32 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index e88b4194c5..642ba284d0 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -42,13 +42,26 @@ private void Start() { m_ToggleObject = gameObject.GetComponentInChildren(); StartCoroutine(CheckForVisibility()); + + //NetworkManager.SceneManager.OnSceneSwitchStarted += SceneManager_OnSceneSwitchStarted; } + //private void SceneManager_OnSceneSwitchStarted(AsyncOperation operation) + //{ + // if (m_ToggleObject) + // { + // if (m_ToggleObject.isOn) + // { + // m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); + // } + // } + //} + private bool m_ExitingScene; private void OnDestroy() { m_ExitingScene = true; - StopAllCoroutines(); + StopCoroutine(CheckForVisibility()); } private IEnumerator CheckForVisibility() diff --git a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs index 71e021dd9a..d480cc9f0f 100644 --- a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs +++ b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs @@ -72,6 +72,34 @@ private void FixedUpdate() } } + public bool IsRegisteredPoolObject; + + public bool IsRemovedFromPool; + + public bool DetectedMissedDespawn; + + public float TimeToWaitUntilDestroy; + + private void LateUpdate() + { + if (!IsOwner && !NetworkObject.IsSpawned && !IsRegisteredPoolObject && !IsRemovedFromPool) + { + if (!DetectedMissedDespawn) + { + DetectedMissedDespawn = true; + TimeToWaitUntilDestroy = Time.realtimeSinceStartup + 1.0f; + } + else if (TimeToWaitUntilDestroy < Time.realtimeSinceStartup) + { + Destroy(gameObject); + } + } + else if (!IsOwner && IsRegisteredPoolObject && DetectedMissedDespawn) + { + DetectedMissedDespawn = false; + } + } + private void OnTriggerEnter(Collider other) { @@ -83,8 +111,11 @@ private void OnTriggerEnter(Collider other) } else { - NetworkObject.Despawn(); - NetworkObject.gameObject.SetActive(false); + NetworkObject.Despawn(IsRemovedFromPool); + if (!IsRemovedFromPool) + { + NetworkObject.gameObject.SetActive(false); + } } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 87e87e28fc..791080bf65 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -56,6 +56,7 @@ private void OnEnable() //This registers early under the condition of a scene transition RegisterCustomPrefabHandler(); + } /// @@ -85,39 +86,76 @@ private void RegisterCustomPrefabHandler() } } - /// - /// When disabled, stop spawning objects - /// + private void DeRegisterCustomPrefabHandler() + { + // Register the custom spawn handler? + if (EnableHandler && NetworkManager && NetworkManager.PrefabHandler != null && m_MyCustomPrefabSpawnHandler != null) + { + NetworkManager.PrefabHandler.RemoveHandler(ServerObjectToPool); + if (IsClient && m_ObjectToSpawn != null) + { + NetworkManager.PrefabHandler.RemoveHandler(m_ObjectToSpawn); + } + } + } + private void OnDisable() { - m_IsSpawningObjects = false; - if (NetworkManager.Singleton && EnableHandler && m_MyCustomPrefabSpawnHandler != null) + if (NetworkManager != null && NetworkManager.SceneManager != null) + { + NetworkManager.SceneManager.OnSceneSwitchStarted -= OnSceneSwitchStarted; + } + if (!IsServer) { - var no = ServerObjectToPool.GetComponent(); - NetworkManager.Singleton.PrefabHandler.RemoveHandler(no); - m_MyCustomPrefabSpawnHandler = null; + StopCoroutine(SpawnObjects()); + DeRegisterCustomPrefabHandler(); + CleanNetworkObjects(); } } /// /// General clean up - /// The custom prefab handler is de-registered here + /// The custom prefab handler is unregistered here /// private void OnDestroy() { - if (SwitchScene) - { - SwitchScene.OnSceneSwitchBegin -= OnSceneSwitchBegin; - SwitchScene.OnSceneSwitchCompleted -= SwitchScene_OnSceneSwitchCompleted; - } } - private void SwitchScene_OnSceneSwitchCompleted() + private void CleanNetworkObjects() { - InitializeObjectPool(); + if (m_ObjectPool != null) + { + foreach (var obj in m_ObjectPool) + { + var networkObject = obj.GetComponent(); + var genericBehaviour = obj.GetComponent(); + genericBehaviour.IsRegisteredPoolObject = false; + genericBehaviour.IsRemovedFromPool = true; + if (IsServer) + { + if (networkObject.IsSpawned) + { + networkObject.Despawn(true); + } + else + { + DestroyImmediate(obj); + } + } + else //Client + { + if (!networkObject.IsSpawned) + { + DestroyImmediate(obj); + } + } + } + m_ObjectPool.Clear(); + } } + // Start is called before the first frame update private void Start() { @@ -126,10 +164,9 @@ private void Start() SpawnSliderValueText.text = SpawnsPerSecond.ToString(); } - if (SwitchScene) + if (NetworkManager.SceneManager != null && IsServer) { - SwitchScene.OnSceneSwitchBegin += OnSceneSwitchBegin; - SwitchScene.OnSceneSwitchCompleted += SwitchScene_OnSceneSwitchCompleted; + NetworkManager.SceneManager.OnSceneSwitchStarted += OnSceneSwitchStarted; } //Call this again in case we didn't have access to the NetworkManager already (i.e. first scene loaded) @@ -141,25 +178,13 @@ private void Start() /// Detect when we are switching scenes in order /// to assure we stop spawning objects /// - private void OnSceneSwitchBegin() + private void OnSceneSwitchStarted(AsyncOperation operation) { if (IsServer) { StopCoroutine(SpawnObjects()); - - if (m_ObjectPool != null) - { - foreach (var obj in m_ObjectPool) - { - var networkObject = obj.GetComponent(); - if (networkObject.IsSpawned) - { - networkObject.Despawn(); - } - Destroy(obj); - } - m_ObjectPool.Clear(); - } + DeRegisterCustomPrefabHandler(); + CleanNetworkObjects(); } } @@ -180,6 +205,10 @@ public override void OnNetworkSpawn() UpdateSpawnsPerSecond(); } } + else + { + NetworkManager.SceneManager.OnSceneSwitchStarted += OnSceneSwitchStarted; + } } /// @@ -194,7 +223,7 @@ public void InitializeObjectPool() // If we are a host, then we have to get the NetworkPrefab Override (if one exists) if (IsHost && !EnableHandler) { - m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(ServerObjectToPool); + m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); } // If we are a client and we are using the custom prefab override handler, then we need to use that for our pool // This also checks to see if the ClientObjectToPool is set, if not then we are just using the custom prefab override handler @@ -216,6 +245,7 @@ public void InitializeObjectPool() if (EnableHandler && IsClient) { m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); + NetworkManager.PrefabHandler.AddHandler(m_ObjectToSpawn, m_MyCustomPrefabSpawnHandler); } m_ObjectPool = new List(PoolSize); @@ -258,6 +288,11 @@ private GameObject AddNewInstance() { var obj = Instantiate(m_ObjectToSpawn); var no = obj.GetComponent(); + var genericBehaviour = obj.GetComponent(); + if (genericBehaviour) + { + genericBehaviour.IsRegisteredPoolObject = true; + } obj.SetActive(false); m_ObjectPool.Add(obj); return obj; @@ -382,8 +417,16 @@ public NetworkObject HandleNetworkPrefabSpawn(ulong ownerClientId, Vector3 posit } public void HandleNetworkPrefabDestroy(NetworkObject networkObject) { - networkObject.transform.position = Vector3.zero; - networkObject.gameObject.SetActive(false); + var genericBehaviour = networkObject.gameObject.GetComponent(); + if (genericBehaviour.IsRegisteredPoolObject) + { + networkObject.transform.position = Vector3.zero; + networkObject.gameObject.SetActive(false); + } + else if (genericBehaviour.IsRemovedFromPool) + { + Object.DestroyImmediate(networkObject.gameObject); + } } public MyCustomPrefabSpawnHandler(NetworkPrefabPool objectPool) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index e55d29e7a4..d3358f540d 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -70,24 +70,19 @@ private void RegisterCustomPrefabHandler() NetworkManager.PrefabHandler.AddHandler(ServerObjectToPool, m_AdditiveCustomPrefabSpawnHandler); } } - else if (!IsServer) - { - Debug.LogWarning($"Failed to register custom spawn handler and {nameof(EnableHandler)} is set to true!"); - } } } - /// - /// When disabled, stop spawning objects - /// - private void OnDisable() + private void DeRegisterCustomPrefabHandler() { - m_IsSpawningObjects = false; - if (NetworkManager.Singleton && EnableHandler && m_AdditiveCustomPrefabSpawnHandler != null) + // Register the custom spawn handler? + if (EnableHandler && NetworkManager && NetworkManager.PrefabHandler != null && m_AdditiveCustomPrefabSpawnHandler != null) { - var no = ServerObjectToPool.GetComponent(); - NetworkManager.Singleton.PrefabHandler.RemoveHandler(no); - m_AdditiveCustomPrefabSpawnHandler = null; + NetworkManager.PrefabHandler.RemoveHandler(ServerObjectToPool); + if (IsClient && m_ObjectToSpawn != null) + { + NetworkManager.PrefabHandler.RemoveHandler(m_ObjectToSpawn); + } } } @@ -97,9 +92,16 @@ private void OnDisable() /// private void OnDestroy() { + if (IsServer) + { + StopCoroutine(SpawnObjects()); + } + DeRegisterCustomPrefabHandler(); + + + if (NetworkManager != null && NetworkManager.SceneManager != null) { - NetworkManager.SceneManager.OnSceneSwitchStarted -= SceneManager_OnSceneSwitchStarted; NetworkManager.SceneManager.OnAdditiveSceneEvent -= OnAdditiveSceneEvent; } } @@ -108,7 +110,6 @@ private void OnDestroy() private void Start() { SpawnsPerSecond = 3; - NetworkManager.SceneManager.OnSceneSwitchStarted += SceneManager_OnSceneSwitchStarted; NetworkManager.SceneManager.OnAdditiveSceneEvent += OnAdditiveSceneEvent; //Call this again in case we didn't have access to the NetworkManager already (i.e. first scene loaded) RegisterCustomPrefabHandler(); @@ -123,19 +124,53 @@ private void Start() /// private void OnAdditiveSceneEvent(AsyncOperation operation, string sceneName, bool isLoading) { - if(!isLoading && gameObject.scene.name == sceneName && SpawnInSourceScene) + if (!isLoading && gameObject.scene.name == sceneName) { OnUnloadScene(); } } - /// - /// For additive scenes, we always clear out our pooled NetworkObjects - /// - /// - private void SceneManager_OnSceneSwitchStarted(AsyncOperation operation) + private void CleanNetworkObjects() { - OnUnloadScene(); + if (m_ObjectPool != null) + { + foreach (var obj in m_ObjectPool) + { + var networkObject = obj.GetComponent(); + var genericBehaviour = obj.GetComponent(); + genericBehaviour.IsRegisteredPoolObject = false; + genericBehaviour.IsRemovedFromPool = true; + if (IsServer) + { + if (SpawnInSourceScene) + { + if (networkObject.IsSpawned) + { + networkObject.Despawn(true); + } + else + { + DestroyImmediate(obj); + } + } + else + { + if (!networkObject.IsSpawned) + { + DestroyImmediate(obj); + } + } + } + else //Client + { + if (!networkObject.IsSpawned) + { + DestroyImmediate(obj); + } + } + } + m_ObjectPool.Clear(); + } } /// @@ -147,21 +182,11 @@ private void OnUnloadScene() if (IsServer) { StopCoroutine(SpawnObjects()); - - if (m_ObjectPool != null) - { - foreach (var obj in m_ObjectPool) - { - var networkObject = obj.GetComponent(); - if (networkObject.IsSpawned) - { - networkObject.Despawn(); - } - Destroy(obj); - } - m_ObjectPool.Clear(); - } } + // De-register the custom prefab handler + DeRegisterCustomPrefabHandler(); + + CleanNetworkObjects(); } /// @@ -217,6 +242,7 @@ public void InitializeObjectPool() if (EnableHandler && IsClient) { m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); + NetworkManager.PrefabHandler.AddHandler(m_ObjectToSpawn, m_AdditiveCustomPrefabSpawnHandler); } m_ObjectPool = new List(PoolSize); @@ -260,12 +286,14 @@ private GameObject AddNewInstance() var obj = Instantiate(m_ObjectToSpawn); var no = obj.GetComponent(); var genericBehaviour = obj.GetComponent(); - if(genericBehaviour) + if (genericBehaviour) { genericBehaviour.ShouldMoveRandomly(RandomMovement); + genericBehaviour.IsRegisteredPoolObject = true; } - if(SpawnInSourceScene && gameObject.scene != null) + // Example of how to keep your pooled NetworkObjects in the same scene as your spawn generator (additive scenes only) + if (SpawnInSourceScene && gameObject.scene != null) { SceneManager.MoveGameObjectToScene(obj, gameObject.scene); } @@ -294,7 +322,7 @@ private void StartSpawningBoxes() public void UpdateSpawnsPerSecond() { // Handle case where the initial value is set to zero and so coroutine needs to be started - if(SpawnsPerSecond > 0 && !m_IsSpawningObjects) + if (SpawnsPerSecond > 0 && !m_IsSpawningObjects) { StartSpawningBoxes(); } @@ -386,8 +414,16 @@ public NetworkObject HandleNetworkPrefabSpawn(ulong ownerClientId, Vector3 posit } public void HandleNetworkPrefabDestroy(NetworkObject networkObject) { - networkObject.transform.position = Vector3.zero; - networkObject.gameObject.SetActive(false); + var genericBehaviour = networkObject.gameObject.GetComponent(); + if (genericBehaviour.IsRegisteredPoolObject) + { + networkObject.transform.position = Vector3.zero; + networkObject.gameObject.SetActive(false); + } + else + { + Object.DestroyImmediate(networkObject.gameObject); + } } public MyAdditiveCustomPrefabSpawnHandler(NetworkPrefabPoolAdditive objectPool) From 5d10e276e8e6ef54e87876d6df9494d09c968fa2 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 12 Jul 2021 09:21:19 -0500 Subject: [PATCH 019/106] refactor Minor checks for an object still being around during clean up. --- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 791080bf65..3c8d33ec7e 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -105,12 +105,10 @@ private void OnDisable() { NetworkManager.SceneManager.OnSceneSwitchStarted -= OnSceneSwitchStarted; } - if (!IsServer) - { - StopCoroutine(SpawnObjects()); - DeRegisterCustomPrefabHandler(); - CleanNetworkObjects(); - } + + StopCoroutine(SpawnObjects()); + DeRegisterCustomPrefabHandler(); + CleanNetworkObjects(); } /// @@ -128,26 +126,32 @@ private void CleanNetworkObjects() { foreach (var obj in m_ObjectPool) { - var networkObject = obj.GetComponent(); - var genericBehaviour = obj.GetComponent(); - genericBehaviour.IsRegisteredPoolObject = false; - genericBehaviour.IsRemovedFromPool = true; - if (IsServer) - { - if (networkObject.IsSpawned) - { - networkObject.Despawn(true); - } - else - { - DestroyImmediate(obj); - } - } - else //Client + if (obj != null) { - if (!networkObject.IsSpawned) + var networkObject = obj.GetComponent(); + var genericBehaviour = obj.GetComponent(); + if (genericBehaviour != null) { - DestroyImmediate(obj); + genericBehaviour.IsRegisteredPoolObject = false; + genericBehaviour.IsRemovedFromPool = true; + if (IsServer) + { + if (networkObject.IsSpawned) + { + networkObject.Despawn(true); + } + else + { + DestroyImmediate(obj); + } + } + else //Client + { + if (!networkObject.IsSpawned) + { + DestroyImmediate(obj); + } + } } } } @@ -182,9 +186,7 @@ private void OnSceneSwitchStarted(AsyncOperation operation) { if (IsServer) { - StopCoroutine(SpawnObjects()); - DeRegisterCustomPrefabHandler(); - CleanNetworkObjects(); + } } From 729a2ce09aef653fdc5371f9437333b796d62a72 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 12 Jul 2021 12:02:53 -0500 Subject: [PATCH 020/106] fix Fixes the basic scene transitioning test failure. --- .../SceneManagement/NetworkSceneManager.cs | 25 ++++++++++--------- .../Assets/Tests/Runtime/SceneLoadingTest.cs | 17 ++++++++++++- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 4ae72cf483..1ac100594e 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -88,6 +88,7 @@ public class NetworkSceneManager private static bool s_IsSceneEventActive = false; internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + internal static bool IsRunningUnitTest = false; //Client and Server: used for all scene event processing exception for client synchronization internal SceneEventData SceneEventData; @@ -374,7 +375,7 @@ public SceneSwitchProgress LoadScene(string sceneName) public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) { // NSS TODO: Remove this once the LoadScene method is completed and all areas in the code that use the loadSceneMode parameter are updated - if (loadSceneMode == LoadSceneMode.Additive) + if (loadSceneMode == LoadSceneMode.Additive && !IsRunningUnitTest) { return LoadScene(sceneName); } @@ -397,8 +398,7 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; - // NSS TODO: remove this completely once done with the transition? - SceneEventData.LoadSceneMode = LoadSceneMode.Single; + SceneEventData.LoadSceneMode = IsRunningUnitTest ? loadSceneMode : LoadSceneMode.Single; // Destroy current scene objects before switching. m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects(); @@ -407,7 +407,7 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene MoveObjectsToDontDestroyOnLoad(); // Begin the scene event - OnBeginSceneEvent(sceneName, switchSceneProgress, LoadSceneMode.Single); + OnBeginSceneEvent(sceneName, switchSceneProgress, SceneEventData.LoadSceneMode); //Return our scene progress instance return switchSceneProgress; @@ -425,15 +425,17 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); - // NSS TODO: Make a single unified notification callback - if (loadSceneMode == LoadSceneMode.Single) + + + + if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH) { foreach (var additiveSceneName in m_AdditiveScenesLoaded) { SceneManager.UnloadSceneAsync(additiveSceneName); } m_AdditiveScenesLoaded.Clear(); - + // NSS TODO: Make a single unified notification callback OnSceneSwitchStarted?.Invoke(sceneLoad); } else @@ -446,7 +448,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene { throw new Exception($"{sceneName} is being loaded twice?!"); } - + // NSS TODO: Make a single unified notification callback OnAdditiveSceneEvent?.Invoke(sceneLoad, sceneName, true); } } @@ -519,7 +521,7 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) private void OnSceneLoaded(string sceneName) { var nextScene = SceneManager.GetSceneByName(sceneName); - if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH) { SceneManager.SetActiveScene(nextScene); } @@ -527,7 +529,7 @@ private void OnSceneLoaded(string sceneName) //Get all NetworkObjects loaded by the scene PopulateScenePlacedObjects(nextScene); - if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH) { // Move all objects to the new scene MoveObjectsToScene(nextScene); @@ -602,13 +604,12 @@ private void OnServerLoadedScene() } // Tell server that scene load is completed - if (m_NetworkManager.IsHost) + if (m_NetworkManager.IsServer) { OnClientSceneLoadingEventCompleted(m_NetworkManager.LocalClientId, SceneEventData.SwitchSceneGuid); } s_IsSceneEventActive = false; - OnSceneSwitched?.Invoke(); } diff --git a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs index 589898cb1a..fceae52ea4 100644 --- a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs +++ b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs @@ -31,6 +31,7 @@ public IEnumerator SceneLoading() // Load the first scene with the predefined NetworkManager m_TargetSceneNameToLoad = "SceneTransitioningTest"; + SceneManager.sceneLoaded += SceneManager_sceneLoaded; SceneManager.LoadScene(m_TargetSceneNameToLoad, LoadSceneMode.Additive); @@ -100,7 +101,7 @@ public IEnumerator SceneLoading() m_SceneLoaded = false; // Wait for the scene to load - timeOut = Time.realtimeSinceStartup + 5; + timeOut = Time.realtimeSinceStartup + 30; m_TimedOut = false; while (!m_SceneLoaded) { @@ -209,5 +210,19 @@ private void SceneManager_sceneLoaded(Scene scene, LoadSceneMode sceneMode) m_LoadedScene = scene; } } + + [UnitySetUp] + private IEnumerator SetUp() + { + MLAPI.SceneManagement.NetworkSceneManager.IsRunningUnitTest = true; + yield return null; + } + + [UnityTearDown] + private IEnumerator TearDown() + { + MLAPI.SceneManagement.NetworkSceneManager.IsRunningUnitTest = false; + yield return null; + } } } From 35a46b746ce85aea925148717ec2681eb6283ed6 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 12 Jul 2021 13:15:59 -0500 Subject: [PATCH 021/106] fix Fixing other unit test issues. Removing the temporary block for clients to receive messages (it was an artifact from testing). Still have a few tests that are failing. --- .../Runtime/Connection/NetworkClient.cs | 1 - .../Runtime/Core/NetworkManager.cs | 4 -- .../SceneManagement/NetworkSceneManager.cs | 46 ++++++++----------- .../Runtime/SceneManagement/SceneEventData.cs | 14 +++--- .../Runtime/Spawning/NetworkSpawnManager.cs | 5 -- 5 files changed, 24 insertions(+), 46 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs b/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs index 18fa0249fc..8a7723e390 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Connection/NetworkClient.cs @@ -7,7 +7,6 @@ namespace MLAPI.Connection /// public class NetworkClient { - public bool IsReadyToReceiveMessages; /// /// The ClientId of the NetworkClient /// diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 9e0c2dd86a..10de5a26fa 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -1698,10 +1698,6 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? SceneManager.SynchronizeNetworkObjects(ownerClientId); } } - else - { - ConnectedClients[ownerClientId].IsReadyToReceiveMessages = true; - } OnClientConnectedCallback?.Invoke(ownerClientId); diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 1ac100594e..c2bb63dc26 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -386,14 +386,6 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene return null; } - foreach (var client in m_NetworkManager.ConnectedClientsList) - { - if (client.ClientId != m_NetworkManager.ServerClientId) - { - client.IsReadyToReceiveMessages = false; - } - } - SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; @@ -762,23 +754,7 @@ private void ClientLoadedSynchronization(uint sceneIndex) } // Check to see if we still have scenes to load and synchronize with - if (!SceneEventData.IsDoneWithSynchronization()) - { - HandleClientSceneEvent(null); - } - else - { - // All scenes are synchronized, let the server know we are done synchronizing - m_NetworkManager.IsConnectedClient = true; - m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId); - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) - { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC_COMPLETE; - writer.WriteObjectPacked(SceneEventData); - m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); - } - } + HandleClientSceneEvent(null); } internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; @@ -898,7 +874,23 @@ private void HandleClientSceneEvent(Stream stream) } case SceneEventData.SceneEventTypes.SYNC: { - OnClientBeginSynch(SceneEventData.GetNextSceneSynchronizationIndex()); + if (!SceneEventData.IsDoneWithSynchronization()) + { + OnClientBeginSynch(SceneEventData.GetNextSceneSynchronizationIndex()); + } + else + { + // All scenes are synchronized, let the server know we are done synchronizing + m_NetworkManager.IsConnectedClient = true; + m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId); + using (var buffer = PooledNetworkBuffer.Get()) + using (var writer = PooledNetworkWriter.Get(buffer)) + { + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC_COMPLETE; + writer.WriteObjectPacked(SceneEventData); + m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + } + } break; } default: @@ -921,7 +913,6 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) case SceneEventData.SceneEventTypes.SWITCH_COMPLETE: { OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); - m_NetworkManager.ConnectedClients[clientId].IsReadyToReceiveMessages = true; break; } case SceneEventData.SceneEventTypes.LOAD_COMPLETE: @@ -936,7 +927,6 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } case SceneEventData.SceneEventTypes.SYNC_COMPLETE: { - m_NetworkManager.ConnectedClients[clientId].IsReadyToReceiveMessages = true; // NSS TOOD: The scene event local notification needs to be called //m_NetworkManager.NotifyPlayerConnected(clientId); break; diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 83bd8ac395..955e73e417 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -242,16 +242,14 @@ private void OnRead(NetworkReader reader) { var keyPairCount = reader.ReadInt32Packed(); + if (m_SceneNetworkObjectDataOffsets == null) + { + m_SceneNetworkObjectDataOffsets = new Dictionary(); + } + if (keyPairCount > 0) { - if (m_SceneNetworkObjectDataOffsets == null) - { - m_SceneNetworkObjectDataOffsets = new Dictionary(); - } - else - { - m_SceneNetworkObjectDataOffsets.Clear(); - } + m_SceneNetworkObjectDataOffsets.Clear(); InternalBuffer.Position = 0; diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs index 0f18031ba1..871556170b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs @@ -362,11 +362,6 @@ internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject return; } - if (!NetworkManager.ConnectedClients[clientId].IsReadyToReceiveMessages) - { - return; - } - var rpcQueueContainer = NetworkManager.RpcQueueContainer; var buffer = PooledNetworkBuffer.Get(); From a5c3bf845c4be027869c870361a9b03b672f7c91 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 12 Jul 2021 15:41:11 -0500 Subject: [PATCH 022/106] fix Two issues were discovered while getting the parenting test working: 1.) t was doing a move objects to scene during the client side synchronization process (which doesn't need to happen) and as such was setting all parents to null which was throwing an exception. 2.) The server side wasn't filtering out any pre-loaded scenes during the synchronization process. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index c2bb63dc26..ff9c7cc413 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -660,8 +660,13 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) for (int i = 0; i < SceneManager.sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); - uint malpiSceneIndex = GetMLAPISceneIndexFromScene(scene); + var malpiSceneIndex = GetMLAPISceneIndexFromScene(scene); + + if( malpiSceneIndex == uint.MaxValue) + { + continue; + } // This would depend upon whether we are additive or note if (activeScene == scene) { @@ -747,12 +752,6 @@ private void ClientLoadedSynchronization(uint sceneIndex) // Synchronize the NetworkObjects for this scene SceneEventData.SynchronizeSceneNetworkObjects(sceneIndex, m_NetworkManager); - // If this was the base scene (i.e. active scene) then move all NetworkObjects to this scene (if they were moved out) - if ((sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) - { - MoveObjectsToScene(nextScene); - } - // Check to see if we still have scenes to load and synchronize with HandleClientSceneEvent(null); } From d7846f82def8d9bfe2d1757fdd025569fbc6d017 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 12 Jul 2021 16:33:06 -0500 Subject: [PATCH 023/106] fix Adding missing scene assets for the multiprocess test. Making sure the SceneLoading test destroys the network manager instance. --- .../Assets/Scenes/MultiprocessTestScene.unity | 14 ++++++++------ .../Assets/Tests/Runtime/SceneLoadingTest.cs | 12 +++++++++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/testproject/Assets/Scenes/MultiprocessTestScene.unity b/testproject/Assets/Scenes/MultiprocessTestScene.unity index d4a6e985ce..b9d7274b57 100644 --- a/testproject/Assets/Scenes/MultiprocessTestScene.unity +++ b/testproject/Assets/Scenes/MultiprocessTestScene.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: @@ -320,6 +320,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -498,6 +499,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -593,6 +595,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -685,6 +688,9 @@ MonoBehaviour: RegisteredScenes: - MultiprocessTestScene - SampleScene + RegisteredSceneAssets: + - {fileID: 102900000, guid: 76743cb7b342c49279327834918a9c6e, type: 3} + - {fileID: 102900000, guid: 9fc0d4010bbf28b4594072e72b8655ab, type: 3} AllowRuntimeSceneChanges: 0 PlayerPrefab: {fileID: 4700706668509470175, guid: 7eeaaf9e50c0afc4dab93584a54fb0d6, type: 3} @@ -740,11 +746,9 @@ MonoBehaviour: m_GameObject: {fileID: 1211923374} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 068bf11ceb1344667af4cc40950f44f4, type: 3} + m_Script: {fileID: 0} m_Name: m_EditorClassIdentifier: - referencedPrefab: {fileID: 5637023994061915634, guid: b0952a471c5a147cb92f6afcdb648f8a, - type: 3} --- !u!114 &1211923378 MonoBehaviour: m_ObjectHideFlags: 0 @@ -817,8 +821,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: ef1240e0784f84eadb77fe822e2e03c7, type: 3} m_Name: m_EditorClassIdentifier: - isRegistering: 0 - hasRegistered: 0 --- !u!1 &1674777071 GameObject: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs index fceae52ea4..78f01ca759 100644 --- a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs +++ b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs @@ -75,6 +75,8 @@ public IEnumerator SceneLoading() m_NetworkManager = gameObject.GetComponent(); + + Assert.IsNotNull(m_NetworkManager); // Start in host mode @@ -146,13 +148,17 @@ public IEnumerator SceneLoading() break; } } - + Assert.IsFalse(m_TimedOut); // We are now done with the NetworkSceneManager switch scene test so stop the host m_NetworkManager.StopHost(); // Set the original Test Runner Scene to be the active scene SceneManager.SetActiveScene(originalScene); + m_NetworkManager.DontDestroy = false; + SceneManager.MoveGameObjectToScene(m_NetworkManager.gameObject, originalScene); + + // Unload the previously active scene SceneManager.UnloadSceneAsync(primaryScene).completed += UnloadAsync_completed; @@ -167,6 +173,8 @@ public IEnumerator SceneLoading() break; } } + Object.DestroyImmediate(m_NetworkManager.gameObject); + Assert.IsFalse(m_TimedOut); // Done! } @@ -222,6 +230,8 @@ private IEnumerator SetUp() private IEnumerator TearDown() { MLAPI.SceneManagement.NetworkSceneManager.IsRunningUnitTest = false; + + yield return null; } } From 3a7a0e9ccd1de3d74f1616a042dc6533000adade Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 12 Jul 2021 17:02:02 -0500 Subject: [PATCH 024/106] style PascalCased my enums and added event in front to avoid any NDA complications for the time being. --- .../SceneManagement/NetworkSceneManager.cs | 40 +++++++++---------- .../Runtime/SceneManagement/SceneEventData.cs | 32 +++++++-------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index ff9c7cc413..d9dfdd402c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -261,7 +261,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.UNLOAD; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.EventUnload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++) @@ -324,7 +324,7 @@ private void OnSceneUnloaded() using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.UNLOAD_COMPLETE; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Unload_Complete; writer.WriteObjectPacked(SceneEventData); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -351,7 +351,7 @@ public SceneSwitchProgress LoadScene(string sceneName) } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.LOAD; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.EventLoad; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; // NSS TODO: remove this completely once done with the transition @@ -387,7 +387,7 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SWITCH; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.EventSwitch; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; SceneEventData.LoadSceneMode = IsRunningUnitTest ? loadSceneMode : LoadSceneMode.Single; @@ -420,7 +420,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene - if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH) + if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch) { foreach (var additiveSceneName in m_AdditiveScenesLoaded) { @@ -513,7 +513,7 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) private void OnSceneLoaded(string sceneName) { var nextScene = SceneManager.GetSceneByName(sceneName); - if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH) + if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch) { SceneManager.SetActiveScene(nextScene); } @@ -521,7 +521,7 @@ private void OnSceneLoaded(string sceneName) //Get all NetworkObjects loaded by the scene PopulateScenePlacedObjects(nextScene); - if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH) + if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch) { // Move all objects to the new scene MoveObjectsToScene(nextScene); @@ -623,7 +623,7 @@ private void OnClientLoadedScene() using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - SceneEventData.SceneEventType = SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.SWITCH ? SceneEventData.SceneEventTypes.SWITCH_COMPLETE : SceneEventData.SceneEventTypes.LOAD_COMPLETE; + SceneEventData.SceneEventType = SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch ? SceneEventData.SceneEventTypes.Event_Switch_Complete : SceneEventData.SceneEventTypes.Event_Load_Complete; writer.WriteObjectPacked(SceneEventData); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -655,7 +655,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.InitializeForSynch(m_NetworkManager); ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); - ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC; + ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.EventSync; for (int i = 0; i < SceneManager.sceneCount; i++) { @@ -860,18 +860,18 @@ private void HandleClientSceneEvent(Stream stream) switch (SceneEventData.SceneEventType) { // Both events are basically the same with some minor differences - case SceneEventData.SceneEventTypes.SWITCH: - case SceneEventData.SceneEventTypes.LOAD: + case SceneEventData.SceneEventTypes.EventSwitch: + case SceneEventData.SceneEventTypes.EventLoad: { OnClientSceneLoadingEvent(stream); break; } - case SceneEventData.SceneEventTypes.UNLOAD: + case SceneEventData.SceneEventTypes.EventUnload: { OnClientUnloadScene(); break; } - case SceneEventData.SceneEventTypes.SYNC: + case SceneEventData.SceneEventTypes.EventSync: { if (!SceneEventData.IsDoneWithSynchronization()) { @@ -885,7 +885,7 @@ private void HandleClientSceneEvent(Stream stream) using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.SYNC_COMPLETE; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync_Complete; writer.WriteObjectPacked(SceneEventData); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -909,22 +909,22 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { switch (SceneEventData.SceneEventType) { - case SceneEventData.SceneEventTypes.SWITCH_COMPLETE: + case SceneEventData.SceneEventTypes.Event_Switch_Complete: { OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); break; } - case SceneEventData.SceneEventTypes.LOAD_COMPLETE: + case SceneEventData.SceneEventTypes.Event_Load_Complete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.LOAD_COMPLETE)}] Client Id {clientId} finished loading additive scene."); + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); break; } - case SceneEventData.SceneEventTypes.UNLOAD_COMPLETE: + case SceneEventData.SceneEventTypes.Event_Unload_Complete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.UNLOAD_COMPLETE)}] Client Id {clientId} finished unloading additive scene."); + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.Event_Unload_Complete)}] Client Id {clientId} finished unloading additive scene."); break; } - case SceneEventData.SceneEventTypes.SYNC_COMPLETE: + case SceneEventData.SceneEventTypes.Event_Sync_Complete: { // NSS TOOD: The scene event local notification needs to be called //m_NetworkManager.NotifyPlayerConnected(clientId); diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 955e73e417..7a4c58bb57 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -15,14 +15,14 @@ public class SceneEventData : INetworkSerializable, IDisposable { public enum SceneEventTypes { - SWITCH, //Server to client full scene switch (i.e. single mode and destroy everything) - LOAD, //Server to client load additive scene - UNLOAD, //Server to client unload additive scene - SYNC, //Server to client late join approval synchronization - SWITCH_COMPLETE, //Client to server - LOAD_COMPLETE, //Client to server - UNLOAD_COMPLETE, //Client to server - SYNC_COMPLETE, //Client to server + EventSwitch, //Server to client full scene switch (i.e. single mode and destroy everything) + EventLoad, //Server to client load additive scene + EventUnload, //Server to client unload additive scene + EventSync, //Server to client late join approval synchronization + Event_Switch_Complete, //Client to server + Event_Load_Complete, //Client to server + Event_Unload_Complete, //Client to server + Event_Sync_Complete, //Client to server } public SceneEventTypes SceneEventType; @@ -103,10 +103,10 @@ public bool IsSceneEventClientSide() { switch (SceneEventType) { - case SceneEventTypes.LOAD: - case SceneEventTypes.SWITCH: - case SceneEventTypes.UNLOAD: - case SceneEventTypes.SYNC: + case SceneEventTypes.EventLoad: + case SceneEventTypes.EventSwitch: + case SceneEventTypes.EventUnload: + case SceneEventTypes.EventSync: { return true; } @@ -149,14 +149,14 @@ private void OnWrite(NetworkWriter writer) writer.WriteByte((byte)LoadSceneMode); - if (SceneEventType != SceneEventTypes.SYNC) + if (SceneEventType != SceneEventTypes.EventSync) { writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); } writer.WriteUInt32Packed(SceneIndex); - if (SceneEventType == SceneEventTypes.SYNC) + if (SceneEventType == SceneEventTypes.EventSync) { writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); @@ -231,14 +231,14 @@ private void OnRead(NetworkReader reader) // NSS TODO: Add to proposal's MTT discussion topics: Should we assert here? } - if (SceneEventType != SceneEventTypes.SYNC) + if (SceneEventType != SceneEventTypes.EventSync) { SwitchSceneGuid = new Guid(reader.ReadByteArray()); } SceneIndex = reader.ReadUInt32Packed(); - if (SceneEventType == SceneEventTypes.SYNC) + if (SceneEventType == SceneEventTypes.EventSync) { var keyPairCount = reader.ReadInt32Packed(); From d1670b79f9bb4b27c561d84452369ff96a9445cc Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 14 Jul 2021 15:45:20 -0500 Subject: [PATCH 025/106] refactor and fix Minor test project fixes and additional tests for the AdditiveSceneToggleHandler to test back to back scene loading during a scene transition. Removed the distinguishing difference between switchscene and loadscene, left the switchscene as a wrapper around loadscene. Updated existing profiling code base to remove legacy scene manager profiler checks. --- .../Runtime/Configuration/NetworkConstants.cs | 5 +- .../Runtime/Core/NetworkManager.cs | 19 +-- .../Runtime/Core/NetworkObject.cs | 13 -- .../Messaging/IInternalMessageHandler.cs | 3 +- .../Messaging/InternalMessageHandler.cs | 6 + ...nternalMessageHandlerProfilingDecorator.cs | 29 ++-- .../SceneManagement/NetworkSceneManager.cs | 139 ++++++++---------- .../Runtime/SceneManagement/SceneEventData.cs | 18 ++- .../Tests/Editor/DummyMessageHandler.cs | 4 +- .../NetworkManagerMessageHandlerTests.cs | 31 +--- ...alMessageHandlerProfilingDecoratorTests.cs | 14 +- .../AdditiveSceneToggleHandler.cs | 98 ++++++------ .../SceneTransitioningBase2.unity | 2 + .../Scripts/GenericNetworkObjectBehaviour.cs | 28 ++-- 14 files changed, 173 insertions(+), 236 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs index d3bdf99cd0..0cc7a5039b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs @@ -24,9 +24,7 @@ internal static class NetworkConstants internal const byte SNAPSHOT_DATA = 25; internal const byte SERVER_RPC = 30; internal const byte CLIENT_RPC = 31; - internal const byte SWITCH_SCENE = 32; internal const byte SCENE_EVENT = 33; - internal const byte SCENE_EVENT_NOTIFICATION = 34; internal const byte CLIENT_SWITCH_SCENE_COMPLETED = 35; internal const byte INVALID = 36; @@ -64,8 +62,7 @@ internal static class NetworkConstants "", "SERVER_RPC", "CLIENT_RPC", - "SWITCH_SCENE", - "SCENE_EVENT", // New Scene Event command (to replace and add Switch, Load, and Unload with sub-commands and additional information to define the scene event) + "SCENE_EVENT", // New Scene Event command "INVALID" // 36 }; } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 10de5a26fa..a159819091 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -1281,25 +1281,9 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, } break; - //case NetworkConstants.SWITCH_SCENE: - // { - // if (IsClient) - // { - // MessageHandler.HandleSwitchScene(clientId, messageStream); - - // } - // break; - // } case NetworkConstants.SCENE_EVENT: { - if (SceneManager != null) - { - SceneManager.HandleSceneEvent(clientId, messageStream); - } - else - { - Debug.LogError($"Received {nameof(NetworkConstants.SCENE_EVENT)} but {nameof(SceneManager)} is null!"); - } + MessageHandler.HandleSceneEvent(clientId, messageStream); break; } case NetworkConstants.CHANGE_OWNER: @@ -1358,7 +1342,6 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, { MessageHandler.HandleNetworkLog(clientId, messageStream); } - break; case NetworkConstants.SERVER_RPC: { diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs index 1710c91100..d001cd1120 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs @@ -179,19 +179,6 @@ internal set /// public bool DontDestroyWithOwner; - internal Scene SourceAdditiveScene; - - /// - /// Proposed Solution for custom Network Prefab Handlers and client late-join synchronization - /// This will provide the users with a way to associate a NetworkObject with a "source scene" in order - /// to make sure the NetworkObjects are synchronized when the additive source scene is loaded and not before - /// - /// - public void SetSourceAdditiveScene(Scene scene) - { - SourceAdditiveScene = scene; - } - /// /// Whether or not to enable automatic NetworkObject parent synchronization. /// diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs index ceab9b95f0..01f7b627ec 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs @@ -11,8 +11,7 @@ internal interface IInternalMessageHandler void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime); void HandleAddObject(ulong clientId, Stream stream); void HandleDestroyObject(ulong clientId, Stream stream); - void HandleSwitchScene(ulong clientId, Stream stream); - void HandleClientSwitchSceneCompleted(ulong clientId, Stream stream); + void HandleSceneEvent(ulong clientId, Stream stream); void HandleChangeOwner(ulong clientId, Stream stream); void HandleAddObjects(ulong clientId, Stream stream); void HandleDestroyObjects(ulong clientId, Stream stream); diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index b1d95503b4..69045cc0d9 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -147,6 +147,12 @@ public void HandleDestroyObject(ulong clientId, Stream stream) } } + + public void HandleSceneEvent(ulong clientId, Stream stream) + { + NetworkManager.SceneManager.HandleSceneEvent(clientId, stream); + } + public void HandleSwitchScene(ulong clientId, Stream stream) { Debug.LogError("HandleSwitchScene is no longer supported!\n"); diff --git a/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs b/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs index b3c76d30b1..219a5760fe 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs @@ -12,8 +12,7 @@ internal class InternalMessageHandlerProfilingDecorator : IInternalMessageHandle private readonly ProfilerMarker m_HandleConnectionApproved = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleConnectionApproved)}"); private readonly ProfilerMarker m_HandleAddObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleAddObject)}"); private readonly ProfilerMarker m_HandleDestroyObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleDestroyObject)}"); - private readonly ProfilerMarker m_HandleSwitchScene = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleSwitchScene)}"); - private readonly ProfilerMarker m_HandleClientSwitchSceneCompleted = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleClientSwitchSceneCompleted)}"); + private readonly ProfilerMarker m_HandleSceneEvent = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleSceneEvent)}"); private readonly ProfilerMarker m_HandleChangeOwner = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleChangeOwner)}"); private readonly ProfilerMarker m_HandleAddObjects = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleAddObjects)}"); private readonly ProfilerMarker m_HandleDestroyObjects = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleDestroyObjects)}"); @@ -71,23 +70,6 @@ public void HandleDestroyObject(ulong clientId, Stream stream) m_HandleDestroyObject.End(); } - public void HandleSwitchScene(ulong clientId, Stream stream) - { - m_HandleSwitchScene.Begin(); - - m_MessageHandler.HandleSwitchScene(clientId, stream); - - m_HandleSwitchScene.End(); - } - - public void HandleClientSwitchSceneCompleted(ulong clientId, Stream stream) - { - m_HandleClientSwitchSceneCompleted.Begin(); - - m_MessageHandler.HandleClientSwitchSceneCompleted(clientId, stream); - - m_HandleClientSwitchSceneCompleted.End(); - } public void HandleChangeOwner(ulong clientId, Stream stream) { @@ -186,6 +168,15 @@ public void HandleNetworkLog(ulong clientId, Stream stream) m_HandleNetworkLog.End(); } + public void HandleSceneEvent(ulong clientId, Stream stream) + { + m_HandleSceneEvent.Begin(); + + m_MessageHandler.HandleSceneEvent(clientId, stream); + + m_HandleSceneEvent.End(); + } + public void HandleAllClientsSwitchSceneCompleted(ulong clientId, Stream stream) { m_HandleAllClientsSwitchSceneCompleted.Begin(); diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index d9dfdd402c..4d996f933d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -165,7 +165,6 @@ public void AddRuntimeSceneName(string sceneName, uint index) /// /// Validates the new scene event request by the server-side code. /// This also initializes some commonly shared values as well as switchSceneProgress - /// NSS TODO: Should switchSceneProgress /// /// /// SceneSwitchProgress (if null it failed) @@ -207,16 +206,15 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn if (!isUnloading) { - // NSS TODO: This either needs a better name or there has to be a better way of detecting this condition - // The Condition: While a scene is asynchronously loaded, if any new NetworkObjects are spawned they need to be moved into the do not destroy temporary scene + // 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 // When it is set: Just before starting the asynchronous loading call - // When it is unset: - // after the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene + // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene IsSpawnedObjectsPendingInDontDestroyOnLoad = true; } s_IsSceneEventActive = true; + // NSS TODO: switchSceneProgress needs to be re-factored switchSceneProgress.OnClientLoadedScene += clientId => { OnNotifyServerClientLoadedScene?.Invoke(switchSceneProgress, clientId); }; switchSceneProgress.OnComplete += timedOut => { @@ -231,7 +229,6 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn writer.WriteULongArray(doneClientIds, doneClientIds.Length); writer.WriteULongArray(timedOutClientIds, timedOutClientIds.Length); - // NSS TODO: This will need to be modified for loading and unloading m_NetworkManager.MessageSender.Send(NetworkManager.Singleton.ServerClientId, NetworkConstants.ALL_CLIENTS_LOADED_SCENE, NetworkChannel.Internal, buffer); } }; @@ -281,9 +278,9 @@ public SceneSwitchProgress UnloadScene(string sceneName) AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneUnload); - if(m_AdditiveScenesLoaded.Contains(sceneName)) + if(m_ScenesLoaded.Contains(sceneName)) { - m_AdditiveScenesLoaded.Remove(sceneName); + m_ScenesLoaded.Remove(sceneName); } OnAdditiveSceneEvent?.Invoke(sceneUnload, sceneName, false); @@ -306,9 +303,9 @@ private void OnClientUnloadScene() var sceneUnload = SceneManager.UnloadSceneAsync(sceneName); - if (m_AdditiveScenesLoaded.Contains(sceneName)) + if (m_ScenesLoaded.Contains(sceneName)) { - m_AdditiveScenesLoaded.Remove(sceneName); + m_ScenesLoaded.Remove(sceneName); } sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); @@ -333,7 +330,7 @@ private void OnSceneUnloaded() } - private List m_AdditiveScenesLoaded = new List(); + private List m_ScenesLoaded = new List(); /// /// Additively loads the scene @@ -342,7 +339,7 @@ private void OnSceneUnloaded() /// NSS TODO: This could probably stand to have some form of "scene event status" class/structure that will /// include any error types/messages and the SceneSwitchProgress class associated with the status (if success) /// SceneSwitchProgress (if null this call failed) - public SceneSwitchProgress LoadScene(string sceneName) + public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Additive, SceneEventData.SceneEventTypes eventType = SceneEventData.SceneEventTypes.EventLoad) { var switchSceneProgress = ValidateServerSceneEvent(sceneName); if (switchSceneProgress == null) @@ -351,14 +348,21 @@ public SceneSwitchProgress LoadScene(string sceneName) } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.EventLoad; + SceneEventData.SceneEventType = eventType; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + SceneEventData.LoadSceneMode = loadSceneMode; - // NSS TODO: remove this completely once done with the transition - SceneEventData.LoadSceneMode = LoadSceneMode.Additive; + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + { + // Destroy current scene objects before switching. + m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects(); + + // Preserve the objects that should not be destroyed during the scene event + MoveObjectsToDontDestroyOnLoad(); + } // Begin the scene event - OnBeginSceneEvent(sceneName, switchSceneProgress, LoadSceneMode.Additive); + OnBeginSceneEvent(sceneName, switchSceneProgress, loadSceneMode); //Return our scene progress instance return switchSceneProgress; @@ -369,40 +373,12 @@ public SceneSwitchProgress LoadScene(string sceneName) /// /// The name of the scene to switch to /// The mode to load the scene (Additive vs Single) - /// NSS TODO: This could probably stand to have some form of "scene event status" class/structure that will - /// include any error types/messages and the SceneSwitchProgress class associated with the status (if success) + /// NSS TODO: This is a topic for MTT discussion. Do we want to keep this divergence from Unity's SceneManager API? + /// It is proposed we completely remove this call or mark it as deprecated with message to use Load and Unload /// SceneSwitchProgress public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) { - // NSS TODO: Remove this once the LoadScene method is completed and all areas in the code that use the loadSceneMode parameter are updated - if (loadSceneMode == LoadSceneMode.Additive && !IsRunningUnitTest) - { - return LoadScene(sceneName); - } - - var switchSceneProgress = ValidateServerSceneEvent(sceneName); - if (switchSceneProgress == null) - { - return null; - } - - SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.EventSwitch; - SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; - - SceneEventData.LoadSceneMode = IsRunningUnitTest ? loadSceneMode : LoadSceneMode.Single; - - // Destroy current scene objects before switching. - m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects(); - - // Preserve the objects that should not be destroyed during the scene event - MoveObjectsToDontDestroyOnLoad(); - - // Begin the scene event - OnBeginSceneEvent(sceneName, switchSceneProgress, SceneEventData.LoadSceneMode); - - //Return our scene progress instance - return switchSceneProgress; + return LoadScene(sceneName, loadSceneMode, SceneEventData.SceneEventTypes.EventSwitch); } /// @@ -414,27 +390,32 @@ public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadScene private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchSceneProgress, LoadSceneMode loadSceneMode) { // start loading the scene + // NSS TODO: This is a temporary place holder check to make sure we don't try to unload a scene loaded in single mode. + var currentActiveScene = SceneManager.GetActiveScene(); AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); - if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch) + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - foreach (var additiveSceneName in m_AdditiveScenesLoaded) + foreach (var additiveSceneName in m_ScenesLoaded) { - SceneManager.UnloadSceneAsync(additiveSceneName); + if(currentActiveScene.name != additiveSceneName) + { + SceneManager.UnloadSceneAsync(additiveSceneName); + } } - m_AdditiveScenesLoaded.Clear(); + m_ScenesLoaded.Clear(); // NSS TODO: Make a single unified notification callback OnSceneSwitchStarted?.Invoke(sceneLoad); } else { - if(!m_AdditiveScenesLoaded.Contains(sceneName)) + if(!m_ScenesLoaded.Contains(sceneName)) { - m_AdditiveScenesLoaded.Add(sceneName); + m_ScenesLoaded.Add(sceneName); } else { @@ -463,23 +444,28 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) return; } - + // NSS TODO: This is a temporary place holder check to make sure we don't try to unload a scene loaded in single mode. + var currentActiveScene = SceneManager.GetActiveScene(); if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - foreach(var additiveSceneName in m_AdditiveScenesLoaded) + foreach(var loadedSceneName in m_ScenesLoaded) { - SceneManager.UnloadSceneAsync(additiveSceneName); + if(currentActiveScene.name != loadedSceneName) + { + SceneManager.UnloadSceneAsync(loadedSceneName); + } + } - m_AdditiveScenesLoaded.Clear(); + m_ScenesLoaded.Clear(); // Move ALL NetworkObjects to the temp scene MoveObjectsToDontDestroyOnLoad(); } else { - if (!m_AdditiveScenesLoaded.Contains(sceneName)) + if (!m_ScenesLoaded.Contains(sceneName)) { - m_AdditiveScenesLoaded.Add(sceneName); + m_ScenesLoaded.Add(sceneName); } else { @@ -487,12 +473,13 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) } } - // NSS TODO: This either needs a better name or there has to be a better way of detecting this condition - // The Condition: While a scene is asynchronously loaded, if any new NetworkObjects are spawned they need to be moved into the do not destroy temporary scene + // 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 // When it is set: Just before starting the asynchronous loading call - // When it is unset: - // after the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene - IsSpawnedObjectsPendingInDontDestroyOnLoad = true; + // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + { + IsSpawnedObjectsPendingInDontDestroyOnLoad = true; + } var sceneLoad = SceneManager.LoadSceneAsync(sceneName, SceneEventData.LoadSceneMode); sceneLoad.completed += asyncOp2 => OnSceneLoaded(sceneName); @@ -513,25 +500,25 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) private void OnSceneLoaded(string sceneName) { var nextScene = SceneManager.GetSceneByName(sceneName); - if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch) + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { SceneManager.SetActiveScene(nextScene); + m_ScenesLoaded.Add(sceneName); } //Get all NetworkObjects loaded by the scene PopulateScenePlacedObjects(nextScene); - if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch) + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { // Move all objects to the new scene MoveObjectsToScene(nextScene); + } - // NSS TODO: This either needs a better name or there has to be a better way of detecting this condition - // The Condition: While a scene is asynchronously loaded, if any new NetworkObjects are spawned they need to be moved into the do not destroy temporary scene + // 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 // When it is set: Just before starting the asynchronous loading call - // When it is unset: - // after the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene + // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene IsSpawnedObjectsPendingInDontDestroyOnLoad = false; if (m_NetworkManager.IsServer) @@ -623,7 +610,8 @@ private void OnClientLoadedScene() using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - SceneEventData.SceneEventType = SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.EventSwitch ? SceneEventData.SceneEventTypes.Event_Switch_Complete : SceneEventData.SceneEventTypes.Event_Load_Complete; + // NSS TODO: This is part of decoupling the concept of "scene switching" and just distinguishing between single and additive scene loading modes + SceneEventData.SceneEventType = SceneEventData.LoadSceneMode == LoadSceneMode.Single ? SceneEventData.SceneEventTypes.Event_Switch_Complete : SceneEventData.SceneEventTypes.Event_Load_Complete; writer.WriteObjectPacked(SceneEventData); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -677,11 +665,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) { if (networkObject.gameObject.scene != scene) { - // If it is not associated with an additive scene or if the additive scene is not the current scene we are processing then continue - if (networkObject.SourceAdditiveScene == null || networkObject.SourceAdditiveScene != scene) - { - continue; - } + continue; } ClientSynchEventData.AddNetworkObjectForSynch(malpiSceneIndex, networkObject); } @@ -718,6 +702,7 @@ private void OnClientBeginSynch(uint sceneIndex) if (sceneName != activeScene.name) { + // NSS TODO: Add to proposal's MTT discussion topics: Should we cover switching the active scene for V1.0.0? var sceneLoad = SceneManager.LoadSceneAsync(sceneName, sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive); sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex); } @@ -737,6 +722,7 @@ private void ClientLoadedSynchronization(uint sceneIndex) var nextScene = SceneManager.GetSceneByName(GetSceneNameFromMLAPISceneIndex(sceneIndex)); if (nextScene == null) { + Debug.LogError($"Client was trying to load {sceneIndex} which does not appear to be a valid registered scene index!"); return; } @@ -916,6 +902,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } case SceneEventData.SceneEventTypes.Event_Load_Complete: { + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); break; } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 7a4c58bb57..847ddc8e3d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -15,14 +15,16 @@ public class SceneEventData : INetworkSerializable, IDisposable { public enum SceneEventTypes { - EventSwitch, //Server to client full scene switch (i.e. single mode and destroy everything) - EventLoad, //Server to client load additive scene - EventUnload, //Server to client unload additive scene - EventSync, //Server to client late join approval synchronization - Event_Switch_Complete, //Client to server - Event_Load_Complete, //Client to server - Event_Unload_Complete, //Client to server - Event_Sync_Complete, //Client to server + EventSwitch, //Server to client full scene switch (i.e. single mode and destroy everything) + EventLoad, //Server to client load additive scene + EventUnload, //Server to client unload additive scene + EventSetActive, //Server to client make scene active + EventSync, //Server to client late join approval synchronization + Event_Switch_Complete, //Client to server + Event_Load_Complete, //Client to server + Event_Unload_Complete, //Client to server + Event_SetActiveComplete, //Client to server + Event_Sync_Complete, //Client to server } public SceneEventTypes SceneEventType; diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/DummyMessageHandler.cs b/com.unity.multiplayer.mlapi/Tests/Editor/DummyMessageHandler.cs index f6a28be50f..8b8171a01c 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/DummyMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/DummyMessageHandler.cs @@ -18,9 +18,7 @@ internal class DummyMessageHandler : IInternalMessageHandler public void HandleDestroyObject(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleDestroyObject)); - public void HandleSwitchScene(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleSwitchScene)); - - public void HandleClientSwitchSceneCompleted(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleClientSwitchSceneCompleted)); + public void HandleSceneEvent(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleSceneEvent)); public void HandleChangeOwner(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleChangeOwner)); diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs index 5a64f58c4a..15f1e6d07b 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs @@ -71,12 +71,6 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); } - // Should not cause log (client only) - //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SWITCH_SCENE, inputBuffer)) - //{ - // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - //} - // Should not cause log (client only) using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CHANGE_OWNER, inputBuffer)) { @@ -122,12 +116,12 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); } - //// Should cause log (server only) - //LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleClientSwitchSceneCompleted)); - //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, inputBuffer)) - //{ - // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - //} + // Should cause log (server and client) + LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleSceneEvent)); + using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SCENE_EVENT, inputBuffer)) + { + networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); + } // Should cause log (server only) LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleNetworkLog)); @@ -189,13 +183,6 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); } - // Should cause log (client only) - //LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleSwitchScene)); - //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SWITCH_SCENE, inputBuffer)) - //{ - // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - //} - // Should cause log (client only) LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleChangeOwner)); using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CHANGE_OWNER, inputBuffer)) @@ -245,12 +232,6 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); } - //// Should not cause log (server only) - //using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.CLIENT_SWITCH_SCENE_COMPLETED, inputBuffer)) - //{ - // networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0, true); - //} - // Should not cause log (server only) using (var messageStream = MessagePacker.WrapMessage(NetworkConstants.SERVER_LOG, inputBuffer)) { diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs index bbe369642c..599c9c0f05 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs @@ -49,19 +49,11 @@ public void HandleDestroyObjectCallsUnderlyingHandler() } [Test] - public void HandleSwitchSceneCallsUnderlyingHandler() + public void HandleSceneEventCallsUnderlyingHandler() { - m_Decorator.HandleSwitchScene(0, null); + m_Decorator.HandleSceneEvent(0, null); - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleSwitchScene)); - } - - [Test] - public void HandleClientSwitchSceneCompletedCallsUnderlyingHandler() - { - m_Decorator.HandleClientSwitchSceneCompleted(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleClientSwitchSceneCompleted)); + LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleSceneEvent)); } [Test] diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index 642ba284d0..0f0ce56aa7 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -12,7 +12,8 @@ namespace TestProject.ManualTests { public class AdditiveSceneToggleHandler : NetworkBehaviour { - public static bool ExitingNow { get; internal set; } + [SerializeField] + private bool m_ActivateOnLoad = false; private Toggle m_ToggleObject; @@ -20,6 +21,8 @@ public class AdditiveSceneToggleHandler : NetworkBehaviour [SerializeField] private string m_SceneToLoad; + + #if UNITY_EDITOR [SerializeField] private SceneAsset m_SceneAsset; @@ -31,32 +34,13 @@ private void OnValidate() } } #endif - private int m_CurrentSceneIndex; - - private void Awake() - { - ExitingNow = false; - } private void Start() { m_ToggleObject = gameObject.GetComponentInChildren(); StartCoroutine(CheckForVisibility()); - - //NetworkManager.SceneManager.OnSceneSwitchStarted += SceneManager_OnSceneSwitchStarted; } - //private void SceneManager_OnSceneSwitchStarted(AsyncOperation operation) - //{ - // if (m_ToggleObject) - // { - // if (m_ToggleObject.isOn) - // { - // m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); - // } - // } - //} - private bool m_ExitingScene; private void OnDestroy() { @@ -68,44 +52,47 @@ private IEnumerator CheckForVisibility() { while (!m_ExitingScene) { - if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening ) { if (m_ToggleObject) { - m_ToggleObject.gameObject.SetActive(true); + if(NetworkManager.Singleton.IsServer) + { + m_ToggleObject.gameObject.SetActive(true); + if (m_ActivateOnLoad) + { + StartCoroutine(DelayedActivate()); + } + } + else + { + m_ToggleObject.gameObject.SetActive(false); + } } + break; } else { - if (m_ToggleObject) + if (m_ToggleObject && m_ToggleObject.gameObject.activeInHierarchy) { m_ToggleObject.gameObject.SetActive(false); } } - yield return new WaitForSeconds(0.5f); + yield return new WaitForSeconds(0.1f); } yield return null; } - public override void OnNetworkSpawn() + private IEnumerator DelayedActivate() { - if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) + yield return new WaitForSeconds(0.5f); + if (m_ToggleObject) { - if (m_ToggleObject) - { - m_ToggleObject.gameObject.SetActive(true); - } + m_ToggleObject.isOn = true; } - else - { - if (m_ToggleObject) - { - m_ToggleObject.gameObject.SetActive(false); - } - } - base.OnNetworkSpawn(); + yield return null; } private SceneSwitchProgress m_CurrentSceneSwitchProgress; @@ -117,18 +104,37 @@ public void OnToggle() { if (m_ToggleObject) { - if(m_ToggleObject.isOn) - { - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad); - } - else - { - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); - } + m_ToggleObject.enabled = false; + StartCoroutine(SceneEventCoroutine(m_ToggleObject.isOn)); + } + } + } + + private IEnumerator SceneEventCoroutine(bool isLoading) + { + while (m_CurrentSceneSwitchProgress == null) + { + if (isLoading) + { + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad); + } + else + { + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); + } + if (m_CurrentSceneSwitchProgress == null) + { + yield return new WaitForSeconds(0.25f); } } + m_ToggleObject.isOn = isLoading; + m_ToggleObject.enabled = true; + m_CurrentSceneSwitchProgress = null; + yield return null; } + + public delegate void OnSceneSwitchCompletedDelegateHandler(); public event OnSceneSwitchCompletedDelegateHandler OnSceneSwitchCompleted; diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity index f1b2558852..603d6268aa 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity @@ -780,6 +780,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} m_Name: m_EditorClassIdentifier: + m_ActivateOnLoad: 1 m_SceneToLoad: AdditiveScene4 m_SceneAsset: {fileID: 102900000, guid: dc7e17c86f5ca81478043be306027c13, type: 3} --- !u!1 &300124661 @@ -1366,6 +1367,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} m_Name: m_EditorClassIdentifier: + m_ActivateOnLoad: 1 m_SceneToLoad: AdditiveScene3 m_SceneAsset: {fileID: 102900000, guid: 7da3dd618f5b5a34db1f6d3c9511e221, type: 3} --- !u!850595691 &903034822 diff --git a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs index d480cc9f0f..16d4bfb9cb 100644 --- a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs +++ b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs @@ -82,25 +82,31 @@ private void FixedUpdate() private void LateUpdate() { - if (!IsOwner && !NetworkObject.IsSpawned && !IsRegisteredPoolObject && !IsRemovedFromPool) + if (!IsOwner) { - if (!DetectedMissedDespawn) + if (!NetworkObject.IsSpawned) { - DetectedMissedDespawn = true; - TimeToWaitUntilDestroy = Time.realtimeSinceStartup + 1.0f; + if (IsRegisteredPoolObject && !IsRemovedFromPool) + { + if (!DetectedMissedDespawn) + { + DetectedMissedDespawn = true; + TimeToWaitUntilDestroy = Time.realtimeSinceStartup + 1.0f; + } + else if (TimeToWaitUntilDestroy < Time.realtimeSinceStartup) + { + Debug.Log($"Destroying {gameObject.name} via {nameof(GenericNetworkObjectBehaviour)}."); + Destroy(gameObject); + } + } } - else if (TimeToWaitUntilDestroy < Time.realtimeSinceStartup) + else if (IsRegisteredPoolObject && DetectedMissedDespawn) { - Destroy(gameObject); + DetectedMissedDespawn = false; } } - else if (!IsOwner && IsRegisteredPoolObject && DetectedMissedDespawn) - { - DetectedMissedDespawn = false; - } } - private void OnTriggerEnter(Collider other) { if (IsOwner) From c686eee1e9bc207d4f5b251406a59da34cb16b2f Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 14 Jul 2021 16:41:19 -0500 Subject: [PATCH 026/106] fix Removing Scene Loading Test: This test needs to be completely refactored and no longer is valid. WIP --- .../Assets/Tests/Runtime/SceneLoadingTest.cs | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs index 6fab7883f5..cb8ea6ecd2 100644 --- a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs +++ b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs @@ -7,6 +7,7 @@ namespace TestProject.RuntimeTests { +#if IGNORETHISTEST /// /// This is nothing more than a template to follow in order to /// use a scene to configure your NetworkManager as well as how @@ -16,6 +17,7 @@ namespace TestProject.RuntimeTests /// public class SceneLoadingTest { + private NetworkManager m_NetworkManager; private bool m_SceneLoaded; @@ -26,6 +28,9 @@ public class SceneLoadingTest [UnityTest] public IEnumerator SceneLoading() { + + + // Keep track of the original test scene Scene originalScene = SceneManager.GetActiveScene(); @@ -85,6 +90,7 @@ public IEnumerator SceneLoading() m_NetworkManager.StartHost(); } + // Next, we want to do a scene transition using NetworkSceneManager m_TargetSceneNameToLoad = "SecondSceneToLoad"; @@ -93,9 +99,6 @@ public IEnumerator SceneLoading() // m_NetworkManager.NetworkConfig.AllowRuntimeSceneChanges = true; // m_NetworkManager.SceneManager.AddRuntimeSceneName(m_TargetSceneNameToLoad, (uint)m_NetworkManager.SceneManager.RegisteredSceneNames.Count); - // Store off the currently active scene so we can unload it - primaryScene = SceneManager.GetActiveScene(); - // Switch the scene using NetworkSceneManager var sceneSwitchProgress = m_NetworkManager.SceneManager.SwitchScene(m_TargetSceneNameToLoad, LoadSceneMode.Additive); @@ -126,13 +129,19 @@ public IEnumerator SceneLoading() // (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); } - // Now unload the previous scene - SceneManager.UnloadSceneAsync(primaryScene).completed += UnloadAsync_completed; + 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(); @@ -149,16 +158,11 @@ public IEnumerator SceneLoading() } } Assert.IsFalse(m_TimedOut); - // We are now done with the NetworkSceneManager switch scene test so stop the host - m_NetworkManager.StopHost(); + + m_NetworkManager.SceneManager.OnAdditiveSceneEvent -= SceneManager_OnAdditiveSceneEvent; // Set the original Test Runner Scene to be the active scene SceneManager.SetActiveScene(originalScene); - - m_NetworkManager.DontDestroy = false; - SceneManager.MoveGameObjectToScene(m_NetworkManager.gameObject, originalScene); - - // Unload the previously active scene SceneManager.UnloadSceneAsync(primaryScene).completed += UnloadAsync_completed; @@ -173,16 +177,27 @@ public IEnumerator SceneLoading() break; } } - Object.DestroyImmediate(m_NetworkManager.gameObject); Assert.IsFalse(m_TimedOut); - // Done! + + 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); } - [UnityTearDown] - public IEnumerator Teardown() + private void SceneManager_OnAdditiveSceneEvent(AsyncOperation operation, string sceneName, bool isLoading) { - Object.Destroy(m_NetworkManager); - yield return null; + if(!isLoading) + { + m_SceneLoaded = true; + } + } + + private void SceneManager_sceneUnloaded(Scene arg0) + { + m_SceneLoaded = true; } /// @@ -242,4 +257,5 @@ private IEnumerator TearDown() yield return null; } } +#endif } From e16628cf7886f1810d0d2e887b49cb05bfd25846 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 15 Jul 2021 17:18:20 -0500 Subject: [PATCH 027/106] refactor and feat Did some minor refactoring to make Load and Unload the only two methods that need to be called. Removed the switch event related enum types since they are no longer used. Added a re-synchronization step to handle edge case scenarios where a late joining client might miss destroy messages during the initial synchronization process. This includes adding an additional DependentSceneName property to NetworkObject. This lets the NetworkSceneManager know that a scene other than the NetworkObject's current scene is dependent upon this NetworkObject and to not instantiate it until that dependent scene is loaded. Removed a bunch of user-side code (i.e. test project) from GenericNetworkObject that was required to handle the above edge case scenarios regarding scene dependencies. Adjusted the NetworkPrefabPool and NetworkPrefabPoolAdditive to account for this addition, and removed a chunk of user-side code that would be required to handle this scenario. Refactored SceneEventData to not implement INetworkSerializable which removes additional allocation for the NetworkSerializer during read and write operations. Also adjusted the constructor to accept a NetworkManager instance to make it multi-instance compatible. Set the SceneTransitioningBase1 NetworkManager config to not recycle NetworkObjectIds due to an issue in how they are recycled. (topic for discussion) --- .../Runtime/Core/NetworkObject.cs | 21 ++ .../Messaging/InternalMessageHandler.cs | 30 +-- .../SceneManagement/NetworkSceneManager.cs | 84 ++++--- .../Runtime/SceneManagement/SceneEventData.cs | 219 ++++++++++++++---- .../AdditiveSceneToggleHandler.cs | 2 +- .../SceneTransitioningBase1.unity | 4 +- .../SwitchSceneHandlerAdditive.cs | 2 +- .../Scripts/GenericNetworkObjectBehaviour.cs | 45 +--- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 16 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 16 +- 10 files changed, 300 insertions(+), 139 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs index d001cd1120..5b8dd3588f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs @@ -151,6 +151,27 @@ internal set /// public bool DestroyWithScene { get; internal set; } + /// + /// Used for late-joining client synchronization purposes. + /// The scene that has dependencies to this NetworkObject + /// If not set then it is ignored. + /// (see NetworkObject.SetSceneAsDependency for more information) + /// + public string DependentSceneName { get; internal set; } + + /// + /// For late-joining client synchronization and additive scene(s) purposes + /// This provides the ability to associate a NetworkObject with a scene that is not + /// currently spawned in but may have dependencies within the dependent scene + /// Example: NetworkObject pool generator with custom Network Prefab override handler + /// needs to initialize before this NetworkObject is spawned. + /// + /// scene that has dependencies to the NetworkObject + public void SetSceneAsDependency(string sceneName) + { + DependentSceneName = sceneName; + } + /// /// Delegate type for checking visibility /// diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 69045cc0d9..0edb3e623d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -147,36 +147,16 @@ public void HandleDestroyObject(ulong clientId, Stream stream) } } - + /// + /// Called for all Scene Management related events + /// + /// + /// public void HandleSceneEvent(ulong clientId, Stream stream) { NetworkManager.SceneManager.HandleSceneEvent(clientId, stream); } - public void HandleSwitchScene(ulong clientId, Stream stream) - { - Debug.LogError("HandleSwitchScene is no longer supported!\n"); - //m_NetworkManager.SceneManager.OnSceneSwitch(stream); - //using (var reader = PooledNetworkReader.Get(stream)) - //{ - // uint sceneIndex = reader.ReadUInt32Packed(); - // var switchSceneGuid = new Guid(reader.ReadByteArray()); - // var isAdditivelyLoadedScene = reader.ReadBool(); - // var objectBuffer = new NetworkBuffer(); - // objectBuffer.CopyUnreadFrom(stream); - // objectBuffer.Position = 0; - - // m_NetworkManager.SceneManager.OnSceneSwitch(sceneIndex, switchSceneGuid, objectBuffer, isAdditivelyLoadedScene ? LoadSceneMode.Additive:LoadSceneMode.Single); - //} - } - - public void HandleClientSwitchSceneCompleted(ulong clientId, Stream stream) - { - using (var reader = PooledNetworkReader.Get(stream)) - { - m_NetworkManager.SceneManager.OnClientSceneLoadingEventCompleted(clientId, new Guid(reader.ReadByteArray())); - } - } public void HandleChangeOwner(ulong clientId, Stream stream) { diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 4d996f933d..018e613718 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -101,8 +101,8 @@ public class NetworkSceneManager internal NetworkSceneManager(NetworkManager networkManager) { m_NetworkManager = networkManager; - SceneEventData = new SceneEventData(); - ClientSynchEventData = new SceneEventData(); + SceneEventData = new SceneEventData(networkManager); + ClientSynchEventData = new SceneEventData(networkManager); } /// @@ -258,7 +258,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.EventUnload; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++) @@ -267,8 +267,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteObjectPacked(SceneEventData); - + SceneEventData.OnWrite(writer); m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } } @@ -322,7 +321,7 @@ private void OnSceneUnloaded() using (var writer = PooledNetworkWriter.Get(buffer)) { SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Unload_Complete; - writer.WriteObjectPacked(SceneEventData); + SceneEventData.OnWrite(writer); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -339,7 +338,7 @@ private void OnSceneUnloaded() /// NSS TODO: This could probably stand to have some form of "scene event status" class/structure that will /// include any error types/messages and the SceneSwitchProgress class associated with the status (if success) /// SceneSwitchProgress (if null this call failed) - public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Additive, SceneEventData.SceneEventTypes eventType = SceneEventData.SceneEventTypes.EventLoad) + public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMode) { var switchSceneProgress = ValidateServerSceneEvent(sceneName); if (switchSceneProgress == null) @@ -348,7 +347,7 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = eventType; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Load; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; SceneEventData.LoadSceneMode = loadSceneMode; @@ -378,7 +377,7 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo /// SceneSwitchProgress public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) { - return LoadScene(sceneName, loadSceneMode, SceneEventData.SceneEventTypes.EventSwitch); + return LoadScene(sceneName, loadSceneMode); } /// @@ -554,7 +553,7 @@ private void OnServerLoadedScene() { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteObjectPacked(SceneEventData); + SceneEventData.OnWrite(writer); uint sceneObjectsToSpawn = 0; @@ -610,9 +609,8 @@ private void OnClientLoadedScene() using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - // NSS TODO: This is part of decoupling the concept of "scene switching" and just distinguishing between single and additive scene loading modes - SceneEventData.SceneEventType = SceneEventData.LoadSceneMode == LoadSceneMode.Single ? SceneEventData.SceneEventTypes.Event_Switch_Complete : SceneEventData.SceneEventTypes.Event_Load_Complete; - writer.WriteObjectPacked(SceneEventData); + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Load_Complete; + SceneEventData.OnWrite(writer); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -640,10 +638,10 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) } } - ClientSynchEventData.InitializeForSynch(m_NetworkManager); + ClientSynchEventData.InitializeForSynch(); ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); - ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.EventSync; + ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync; for (int i = 0; i < SceneManager.sceneCount; i++) { @@ -661,9 +659,16 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.SceneIndex = malpiSceneIndex; } + // Separate NetworkObjects by scene foreach (var networkObject in m_ObservedObjects) { - if (networkObject.gameObject.scene != scene) + // If the current scene we are matching NetworkObjects to does not match and this NetworkObject has no dependent scene, then continue. + if (networkObject.gameObject.scene != scene && (networkObject.DependentSceneName == null || networkObject.DependentSceneName == string.Empty)) + { + continue; + } + else // If this NetworkObject has a dependent scene and the current scene is not the dependent scene, then continue + if (networkObject.DependentSceneName != null && networkObject.DependentSceneName != string.Empty && networkObject.DependentSceneName != scene.name) { continue; } @@ -675,7 +680,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) using (var buffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteObjectPacked(ClientSynchEventData); + ClientSynchEventData.OnWrite(writer); m_NetworkManager.MessageSender.Send(ownerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } @@ -683,7 +688,8 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) /// /// This is called when the client receives the SCENE_EVENT of type SceneEventData.SceneEventTypes.SYNC - /// Note: This can be invoked several times recursively by the client (depending upon how many additive scenes need to be loaded + /// Note: This can recurse one additional time by the client if the current scene loaded by the client + /// is already loaded. /// /// MLAPI sceneIndex to load private void OnClientBeginSynch(uint sceneIndex) @@ -846,18 +852,18 @@ private void HandleClientSceneEvent(Stream stream) switch (SceneEventData.SceneEventType) { // Both events are basically the same with some minor differences - case SceneEventData.SceneEventTypes.EventSwitch: - case SceneEventData.SceneEventTypes.EventLoad: + //case SceneEventData.SceneEventTypes.EventSwitch: + case SceneEventData.SceneEventTypes.Event_Load: { OnClientSceneLoadingEvent(stream); break; } - case SceneEventData.SceneEventTypes.EventUnload: + case SceneEventData.SceneEventTypes.Event_Unload: { OnClientUnloadScene(); break; } - case SceneEventData.SceneEventTypes.EventSync: + case SceneEventData.SceneEventTypes.Event_Sync: { if (!SceneEventData.IsDoneWithSynchronization()) { @@ -872,12 +878,16 @@ private void HandleClientSceneEvent(Stream stream) using (var writer = PooledNetworkWriter.Get(buffer)) { SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync_Complete; - writer.WriteObjectPacked(SceneEventData); + SceneEventData.OnWrite(writer); m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } } break; } + case SceneEventData.SceneEventTypes.Event_ReSync: + { + break; + } default: { Debug.LogWarning($"{SceneEventData.SceneEventType} is not currently supported!"); @@ -895,15 +905,17 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { switch (SceneEventData.SceneEventType) { - case SceneEventData.SceneEventTypes.Event_Switch_Complete: - { - OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); - break; - } case SceneEventData.SceneEventTypes.Event_Load_Complete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + { + OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); + } + else + { + //Other message? + } break; } case SceneEventData.SceneEventTypes.Event_Unload_Complete: @@ -913,6 +925,17 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } case SceneEventData.SceneEventTypes.Event_Sync_Complete: { + if(SceneEventData.ClientNeedsReSynchronization()) + { + Debug.Log($"Re-Synchronizing client {clientId} for missed destroyed NetworkObjects."); + using (var buffer = PooledNetworkBuffer.Get()) + using (var writer = PooledNetworkWriter.Get(buffer)) + { + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_ReSync; + SceneEventData.OnWrite(writer); + m_NetworkManager.MessageSender.Send(clientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + } + } // NSS TOOD: The scene event local notification needs to be called //m_NetworkManager.NotifyPlayerConnected(clientId); break; @@ -937,9 +960,8 @@ public void HandleSceneEvent(ulong clientId, Stream stream) if (stream != null) { var reader = NetworkReaderPool.GetReader(stream); - SceneEventData = (SceneEventData)reader.ReadObjectPacked(typeof(SceneEventData)); + SceneEventData.OnRead(reader); NetworkReaderPool.PutBackInPool(reader); - if (SceneEventData.IsSceneEventClientSide()) { HandleClientSceneEvent(stream); diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 847ddc8e3d..1b717bc8f0 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -11,16 +11,15 @@ namespace MLAPI.SceneManagement { [Serializable] - public class SceneEventData : INetworkSerializable, IDisposable + public class SceneEventData : IDisposable { public enum SceneEventTypes { - EventSwitch, //Server to client full scene switch (i.e. single mode and destroy everything) - EventLoad, //Server to client load additive scene - EventUnload, //Server to client unload additive scene - EventSetActive, //Server to client make scene active - EventSync, //Server to client late join approval synchronization - Event_Switch_Complete, //Client to server + Event_Load, //Server to client load additive scene + Event_Unload, //Server to client unload additive scene + Event_SetActive, //Server to client make scene active + Event_Sync, //Server to client late join approval synchronization + Event_ReSync, //Server to client update of objects that were destroyed during sync Event_Load_Complete, //Client to server Event_Unload_Complete, //Client to server Event_SetActiveComplete, //Client to server @@ -36,6 +35,23 @@ public enum SceneEventTypes private Dictionary> m_SceneNetworkObjects; private Dictionary m_SceneNetworkObjectDataOffsets; + + /// + /// Client or Server Side: + /// Client side: Generates a list of all NetworkObjects by their NetworkObjectId that was spawned during th synchronization process + /// Server side: Compares list from client to make sure client didn't drop a message about a NetworkObject being despawned while it + /// was synchronizing (if so server will send another message back to the client informing the client of NetworkObjects to remove) + /// spawned during an initial synchronization. + /// + private List m_NetworkObjectsSync = new List(); + + /// + /// Server Side Re-Synchronization: + /// If there happens to be NetworkObjects in the final Event_Sync_Complete message that are no longer spawned, + /// the server will compile a list and send back an Event_ReSync message to the client. + /// + private List m_NetworkObjectsToBeRemoved = new List(); + internal PooledNetworkBuffer InternalBuffer; private NetworkManager m_NetworkManager; @@ -68,9 +84,8 @@ public bool IsDoneWithSynchronization() /// Server Side: /// Called just before the synchronization process /// - public void InitializeForSynch(NetworkManager networkManager) + public void InitializeForSynch() { - m_NetworkManager = networkManager; if (m_SceneNetworkObjects == null) { m_SceneNetworkObjects = new Dictionary>(); @@ -105,10 +120,10 @@ public bool IsSceneEventClientSide() { switch (SceneEventType) { - case SceneEventTypes.EventLoad: - case SceneEventTypes.EventSwitch: - case SceneEventTypes.EventUnload: - case SceneEventTypes.EventSync: + case SceneEventTypes.Event_Load: + case SceneEventTypes.Event_Unload: + case SceneEventTypes.Event_Sync: + case SceneEventTypes.Event_ReSync: { return true; } @@ -142,23 +157,23 @@ private int SortNetworkObjects(NetworkObject first, NetworkObject second) } /// - /// Serializes this class instance + /// Serializes data based on the SceneEvent type. /// /// - private void OnWrite(NetworkWriter writer) + public void OnWrite(NetworkWriter writer) { writer.WriteByte((byte)SceneEventType); writer.WriteByte((byte)LoadSceneMode); - if (SceneEventType != SceneEventTypes.EventSync) + if (SceneEventType != SceneEventTypes.Event_Sync) { writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); } writer.WriteUInt32Packed(SceneIndex); - if (SceneEventType == SceneEventTypes.EventSync) + if (SceneEventType == SceneEventTypes.Event_Sync) { writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); @@ -201,13 +216,23 @@ private void OnWrite(NetworkWriter writer) Debug.Log(msg); } } + + if (SceneEventType == SceneEventTypes.Event_Sync_Complete) + { + WriteClientSynchronizationResults(writer); + } + + if (SceneEventType == SceneEventTypes.Event_ReSync) + { + WriteClientReSynchronizationData(writer); + } } /// - /// Deserialize this class instance + /// Deserialize data based on the SceneEvent type. /// /// - private void OnRead(NetworkReader reader) + public void OnRead(NetworkReader reader) { var sceneEventTypeValue = reader.ReadByte(); @@ -233,15 +258,16 @@ private void OnRead(NetworkReader reader) // NSS TODO: Add to proposal's MTT discussion topics: Should we assert here? } - if (SceneEventType != SceneEventTypes.EventSync) + if (SceneEventType != SceneEventTypes.Event_Sync) { SwitchSceneGuid = new Guid(reader.ReadByteArray()); } SceneIndex = reader.ReadUInt32Packed(); - if (SceneEventType == SceneEventTypes.EventSync) + if (SceneEventType == SceneEventTypes.Event_Sync) { + m_NetworkObjectsSync.Clear(); var keyPairCount = reader.ReadInt32Packed(); if (m_SceneNetworkObjectDataOffsets == null) @@ -271,8 +297,134 @@ private void OnRead(NetworkReader reader) } } } + + if (SceneEventType == SceneEventTypes.Event_Sync_Complete) + { + CheckClientSynchronizationResults(reader); + } + + if (SceneEventType == SceneEventTypes.Event_ReSync) + { + ReadClientReSynchronizationData(reader); + } + } + + /// + /// Client Side: + /// If there happens to be NetworkObjects in the final Event_Sync_Complete message that are no longer spawned, + /// the server will compile a list and send back an Event_ReSync message to the client. + /// + /// + internal void ReadClientReSynchronizationData(NetworkReader reader) + { + var networkObjectsToRemove = reader.ReadULongArrayPacked(); + + if(networkObjectsToRemove.Length > 0) + { + Debug.Log($"Client {NetworkManager.Singleton.LocalClientId} is being re-synchronized."); + var networkObjects = UnityEngine.Object.FindObjectsOfType(); + var networkObjectIdToNetworkObject = new Dictionary(); + foreach(var networkObject in networkObjects) + { + if(!networkObjectIdToNetworkObject.ContainsKey(networkObject.NetworkObjectId)) + { + networkObjectIdToNetworkObject.Add(networkObject.NetworkObjectId, networkObject); + } + } + + foreach(var networkObjectId in networkObjectsToRemove) + { + if (networkObjectIdToNetworkObject.ContainsKey(networkObjectId)) + { + var networkObject = networkObjectIdToNetworkObject[networkObjectId]; + networkObjectIdToNetworkObject.Remove(networkObjectId); + + networkObject.IsSpawned = false; + if (m_NetworkManager.PrefabHandler.ContainsHandler(networkObject)) + { + Debug.Log($"NetworkObjectId {networkObjectId} marked as not spawned and is being destroyed via prefab handler."); + NetworkManager.Singleton.PrefabHandler.HandleNetworkPrefabDestroy(networkObject); + } + else + { + Debug.Log($"NetworkObjectId {networkObjectId} marked as not spawned and is being destroyed immediately."); + UnityEngine.Object.DestroyImmediate(networkObject.gameObject); + } + } + } + } + } + + /// + /// Server Side: + /// If there happens to be NetworkObjects in the final Event_Sync_Complete message that are no longer spawned, + /// the server will compile a list and send back an Event_ReSync message to the client. + /// + /// + internal void WriteClientReSynchronizationData(NetworkWriter writer) + { + //Write how many objects need to be removed + writer.WriteULongArrayPacked(m_NetworkObjectsToBeRemoved.ToArray()); + } + + /// + /// Server Side: + /// Determines if the client needs to be slightly re-synchronized if during the deserialization + /// process the server finds NetworkObjects that the client still thinks are spawned. + /// + /// + public bool ClientNeedsReSynchronization() + { + return (m_NetworkObjectsToBeRemoved.Count > 0); + } + + /// + /// Server Side: + /// Determines if the client needs to be re-synchronized if during the deserialization + /// process the server finds NetworkObjects that the client still thinks are spawned but + /// have since been despawned. + /// + /// + internal void CheckClientSynchronizationResults(NetworkReader reader) + { + m_NetworkObjectsToBeRemoved.Clear(); + var networkObjectIdCount = reader.ReadUInt32Packed(); + for(int i = 0; i < networkObjectIdCount; i++) + { + var networkObjectId = (ulong)reader.ReadUInt32Packed(); + if(!m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) + { + m_NetworkObjectsToBeRemoved.Add(networkObjectId); + } + } } + /// + /// Client Side: + /// During the deserialization process of the servers Event_Sync, the client builds a list of + /// all NetworkObjectIds that were spawned. Upon responding to the server with the Event_Sync_Complete + /// this list is included for the server to review to determine if the client needs a minor resynchronization + /// of NetworkObjects that might have been despawned while the client was processing the Event_Sync. + /// + /// + internal void WriteClientSynchronizationResults(NetworkWriter writer) + { + //Write how many objects were spawned + writer.WriteUInt32Packed((uint)m_NetworkObjectsSync.Count); + foreach (var networkObject in m_NetworkObjectsSync) + { + writer.WriteUInt32Packed((uint)networkObject.NetworkObjectId); + } + } + + /// + /// Client Side: + /// During the processing of a server sent Event_Sync, this method will be called for each scene once + /// it is finished loading. The client will also build a list of NetworkObjects that it spawned during + /// this process which will be used as part of the Event_Sync_Complete response. + /// + /// + /// public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkManager) { if (m_SceneNetworkObjectDataOffsets.ContainsKey(sceneId)) @@ -287,7 +439,11 @@ public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkM for (int i = 0; i < newObjectsCount; i++) { - NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); + var spawnedNetworkObject = NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); + if(!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) + { + m_NetworkObjectsSync.Add(spawnedNetworkObject); + } } } @@ -296,22 +452,6 @@ public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkM } } - /// - /// INetworkSerializable implementation method for SceneEventData - /// - /// serializer passed in during serialization - public void NetworkSerialize(NetworkSerializer serializer) - { - if (serializer.IsReading) - { - OnRead(serializer.Reader); - } - else - { - OnWrite(serializer.Writer); - } - } - /// /// Used to store data during an asynchronous scene loading event /// @@ -338,8 +478,9 @@ public void Dispose() /// /// Constructor /// - public SceneEventData() + public SceneEventData(NetworkManager networkManager) { + m_NetworkManager = networkManager; InternalBuffer = NetworkBufferPool.GetBuffer(); } } diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index 0f0ce56aa7..ed9c08bbb2 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -116,7 +116,7 @@ private IEnumerator SceneEventCoroutine(bool isLoading) { if (isLoading) { - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad); + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad,UnityEngine.SceneManagement.LoadSceneMode.Additive); } else { diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 9ded7e1a66..f3b54fa5b9 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -780,6 +780,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} m_Name: m_EditorClassIdentifier: + m_ActivateOnLoad: 0 m_SceneToLoad: AdditiveScene2 m_SceneAsset: {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} --- !u!1 &300124661 @@ -1366,6 +1367,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} m_Name: m_EditorClassIdentifier: + m_ActivateOnLoad: 0 m_SceneToLoad: AdditiveScene1 m_SceneAsset: {fileID: 102900000, guid: 41a0239b0c49e2047b7063c822f0df8a, type: 3} --- !u!850595691 &903034822 @@ -1552,7 +1554,7 @@ MonoBehaviour: EnsureNetworkVariableLengthSafety: 0 EnableSceneManagement: 1 ForceSamePrefabs: 1 - RecycleNetworkIds: 1 + RecycleNetworkIds: 0 NetworkIdRecycleDelay: 120 RpcHashSize: 0 LoadSceneTimeOut: 120 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index 61e03486f2..2e1af8e1b0 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -112,7 +112,7 @@ public void OnSwitchScene() { OnSceneSwitchBegin?.Invoke(); - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]); + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo[m_CurrentSceneIndex], UnityEngine.SceneManagement.LoadSceneMode.Additive); m_CurrentSceneIndex++; m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; if(m_CurrentSceneIndex == m_SceneToSwitchTo.Count) diff --git a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs index 16d4bfb9cb..f8d616b2c3 100644 --- a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs +++ b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs @@ -63,50 +63,23 @@ private void FixedUpdate() { Debug.LogWarning($"{nameof(GenericNetworkObjectBehaviour)} id {NetworkObject.NetworkObjectId} is not active and enabled but game object is still active!"); } - - if (NetworkObject != null && !NetworkObject.IsSpawned) - { - Debug.LogWarning($"{nameof(GenericNetworkObjectBehaviour)} id {NetworkObject.NetworkObjectId} is not spawned but still active and enabled"); - } } } } + /// + /// Tells us that we are registered with a NetworkPefab pool + /// This is primarily for late joining clients and object synchronization. + /// public bool IsRegisteredPoolObject; + /// + /// This tells us that the NetworkObject has been removed from a pool + /// This is primarily to handle NetworkPrefab pool that was loaded in an additive scene and the + /// additive scene was unloaded but the NetworkObject persisted (i.e. was spawned in a different scene) + /// public bool IsRemovedFromPool; - public bool DetectedMissedDespawn; - - public float TimeToWaitUntilDestroy; - - private void LateUpdate() - { - if (!IsOwner) - { - if (!NetworkObject.IsSpawned) - { - if (IsRegisteredPoolObject && !IsRemovedFromPool) - { - if (!DetectedMissedDespawn) - { - DetectedMissedDespawn = true; - TimeToWaitUntilDestroy = Time.realtimeSinceStartup + 1.0f; - } - else if (TimeToWaitUntilDestroy < Time.realtimeSinceStartup) - { - Debug.Log($"Destroying {gameObject.name} via {nameof(GenericNetworkObjectBehaviour)}."); - Destroy(gameObject); - } - } - } - else if (IsRegisteredPoolObject && DetectedMissedDespawn) - { - DetectedMissedDespawn = false; - } - } - } - private void OnTriggerEnter(Collider other) { if (IsOwner) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 3c8d33ec7e..280bcd861e 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -289,12 +289,22 @@ public GameObject GetObject() private GameObject AddNewInstance() { var obj = Instantiate(m_ObjectToSpawn); - var no = obj.GetComponent(); var genericBehaviour = obj.GetComponent(); if (genericBehaviour) { genericBehaviour.IsRegisteredPoolObject = true; } + else + { + // If your spawn generator is not in the target active scene, then to properly synchronize your NetworkObjects + // for late joining players you **must** set the scene that the NetworkObject depends on + // (i.e. NetworkObjet pool with custom Network Prefab Handler) + if (gameObject.scene != UnityEngine.SceneManagement.SceneManager.GetActiveScene()) + { + var networkObject = obj.GetComponent(); + networkObject.SetSceneAsDependency(gameObject.scene.name); + } + } obj.SetActive(false); m_ObjectPool.Add(obj); return obj; @@ -425,9 +435,9 @@ public void HandleNetworkPrefabDestroy(NetworkObject networkObject) networkObject.transform.position = Vector3.zero; networkObject.gameObject.SetActive(false); } - else if (genericBehaviour.IsRemovedFromPool) + else { - Object.DestroyImmediate(networkObject.gameObject); + Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered..."); } } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index d3358f540d..723be0977c 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -284,7 +284,6 @@ public GameObject GetObject() private GameObject AddNewInstance() { var obj = Instantiate(m_ObjectToSpawn); - var no = obj.GetComponent(); var genericBehaviour = obj.GetComponent(); if (genericBehaviour) { @@ -295,8 +294,21 @@ private GameObject AddNewInstance() // Example of how to keep your pooled NetworkObjects in the same scene as your spawn generator (additive scenes only) if (SpawnInSourceScene && gameObject.scene != null) { + // If you move your NetworkObject into the same scene as the spawn generator, then you do not need to worry + // about setting the NetworkObject's scene dependency. SceneManager.MoveGameObjectToScene(obj, gameObject.scene); } + else // Otherwise, instantiate in the currently active scene + { + // If your spawn generator is not in the target active scene, then to properly synchronize your NetworkObjects + // for late joining players you **must** set the scene that the NetworkObject depends on + // (i.e. NetworkObjet pool with custom Network Prefab Handler) + if (gameObject.scene != SceneManager.GetActiveScene()) + { + var networkObject = obj.GetComponent(); + networkObject.SetSceneAsDependency(gameObject.scene.name); + } + } obj.SetActive(false); @@ -422,7 +434,7 @@ public void HandleNetworkPrefabDestroy(NetworkObject networkObject) } else { - Object.DestroyImmediate(networkObject.gameObject); + Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered..."); } } From 55ffbd3b48cccba178e2a72e1a3d9f3bbf21a188 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 20 Jul 2021 08:58:41 -0500 Subject: [PATCH 028/106] style comments and naming --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 018e613718..0a31237bdf 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -86,6 +86,8 @@ public class NetworkSceneManager // Used for observed object synchronization private readonly List m_ObservedObjects = new List(); + private List m_ScenesLoaded = new List(); + private static bool s_IsSceneEventActive = false; internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; internal static bool IsRunningUnitTest = false; @@ -328,11 +330,8 @@ private void OnSceneUnloaded() s_IsSceneEventActive = false; } - - private List m_ScenesLoaded = new List(); - /// - /// Additively loads the scene + /// Loads the scene name in question as either additive or single. /// /// /// NSS TODO: This could probably stand to have some form of "scene event status" class/structure that will @@ -619,7 +618,6 @@ private void OnClientLoadedScene() OnSceneSwitched?.Invoke(); } - /// /// Server Side Only: /// This is used for late joining players and players that have just had their connection approved @@ -692,7 +690,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) /// is already loaded. /// /// MLAPI sceneIndex to load - private void OnClientBeginSynch(uint sceneIndex) + private void OnClientBeginSync(uint sceneIndex) { if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) { @@ -748,6 +746,7 @@ private void ClientLoadedSynchronization(uint sceneIndex) HandleClientSceneEvent(null); } + #region General Methods internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; internal void RemoveClientFromSceneSwitchProgresses(ulong clientId) @@ -842,6 +841,7 @@ internal void OnClientSceneLoadingEventCompleted(ulong clientId, Guid switchScen SceneSwitchProgresses[switchSceneGuid].AddClientAsDone(clientId); } } + #endregion /// /// Client Side: Handles incoming SCENE_EVENT messages @@ -867,7 +867,7 @@ private void HandleClientSceneEvent(Stream stream) { if (!SceneEventData.IsDoneWithSynchronization()) { - OnClientBeginSynch(SceneEventData.GetNextSceneSynchronizationIndex()); + OnClientBeginSync(SceneEventData.GetNextSceneSynchronizationIndex()); } else { From c890f06ce8cc266b9eb742391ef4bb3045a95c32 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 21 Jul 2021 14:38:01 -0500 Subject: [PATCH 029/106] Refactor and Fix Fixed issue with rogue dynamically spawned NetowrkObjects that were spawned during a client scene synchronization sequence where it required a Custom NetworkPrefab hanler to spawn properly but the scene containing that custom network prefab handler had yet to be loaded so it was never associated with the pool. Migrated the additive scene loading scene transitioning tests into the manual tests region of TestProject --- .../{Samples => Tests/Manual}/SceneTransitioningAdditive.meta | 0 .../Manual}/SceneTransitioningAdditive/AdditiveScene1.unity | 0 .../SceneTransitioningAdditive/AdditiveScene1.unity.meta | 0 .../Manual}/SceneTransitioningAdditive/AdditiveScene2.unity | 0 .../SceneTransitioningAdditive/AdditiveScene2.unity.meta | 0 .../Manual}/SceneTransitioningAdditive/AdditiveScene3.unity | 3 ++- .../SceneTransitioningAdditive/AdditiveScene3.unity.meta | 0 .../Manual}/SceneTransitioningAdditive/AdditiveScene4.unity | 3 ++- .../SceneTransitioningAdditive/AdditiveScene4.unity.meta | 0 .../SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs | 0 .../AdditiveSceneToggleHandler.cs.meta | 0 .../SceneTransitioningAdditive/NetworkManagerMonitor.cs | 0 .../SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta | 0 .../SceneTransitioningAdditive/SceneTransitioningBase1.unity | 2 +- .../SceneTransitioningBase1.unity.meta | 0 .../SceneTransitioningAdditive/SceneTransitioningBase2.unity | 0 .../SceneTransitioningBase2.unity.meta | 0 .../SceneTransitioningBaseSceneRegistration.asset | 0 .../SceneTransitioningBaseSceneRegistration.asset.meta | 0 .../SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs | 0 .../SwitchSceneHandlerAdditive.cs.meta | 0 testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs | 3 ++- .../Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs | 3 ++- 23 files changed, 9 insertions(+), 5 deletions(-) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene1.unity (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene1.unity.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene2.unity (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene2.unity.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene3.unity (99%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene3.unity.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene4.unity (99%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveScene4.unity.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/NetworkManagerMonitor.cs (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SceneTransitioningBase1.unity (99%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SceneTransitioningBase1.unity.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SceneTransitioningBase2.unity (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs (100%) rename testproject/Assets/{Samples => Tests/Manual}/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta (100%) diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene2.unity similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene2.unity diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene2.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene2.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity similarity index 99% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity index 06d7eb747c..bd5e1f182f 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity @@ -246,8 +246,9 @@ MonoBehaviour: m_EditorClassIdentifier: RandomMovement: 0 AutoSpawnEnable: 1 + SpawnInSourceScene: 1 InitialSpawnDelay: 0.2 - SpawnsPerSecond: 1 + SpawnsPerSecond: 3 PoolSize: 32 ObjectSpeed: 8 EnableHandler: 1 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene4.unity similarity index 99% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene4.unity index 3eeae9adc4..16d9e311c2 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene4.unity @@ -246,8 +246,9 @@ MonoBehaviour: m_EditorClassIdentifier: RandomMovement: 0 AutoSpawnEnable: 1 + SpawnInSourceScene: 1 InitialSpawnDelay: 0.2 - SpawnsPerSecond: 1 + SpawnsPerSecond: 3 PoolSize: 32 ObjectSpeed: 8 EnableHandler: 1 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene4.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene4.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity similarity index 99% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity index f3b54fa5b9..8c0e1b7110 100644 --- a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -1554,7 +1554,7 @@ MonoBehaviour: EnsureNetworkVariableLengthSafety: 0 EnableSceneManagement: 1 ForceSamePrefabs: 1 - RecycleNetworkIds: 0 + RecycleNetworkIds: 1 NetworkIdRecycleDelay: 120 RpcHashSize: 0 LoadSceneTimeOut: 120 diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBaseSceneRegistration.asset.meta diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs diff --git a/testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta similarity index 100% rename from testproject/Assets/Samples/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta rename to testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs.meta diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 280bcd861e..619bf45cab 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -437,7 +437,8 @@ public void HandleNetworkPrefabDestroy(NetworkObject networkObject) } else { - Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered..."); + Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); + Object.DestroyImmediate(networkObject.gameObject); } } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 723be0977c..24f6298ad3 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -434,7 +434,8 @@ public void HandleNetworkPrefabDestroy(NetworkObject networkObject) } else { - Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered..."); + Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); + Object.DestroyImmediate(networkObject.gameObject); } } From a1fdf5bb4df736192a9e0e31c9cc684285f6543b Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:59:09 -0500 Subject: [PATCH 030/106] FIx and Style Fix for client side where during resync the GameObject was being destroyed but the spawn manager's lists tracking spawned NetworkObjects was not being updated. The symptom was when a NetworkObjectId was recycled for a new NetoworkObject the client would still have that NetworkObjectId registered. This also includes some style updates. --- .../SceneManagement/NetworkSceneManager.cs | 14 +++++------ .../Runtime/SceneManagement/SceneEventData.cs | 25 +++++++++++++------ .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 0a31237bdf..f99f734e4c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -279,7 +279,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneUnload); - if(m_ScenesLoaded.Contains(sceneName)) + if (m_ScenesLoaded.Contains(sceneName)) { m_ScenesLoaded.Remove(sceneName); } @@ -400,7 +400,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene { foreach (var additiveSceneName in m_ScenesLoaded) { - if(currentActiveScene.name != additiveSceneName) + if (currentActiveScene.name != additiveSceneName) { SceneManager.UnloadSceneAsync(additiveSceneName); } @@ -411,7 +411,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene } else { - if(!m_ScenesLoaded.Contains(sceneName)) + if (!m_ScenesLoaded.Contains(sceneName)) { m_ScenesLoaded.Add(sceneName); } @@ -446,9 +446,9 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) var currentActiveScene = SceneManager.GetActiveScene(); if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - foreach(var loadedSceneName in m_ScenesLoaded) + foreach (var loadedSceneName in m_ScenesLoaded) { - if(currentActiveScene.name != loadedSceneName) + if (currentActiveScene.name != loadedSceneName) { SceneManager.UnloadSceneAsync(loadedSceneName); } @@ -647,7 +647,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) var malpiSceneIndex = GetMLAPISceneIndexFromScene(scene); - if( malpiSceneIndex == uint.MaxValue) + if (malpiSceneIndex == uint.MaxValue) { continue; } @@ -925,7 +925,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } case SceneEventData.SceneEventTypes.Event_Sync_Complete: { - if(SceneEventData.ClientNeedsReSynchronization()) + if (SceneEventData.ClientNeedsReSynchronization()) { Debug.Log($"Re-Synchronizing client {clientId} for missed destroyed NetworkObjects."); using (var buffer = PooledNetworkBuffer.Get()) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 1b717bc8f0..d8538ab6ce 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -319,20 +319,20 @@ internal void ReadClientReSynchronizationData(NetworkReader reader) { var networkObjectsToRemove = reader.ReadULongArrayPacked(); - if(networkObjectsToRemove.Length > 0) + if (networkObjectsToRemove.Length > 0) { Debug.Log($"Client {NetworkManager.Singleton.LocalClientId} is being re-synchronized."); var networkObjects = UnityEngine.Object.FindObjectsOfType(); var networkObjectIdToNetworkObject = new Dictionary(); - foreach(var networkObject in networkObjects) + foreach (var networkObject in networkObjects) { - if(!networkObjectIdToNetworkObject.ContainsKey(networkObject.NetworkObjectId)) + if (!networkObjectIdToNetworkObject.ContainsKey(networkObject.NetworkObjectId)) { networkObjectIdToNetworkObject.Add(networkObject.NetworkObjectId, networkObject); } } - foreach(var networkObjectId in networkObjectsToRemove) + foreach (var networkObjectId in networkObjectsToRemove) { if (networkObjectIdToNetworkObject.ContainsKey(networkObjectId)) { @@ -343,6 +343,17 @@ internal void ReadClientReSynchronizationData(NetworkReader reader) if (m_NetworkManager.PrefabHandler.ContainsHandler(networkObject)) { Debug.Log($"NetworkObjectId {networkObjectId} marked as not spawned and is being destroyed via prefab handler."); + // Since this is the client side and we have missed the delete message, until the Snapshot system is in place for spawn and despawn handling + // we have to remove this from the list of spawned objects manually or when a NetworkObjectId is recycled the client will throw an error + // about the id already being assigned. + if (m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) + { + m_NetworkManager.SpawnManager.SpawnedObjects.Remove(networkObjectId); + } + if (m_NetworkManager.SpawnManager.SpawnedObjectsList.Contains(networkObject)) + { + m_NetworkManager.SpawnManager.SpawnedObjectsList.Remove(networkObject); + } NetworkManager.Singleton.PrefabHandler.HandleNetworkPrefabDestroy(networkObject); } else @@ -389,10 +400,10 @@ internal void CheckClientSynchronizationResults(NetworkReader reader) { m_NetworkObjectsToBeRemoved.Clear(); var networkObjectIdCount = reader.ReadUInt32Packed(); - for(int i = 0; i < networkObjectIdCount; i++) + for (int i = 0; i < networkObjectIdCount; i++) { var networkObjectId = (ulong)reader.ReadUInt32Packed(); - if(!m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) + if (!m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) { m_NetworkObjectsToBeRemoved.Add(networkObjectId); } @@ -440,7 +451,7 @@ public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkM for (int i = 0; i < newObjectsCount; i++) { var spawnedNetworkObject = NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); - if(!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) + if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) { m_NetworkObjectsSync.Add(spawnedNetworkObject); } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 619bf45cab..0af32bbd68 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -337,7 +337,7 @@ public void UpdateSpawnsPerSecond() SpawnSliderValueText.text = SpawnsPerSecond.ToString(); // Handle case where the initial value is set to zero and so coroutine needs to be started - if(SpawnsPerSecond > 0 && !m_IsSpawningObjects) + if (SpawnsPerSecond > 0 && !m_IsSpawningObjects) { StartSpawningBoxes(); } From 28545c3e7aaa6fbe19aea5701455cdecc4e8ae75 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 27 Jul 2021 08:07:12 -0500 Subject: [PATCH 031/106] refactor and fix Refactoring things based on more recent changes. Predominantly the Message Ordering changes and the time synchronization. --- .../Runtime/Core/NetworkBehaviour.cs | 2 +- .../Runtime/Core/NetworkManager.cs | 58 ++++---- .../Messaging/InternalMessageHandler.cs | 8 +- .../MessageQueue/MessageQueueContainer.cs | 9 +- .../MessageQueue/MessageQueueHistoryFrame.cs | 2 +- .../MessageQueue/MessageQueueProcessor.cs | 19 +-- .../SceneManagement/NetworkSceneManager.cs | 135 +++++++++--------- .../Runtime/Transports/NetworkTransport.cs | 2 +- .../NetworkManagerMessageHandlerTests.cs | 18 +-- testproject/Assets/Prefabs/Player.prefab | 15 +- testproject/Assets/Prefabs/SpawnObject.prefab | 43 +----- .../SceneTransitioningTest.unity | 6 +- .../SceneTransitioningBase1.unity | 10 +- .../Scripts/GenericNetworkObjectBehaviour.cs | 35 ++++- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 28 +--- 15 files changed, 156 insertions(+), 234 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index c29090378a..586adf4b69 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -589,7 +589,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) { using (var nonNullContext = (InternalCommandContext)context) { - nonNullContext.NetworkWriter.WriteBytes(buffer.GetBuffer(), buffer.Length); + nonNullContext.NetworkWriter.WriteBytes(buffer.GetBuffer(), buffer.Position); } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index cf5a961c09..9fcd0050dc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -64,7 +64,6 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem, IProfilableTr internal static bool UseSnapshot = false; private const double k_TimeSyncFrequency = 1.0d; // sync every second, TODO will be removed once timesync is done via snapshots - internal MessageQueueContainer MessageQueueContainer { get; private set; } @@ -114,7 +113,7 @@ public GameObject GetNetworkPrefabOverride(GameObject gameObject) } return gameObject; } - + public NetworkTimeSystem NetworkTimeSystem { get; private set; } public NetworkTickSystem NetworkTickSystem { get; private set; } @@ -186,7 +185,7 @@ public ulong LocalClientId /// /// Gets a list of just the IDs of all connected clients. /// - public ulong[] ConnectedClientsIds => ConnectedClientsList.Select(c => c.ClientId).ToArray(); + public ulong[] ConnectedClientsIds => ConnectedClientsList.Select(c => c.ClientId).Where(c => c != LocalClientId).ToArray(); /// /// Gets a dictionary of the clients that have been accepted by the transport but are still pending by the MLAPI. This is only populated on the server. @@ -1170,8 +1169,7 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, N private readonly NetworkBuffer m_InputBufferWrapper = new NetworkBuffer(new byte[0]); private readonly MessageBatcher m_MessageBatcher = new MessageBatcher(); - internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, ArraySegment data, - float receiveTime) + internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, ArraySegment data, float receiveTime) { #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleIncomingData.Begin(); @@ -1208,8 +1206,8 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, #endif } - private void ReceiveCallback(NetworkBuffer messageBuffer, MessageQueueContainer.MessageType messageType, - ulong clientId, float receiveTime, NetworkChannel receiveChannel) + private void ReceiveCallback(NetworkBuffer messageBuffer, MessageQueueContainer.MessageType messageType, ulong clientId, + float receiveTime, NetworkChannel receiveChannel) { MessageHandler.MessageReceiveQueueItem(clientId, messageBuffer, receiveTime, messageType, receiveChannel); } @@ -1431,13 +1429,18 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? // Don't send the CONNECTION_APPROVED message if this is the host that connected locally if (ownerClientId != ServerClientId) { + ulong[] clientIds = {ownerClientId}; + + var context = MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.ConnectionApproved, NetworkChannel.Internal, clientIds, + NetworkUpdateStage.EarlyUpdate); - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) + if (context != null) { - writer.WriteUInt64Packed(ownerClientId); - writer.WriteSinglePacked(Time.realtimeSinceStartup); - MessageSender.Send(ownerClientId, NetworkConstants.CONNECTION_APPROVED, NetworkChannel.Internal, buffer); + using (var nonNullContext = (InternalCommandContext) context) + { + nonNullContext.NetworkWriter.WriteUInt64Packed(ownerClientId); + nonNullContext.NetworkWriter.WriteInt32Packed(LocalTime.Tick); + } } // Now inform the newly joined client of the scenes to be loaded as well as synchronize it with all relevant in-scene and dynamically spawned NetworkObjects @@ -1473,24 +1476,23 @@ internal void NotifyPlayerConnected(ulong clientId, uint playerPrefabHash) { foreach (KeyValuePair clientPair in ConnectedClients) { - if (clientPair.Key == ownerClientId || + if (clientPair.Key == clientId || clientPair.Key == ServerClientId || // Server already spawned it - ConnectedClients[ownerClientId].PlayerObject == null || - !ConnectedClients[ownerClientId].PlayerObject.Observers.Contains(clientPair.Key)) + ConnectedClients[clientId].PlayerObject == null || + !ConnectedClients[clientId].PlayerObject.Observers.Contains(clientPair.Key)) { continue; //The new client. } - var context = MessageQueueContainer.EnterInternalCommandContext( - MessageQueueContainer.MessageType.CreateObject, NetworkChannel.Internal, + var context = MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.CreateObject, NetworkChannel.Internal, new[] {clientPair.Key}, NetworkUpdateLoop.UpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext)context) { nonNullContext.NetworkWriter.WriteBool(true); - nonNullContext.NetworkWriter.WriteUInt64Packed(ConnectedClients[ownerClientId].PlayerObject.NetworkObjectId); - nonNullContext.NetworkWriter.WriteUInt64Packed(ownerClientId); + nonNullContext.NetworkWriter.WriteUInt64Packed(ConnectedClients[clientId].PlayerObject.NetworkObjectId); + nonNullContext.NetworkWriter.WriteUInt64Packed(clientId); //Does not have a parent nonNullContext.NetworkWriter.WriteBool(false); @@ -1498,18 +1500,18 @@ internal void NotifyPlayerConnected(ulong clientId, uint playerPrefabHash) // This is not a scene object nonNullContext.NetworkWriter.WriteBool(false); - nonNullContext.NetworkWriter.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + nonNullContext.NetworkWriter.WriteUInt32Packed(playerPrefabHash); - if (ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning(ownerClientId)) + if (ConnectedClients[clientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[clientId].PlayerObject.IncludeTransformWhenSpawning(clientId)) { nonNullContext.NetworkWriter.WriteBool(true); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.x); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.y); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.position.z); + nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.x); + nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.y); + nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.z); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.x); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.y); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[ownerClientId].PlayerObject.transform.rotation.eulerAngles.z); + nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.x); + nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.y); + nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.z); } else { @@ -1520,7 +1522,7 @@ internal void NotifyPlayerConnected(ulong clientId, uint playerPrefabHash) if (NetworkConfig.EnableNetworkVariable) { - ConnectedClients[ownerClientId].PlayerObject.WriteNetworkVariableData(nonNullContext.NetworkWriter.GetStream(), clientPair.Key); + ConnectedClients[clientId].PlayerObject.WriteNetworkVariableData(nonNullContext.NetworkWriter.GetStream(), clientPair.Key); } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index d26307cba2..e0ecdfd3e2 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -74,8 +74,9 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv { NetworkManager.LocalClientId = reader.ReadUInt64Packed(); - float netTime = reader.ReadSinglePacked(); - NetworkManager.UpdateNetworkTime(clientId, netTime, receiveTime, true); + int tick = reader.ReadInt32Packed(); + var time = new NetworkTime(NetworkManager.NetworkTickSystem.TickRate, tick); + NetworkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport. NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); } @@ -197,9 +198,6 @@ public void HandleDestroyObjects(ulong clientId, Stream stream) public void HandleTimeSync(ulong clientId, Stream stream) { - - Assert.IsTrue(clientId == NetworkManager.ServerClientId); - using (var reader = PooledNetworkReader.Get(stream)) { int tick = reader.ReadInt32Packed(); diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs index fb118b3339..e2638ed72f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs @@ -40,8 +40,7 @@ public enum MessageType SnapshotData, SnapshotAck, NetworkVariableDelta, - SwitchScene, - ClientSwitchSceneCompleted, + SceneEvent, AllClientsLoadedScene, ParentSync, @@ -67,8 +66,8 @@ public enum MessageQueueProcessingTypes private int m_OutBoundStreamBufferIndex; private bool m_IsTestingEnabled; private bool m_ProcessUpdateStagesExternally; - private bool m_IsNotUsingBatching; - + private bool m_IsNotUsingBatching = true; //NSS REMOVE: Batching is broken currently, so I am disabling it. + // TODO hack: Fixed update can run multiple times in a frame and the queue history frame doesn't get cleared // until the end of the frame. This results in messages executing at FixedUpdate being invoked multiple times. // This is used to prevent it being called more than once per frame. @@ -135,7 +134,7 @@ public uint GetStreamBufferFrameCount(MessageQueueHistoryFrame.QueueFrameType qu return null; } - writer = BeginAddQueueItemToFrame(messageType, Time.realtimeSinceStartup, transportChannel, 0, + writer = BeginAddQueueItemToFrame(messageType, Time.realtimeSinceStartup, transportChannel, NetworkManager.LocalClientId, clientIds, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); writer.WriteByte((byte)messageType); diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs index 794ebe6c09..92255f8390 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs @@ -217,7 +217,7 @@ public void CloseQueue() if (m_CurrentItem.NetworkBuffer != null) { - m_CurrentItem.NetworkBuffer.Dispose(); + ((PooledNetworkBuffer)m_CurrentItem.NetworkBuffer).Dispose(); m_CurrentItem.NetworkBuffer = null; } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs index ab695c65ce..40a6f49a7e 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs @@ -128,23 +128,8 @@ public void ProcessMessage(in MessageFrameItem item) case MessageQueueContainer.MessageType.NetworkVariableDelta: m_NetworkManager.MessageHandler.HandleNetworkVariableDelta(item.NetworkId, item.NetworkBuffer); break; - case MessageQueueContainer.MessageType.SwitchScene: - if (m_NetworkManager.IsClient) - { - m_NetworkManager.MessageHandler.HandleSwitchScene(item.NetworkId, item.NetworkBuffer); - } - - break; - case MessageQueueContainer.MessageType.ClientSwitchSceneCompleted: - if (m_NetworkManager.IsServer && m_NetworkManager.NetworkConfig.EnableSceneManagement) - { - m_NetworkManager.MessageHandler.HandleClientSwitchSceneCompleted(item.NetworkId, item.NetworkBuffer); - } - else if (!m_NetworkManager.NetworkConfig.EnableSceneManagement) - { - NetworkLog.LogWarning($"Server received {MessageQueueContainer.MessageType.ClientSwitchSceneCompleted} from client id {item.NetworkId.ToString()}"); - } - + case MessageQueueContainer.MessageType.SceneEvent: + m_NetworkManager.MessageHandler.HandleSceneEvent(item.NetworkId, item.NetworkBuffer); break; case MessageQueueContainer.MessageType.AllClientsLoadedScene: if (m_NetworkManager.IsClient) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 0f03409e92..5bb10620a7 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -10,6 +10,7 @@ using UnityEngine.SceneManagement; using MLAPI.Serialization; using MLAPI.Transports; +using MLAPI.Messaging; namespace MLAPI.SceneManagement { @@ -100,13 +101,35 @@ public class NetworkSceneManager private NetworkManager m_NetworkManager { get; } + private MessageQueueContainer.MessageType m_MessageType = MessageQueueContainer.MessageType.SceneEvent; + private NetworkChannel m_ChannelType = NetworkChannel.Internal; + private NetworkUpdateStage m_NetworkUpdateStage = NetworkUpdateLoop.UpdateStage; + internal NetworkSceneManager(NetworkManager networkManager) { + m_NetworkManager = networkManager; SceneEventData = new SceneEventData(networkManager); ClientSynchEventData = new SceneEventData(networkManager); } + + internal void SendSceneEventData(ulong[] targetClientIds) + { + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, targetClientIds, m_NetworkUpdateStage); + + if (context != null) + { + using (var nonNullContext = (InternalCommandContext)context) + { + SceneEventData.OnWrite(nonNullContext.NetworkWriter); + } + + return; + } + throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientIds {targetClientIds}!"); + } + /// /// Returns the MLAPI scene index from a scene /// @@ -221,10 +244,10 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn switchSceneProgress.OnComplete += timedOut => { OnNotifyServerAllClientsLoadedScene?.Invoke(switchSceneProgress, timedOut); + // Send notification to all clients that everyone is done loading + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.AllClientsLoadedScene, NetworkChannel.Internal, + m_NetworkManager.ConnectedClientsIds, NetworkUpdateLoop.UpdateStage); - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - MessageQueueContainer.MessageType.AllClientsLoadedScene, NetworkChannel.Internal, - new[] {NetworkManager.Singleton.ServerClientId}, NetworkUpdateLoop.UpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext) context) @@ -241,6 +264,8 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn return switchSceneProgress; } + + /// /// Unloads an additively loaded scene /// @@ -266,17 +291,8 @@ public SceneSwitchProgress UnloadScene(string sceneName) SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; - for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++) - { - using (var buffer = PooledNetworkBuffer.Get()) - { - using (var writer = PooledNetworkWriter.Get(buffer)) - { - SceneEventData.OnWrite(writer); - m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); - } - } - } + // Sends the unload scene notification + SendSceneEventData(m_NetworkManager.ConnectedClientsIds); // start loading the scene AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); @@ -322,14 +338,11 @@ private void OnClientUnloadScene() /// private void OnSceneUnloaded() { - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) + if (!m_NetworkManager.IsServer) { SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Unload_Complete; - SceneEventData.OnWrite(writer); - m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); } - s_IsSceneEventActive = false; } @@ -551,35 +564,38 @@ private void OnServerLoadedScene() var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId; if (clientId != m_NetworkManager.ServerClientId) { - using (var buffer = PooledNetworkBuffer.Get()) - { - using (var writer = PooledNetworkWriter.Get(buffer)) - { - SceneEventData.OnWrite(writer); - - uint sceneObjectsToSpawn = 0; - foreach (var keyValuePair in ScenePlacedObjects) - { - if (keyValuePair.Value.Observers.Contains(clientId)) - { - sceneObjectsToSpawn++; - } - } + uint sceneObjectsToSpawn = 0; + foreach (var keyValuePair in ScenePlacedObjects) + { + if (keyValuePair.Value.Observers.Contains(clientId)) + { + sceneObjectsToSpawn++; + } + } + var clientIdAsArray = new ulong[] { m_NetworkManager.ConnectedClientsList[j].ClientId } ; + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, clientIdAsArray, m_NetworkUpdateStage); + if (context != null) + { + using (var nonNullContext = (InternalCommandContext)context) + { + SceneEventData.OnWrite(nonNullContext.NetworkWriter); // Write number of scene objects to spawn - writer.WriteUInt32Packed(sceneObjectsToSpawn); + nonNullContext.NetworkWriter.WriteUInt32Packed(sceneObjectsToSpawn); foreach (var keyValuePair in ScenePlacedObjects) { if (keyValuePair.Value.Observers.Contains(clientId)) { - keyValuePair.Value.SerializeSceneObject(writer, clientId); + keyValuePair.Value.SerializeSceneObject(nonNullContext.NetworkWriter, clientId); } } - - m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); } } + else + { + throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientId {clientIdAsArray}!"); + } } } @@ -608,15 +624,8 @@ private void OnClientLoadedScene() } } - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - MessageQueueContainer.MessageType.ClientSwitchSceneCompleted, NetworkChannel.Internal, - new[] {m_NetworkManager.ServerClientId}, NetworkUpdateLoop.UpdateStage); - if (context != null) - { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Load_Complete; - SceneEventData.OnWrite(writer); - m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); - } + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Load_Complete; + SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); s_IsSceneEventActive = false; @@ -678,17 +687,18 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.AddNetworkObjectForSynch(malpiSceneIndex, networkObject); } } - - // Send the scene event - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) + var clientIdAsArray = new ulong[] { ownerClientId }; + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, clientIdAsArray, m_NetworkUpdateStage); + if (context != null) { - ClientSynchEventData.OnWrite(writer); - m_NetworkManager.MessageSender.Send(ownerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); + using (var nonNullContext = (InternalCommandContext)context) + { + ClientSynchEventData.OnWrite(nonNullContext.NetworkWriter); + } } - } + /// /// This is called when the client receives the SCENE_EVENT of type SceneEventData.SceneEventTypes.SYNC /// Note: This can recurse one additional time by the client if the current scene loaded by the client @@ -879,13 +889,9 @@ private void HandleClientSceneEvent(Stream stream) // All scenes are synchronized, let the server know we are done synchronizing m_NetworkManager.IsConnectedClient = true; m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId); - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) - { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync_Complete; - SceneEventData.OnWrite(writer); - m_NetworkManager.MessageSender.Send(m_NetworkManager.ServerClientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); - } + + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync_Complete; + SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId}); } break; } @@ -933,13 +939,8 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) if (SceneEventData.ClientNeedsReSynchronization()) { Debug.Log($"Re-Synchronizing client {clientId} for missed destroyed NetworkObjects."); - using (var buffer = PooledNetworkBuffer.Get()) - using (var writer = PooledNetworkWriter.Get(buffer)) - { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_ReSync; - SceneEventData.OnWrite(writer); - m_NetworkManager.MessageSender.Send(clientId, NetworkConstants.SCENE_EVENT, NetworkChannel.Internal, buffer); - } + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_ReSync; + SendSceneEventData(new ulong[] { clientId }); } // NSS TOOD: The scene event local notification needs to be called //m_NetworkManager.NotifyPlayerConnected(clientId); diff --git a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs index b7cf3c4a00..2df259b73d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs @@ -103,7 +103,7 @@ public TransportChannel[] MLAPI_CHANNELS new TransportChannel(NetworkChannel.NavAgentCorrection, NetworkDelivery.UnreliableSequenced), // todo: Currently, fragmentation support needed to deal with oversize packets encounterable with current pre-snapshot code". // todo: once we have snapshotting able to deal with missing frame, this should be unreliable - new TransportChannel(NetworkChannel.NetworkVariable, NetworkDelivery.ReliableFragmentedSequenced), + new TransportChannel(NetworkChannel.NetworkVariable, NetworkDelivery.ReliableSequenced), new TransportChannel(NetworkChannel.SnapshotExchange, NetworkDelivery.ReliableFragmentedSequenced), // todo: temporary until we separate snapshots in chunks }; diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs index 65336ebe41..a272e05fb6 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/NetworkManagerMessageHandlerTests.cs @@ -84,6 +84,7 @@ public void MessageHandlerReceivedMessageServerClient() // Should not cause log (client only) // Everything should log MessageReceiveQueueItem even if ignored + LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleSceneEvent)); using (var messageStream = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.SceneEvent, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching())) { @@ -149,15 +150,6 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0); } - // Should cause log (server only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleClientSwitchSceneCompleted)); - using (var messageStream = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ClientSwitchSceneCompleted, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching())) - { - networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0); - } - // Should cause log (server only) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); @@ -287,14 +279,6 @@ public void MessageHandlerReceivedMessageServerClient() networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0); } - // Should not cause log (server only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using (var messageStream = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ClientSwitchSceneCompleted, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching())) - { - networkManager.HandleIncomingData(0, NetworkChannel.Internal, new ArraySegment(messageStream.GetBuffer(), 0, (int)messageStream.Length), 0); - } - // Should not cause log (server only) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); diff --git a/testproject/Assets/Prefabs/Player.prefab b/testproject/Assets/Prefabs/Player.prefab index b9f7e6b43b..58fca36e5b 100644 --- a/testproject/Assets/Prefabs/Player.prefab +++ b/testproject/Assets/Prefabs/Player.prefab @@ -51,17 +51,10 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: m_EditorClassIdentifier: - TransformAuthority: 1 + Authority: 1 + Channel: 0 + InLocalSpace: 0 FixedSendsPerSecond: 15 - InterpolatePosition: 1 - SnapDistance: 10 - InterpolateServer: 1 - MinMeters: 0.15 - MinDegrees: 1.5 - MinSize: 0.15 - Channel: 2 - m_UseLocal: - m_InternalValue: 0 --- !u!114 &-3775814466963834669 MonoBehaviour: m_ObjectHideFlags: 0 @@ -75,9 +68,9 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: GlobalObjectIdHash: 951099334 - m_MarkedAsSceneObject: 0 AlwaysReplicateAsRoot: 0 DontDestroyWithOwner: 0 + AutoObjectParentSync: 0 --- !u!33 &4079352819444256610 MeshFilter: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Prefabs/SpawnObject.prefab b/testproject/Assets/Prefabs/SpawnObject.prefab index f4a677ddfc..0396a36999 100644 --- a/testproject/Assets/Prefabs/SpawnObject.prefab +++ b/testproject/Assets/Prefabs/SpawnObject.prefab @@ -52,6 +52,7 @@ MonoBehaviour: GlobalObjectIdHash: 951099334 AlwaysReplicateAsRoot: 0 DontDestroyWithOwner: 0 + AutoObjectParentSync: 0 --- !u!33 &771575417923360817 MeshFilter: m_ObjectHideFlags: 0 @@ -143,7 +144,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: fa7bfe2bc280049f38557d643d8a3471, type: 3} m_Name: m_EditorClassIdentifier: - m_MoveRandomly: 1 + m_MoveRandomly: 0 + IsRegisteredPoolObject: 0 + IsRemovedFromPool: 0 --- !u!114 &4600632750638426092 MonoBehaviour: m_ObjectHideFlags: 0 @@ -156,39 +159,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: m_EditorClassIdentifier: + Authority: 0 + Channel: 0 + InLocalSpace: 0 FixedSendsPerSecond: 5 - AssumeSyncedSends: 1 - InterpolatePosition: 1 - SnapDistance: 10 - InterpolateServer: 1 - MinMeters: 0.16 - MinDegrees: 1.5 - ExtrapolatePosition: 0 - MaxSendsToExtrapolate: 5 - Channel: - EnableRange: 0 - EnableNonProvokedResendChecks: 0 - DistanceSendrate: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 20 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 500 - value: 20 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 diff --git a/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity b/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity index 966d5a17e1..ff89780772 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioning/SceneTransitioningTest.unity @@ -1043,10 +1043,7 @@ MonoBehaviour: SourcePrefabToOverride: {fileID: 0} SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} - ReceiveTickrate: 64 - NetworkTickIntervalSec: 0.05 - MaxReceiveEventsPerTickRate: 500 - EventTickrate: 64 + TickRate: 30 ClientConnectionBufferTimeout: 10 ConnectionApproval: 0 ConnectionData: @@ -1060,7 +1057,6 @@ MonoBehaviour: NetworkIdRecycleDelay: 120 RpcHashSize: 0 LoadSceneTimeOut: 120 - EnableMessageBuffering: 1 MessageBufferTimeout: 20 EnableNetworkLogs: 1 --- !u!114 &1024114719 diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 8c0e1b7110..77998bcb50 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -1541,10 +1541,7 @@ MonoBehaviour: SourcePrefabToOverride: {fileID: 0} SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} - ReceiveTickrate: 64 - NetworkTickIntervalSec: 0.05 - MaxReceiveEventsPerTickRate: 500 - EventTickrate: 64 + TickRate: 30 ClientConnectionBufferTimeout: 10 ConnectionApproval: 0 ConnectionData: @@ -1554,11 +1551,10 @@ MonoBehaviour: EnsureNetworkVariableLengthSafety: 0 EnableSceneManagement: 1 ForceSamePrefabs: 1 - RecycleNetworkIds: 1 + RecycleNetworkIds: 0 NetworkIdRecycleDelay: 120 RpcHashSize: 0 LoadSceneTimeOut: 120 - EnableMessageBuffering: 1 MessageBufferTimeout: 20 EnableNetworkLogs: 1 --- !u!114 &1024114719 @@ -3312,7 +3308,7 @@ MonoBehaviour: m_MinValue: 0 m_MaxValue: 550 m_WholeNumbers: 1 - m_Value: 2 + m_Value: 0 m_OnValueChanged: m_PersistentCalls: m_Calls: diff --git a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs index f8d616b2c3..8ca861ffef 100644 --- a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs +++ b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs @@ -67,6 +67,23 @@ private void FixedUpdate() } } + private bool m_ShouldDespawn; + + + + private void Update() + { + if(IsOwner && m_ShouldDespawn) + { + m_ShouldDespawn = false; + NetworkObject.Despawn(IsRemovedFromPool); + if (!IsRemovedFromPool) + { + NetworkObject.gameObject.SetActive(false); + } + } + } + /// /// Tells us that we are registered with a NetworkPefab pool /// This is primarily for late joining clients and object synchronization. @@ -82,7 +99,7 @@ private void FixedUpdate() private void OnTriggerEnter(Collider other) { - if (IsOwner) + if (IsOwner && !m_ShouldDespawn) { if (other.CompareTag("GenericObject") || other.CompareTag("Floor")) { @@ -90,11 +107,17 @@ private void OnTriggerEnter(Collider other) } else { - NetworkObject.Despawn(IsRemovedFromPool); - if (!IsRemovedFromPool) - { - NetworkObject.gameObject.SetActive(false); - } + m_ShouldDespawn = true; + + // NSS REMOVE WHEN FIXED: New message ordering changes that force a message to try and match its + // invoker's NetworkUpdateLoop stage will cause this despawn message to happen during the early Fixed Update + // stages. This, in turn, will cause the client side to fail to disable itself. The current "work around" is + // to despawn and disable within the MonoBehaviour.Update so the message is not invoked in FIXED_UPDATE + //NetworkObject.Despawn(IsRemovedFromPool); + //if (!IsRemovedFromPool) + //{ + // NetworkObject.gameObject.SetActive(false); + //} } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 0af32bbd68..3b5882506d 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -101,11 +101,6 @@ private void DeRegisterCustomPrefabHandler() private void OnDisable() { - if (NetworkManager != null && NetworkManager.SceneManager != null) - { - NetworkManager.SceneManager.OnSceneSwitchStarted -= OnSceneSwitchStarted; - } - StopCoroutine(SpawnObjects()); DeRegisterCustomPrefabHandler(); CleanNetworkObjects(); @@ -168,28 +163,11 @@ private void Start() SpawnSliderValueText.text = SpawnsPerSecond.ToString(); } - if (NetworkManager.SceneManager != null && IsServer) - { - NetworkManager.SceneManager.OnSceneSwitchStarted += OnSceneSwitchStarted; - } - //Call this again in case we didn't have access to the NetworkManager already (i.e. first scene loaded) RegisterCustomPrefabHandler(); } - /// - /// Detect when we are switching scenes in order - /// to assure we stop spawning objects - /// - private void OnSceneSwitchStarted(AsyncOperation operation) - { - if (IsServer) - { - - } - } - /// /// Override NetworkBehaviour.NetworkStart /// @@ -207,10 +185,6 @@ public override void OnNetworkSpawn() UpdateSpawnsPerSecond(); } } - else - { - NetworkManager.SceneManager.OnSceneSwitchStarted += OnSceneSwitchStarted; - } } /// @@ -432,7 +406,7 @@ public void HandleNetworkPrefabDestroy(NetworkObject networkObject) var genericBehaviour = networkObject.gameObject.GetComponent(); if (genericBehaviour.IsRegisteredPoolObject) { - networkObject.transform.position = Vector3.zero; + //networkObject.transform.position = Vector3.zero; networkObject.gameObject.SetActive(false); } else From 217748a1b78281798d2fda6f04609f46dbfd802d Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 27 Jul 2021 16:10:53 -0500 Subject: [PATCH 032/106] fix Removing some adjustments I did not intend on checking in. --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs | 2 +- .../Runtime/Messaging/MessageQueue/MessageQueueContainer.cs | 2 +- .../Runtime/Transports/NetworkTransport.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 9fcd0050dc..f888ed2bf0 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -185,7 +185,7 @@ public ulong LocalClientId /// /// Gets a list of just the IDs of all connected clients. /// - public ulong[] ConnectedClientsIds => ConnectedClientsList.Select(c => c.ClientId).Where(c => c != LocalClientId).ToArray(); + public ulong[] ConnectedClientsIds => ConnectedClientsList.Select(c => c.ClientId).ToArray(); /// /// Gets a dictionary of the clients that have been accepted by the transport but are still pending by the MLAPI. This is only populated on the server. diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs index e2638ed72f..765f888283 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs @@ -66,7 +66,7 @@ public enum MessageQueueProcessingTypes private int m_OutBoundStreamBufferIndex; private bool m_IsTestingEnabled; private bool m_ProcessUpdateStagesExternally; - private bool m_IsNotUsingBatching = true; //NSS REMOVE: Batching is broken currently, so I am disabling it. + private bool m_IsNotUsingBatching; // TODO hack: Fixed update can run multiple times in a frame and the queue history frame doesn't get cleared // until the end of the frame. This results in messages executing at FixedUpdate being invoked multiple times. diff --git a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs index 2df259b73d..b7cf3c4a00 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs @@ -103,7 +103,7 @@ public TransportChannel[] MLAPI_CHANNELS new TransportChannel(NetworkChannel.NavAgentCorrection, NetworkDelivery.UnreliableSequenced), // todo: Currently, fragmentation support needed to deal with oversize packets encounterable with current pre-snapshot code". // todo: once we have snapshotting able to deal with missing frame, this should be unreliable - new TransportChannel(NetworkChannel.NetworkVariable, NetworkDelivery.ReliableSequenced), + new TransportChannel(NetworkChannel.NetworkVariable, NetworkDelivery.ReliableFragmentedSequenced), new TransportChannel(NetworkChannel.SnapshotExchange, NetworkDelivery.ReliableFragmentedSequenced), // todo: temporary until we separate snapshots in chunks }; From 28eb59ed6c0898db2ce05bb44c6476031f7ea147 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 27 Jul 2021 16:25:36 -0500 Subject: [PATCH 033/106] refactor removed and re-added scenes in build list --- .../ProjectSettings/EditorBuildSettings.asset | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index e38b2aed68..be1b03a1a7 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -42,29 +42,27 @@ EditorBuildSettings: path: Assets/Tests/Manual/NetworkSceneManagerCallbacks/SceneWeAreSwitchingFrom.unity guid: 073bd2111475c0643be45b7abe6a97ad - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase1.unity + path: Assets/Scenes/MultiprocessTestScene.unity + guid: 76743cb7b342c49279327834918a9c6e + - enabled: 1 + path: Assets/Scenes/EmptyScene.unity + guid: a2545a872c007404fbb6b0393ab74974 + - enabled: 1 + path: Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity guid: 780f96a61e8ac8e41b638ae8ec3a3236 - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/SceneTransitioningBase2.unity + path: Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity guid: 9e437cc704801bc47add735d743985f5 - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene1.unity + path: Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity guid: 41a0239b0c49e2047b7063c822f0df8a - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene2.unity + path: Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene2.unity guid: c6a3d883c8253ee43bca4f2b03797d7b - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene3.unity + path: Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity guid: 7da3dd618f5b5a34db1f6d3c9511e221 - enabled: 1 - path: Assets/Samples/SceneTransitioningAdditive/AdditiveScene4.unity + path: Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene4.unity guid: dc7e17c86f5ca81478043be306027c13 - path: Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorEnhancement.unity - guid: f88da8bb8d07e11418eaad6524d5cc12 - - enabled: 1 - path: Assets/Scenes/MultiprocessTestScene.unity - guid: 76743cb7b342c49279327834918a9c6e - - enabled: 1 - path: Assets/Scenes/EmptyScene.unity - guid: a2545a872c007404fbb6b0393ab74974 m_configObjects: {} From 039ca2c84cf6ff55cacb5f84a04076153066930f Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 27 Jul 2021 16:53:00 -0500 Subject: [PATCH 034/106] refactor Increasing timeout for one at a time. --- .../Tests/Runtime/NetworkTransformTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs index a1d10ebbf3..b10f1100a9 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs @@ -82,7 +82,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) var playerTransform = networkTransform.transform; playerTransform.position = new Vector3(10, 20, 30); Assert.AreEqual(Vector3.zero, otherSideNetworkTransform.transform.position, "server side pos should be zero at first"); // sanity check - yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.position.x > approximation, waitResult, maxFrames: 120)); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.position.x > approximation, waitResult, maxFrames: 512)); if (!waitResult.Result) { throw new Exception("timeout while waiting for position change"); @@ -92,7 +92,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) // test rotation playerTransform.rotation = Quaternion.Euler(45, 40, 35); // using euler angles instead of quaternions directly to really see issues users might encounter Assert.AreEqual(Quaternion.identity, otherSideNetworkTransform.transform.rotation, "wrong initial value for rotation"); // sanity check - yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.rotation.eulerAngles.x > approximation, waitResult, maxFrames: 120)); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.rotation.eulerAngles.x > approximation, waitResult, maxFrames: 512)); if (!waitResult.Result) { throw new Exception("timeout while waiting for position change"); @@ -107,7 +107,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) UnityEngine.Assertions.Assert.AreApproximatelyEqual(1f, otherSideNetworkTransform.transform.lossyScale.y, "wrong initial value for scale"); // sanity check UnityEngine.Assertions.Assert.AreApproximatelyEqual(1f, otherSideNetworkTransform.transform.lossyScale.z, "wrong initial value for scale"); // sanity check playerTransform.localScale = new Vector3(2, 3, 4); - yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.lossyScale.x > 1f + approximation, waitResult, maxFrames: 120)); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.lossyScale.x > 1f + approximation, waitResult, maxFrames: 512)); if (!waitResult.Result) { throw new Exception("timeout while waiting for position change"); From b2157b97fcd723d246a44581417802738cdefd6e Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 27 Jul 2021 18:17:27 -0500 Subject: [PATCH 035/106] refactor Reverted minor modifications to transform test. --- .../Tests/Runtime/NetworkTransformTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs index b10f1100a9..a1d10ebbf3 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs @@ -82,7 +82,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) var playerTransform = networkTransform.transform; playerTransform.position = new Vector3(10, 20, 30); Assert.AreEqual(Vector3.zero, otherSideNetworkTransform.transform.position, "server side pos should be zero at first"); // sanity check - yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.position.x > approximation, waitResult, maxFrames: 512)); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.position.x > approximation, waitResult, maxFrames: 120)); if (!waitResult.Result) { throw new Exception("timeout while waiting for position change"); @@ -92,7 +92,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) // test rotation playerTransform.rotation = Quaternion.Euler(45, 40, 35); // using euler angles instead of quaternions directly to really see issues users might encounter Assert.AreEqual(Quaternion.identity, otherSideNetworkTransform.transform.rotation, "wrong initial value for rotation"); // sanity check - yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.rotation.eulerAngles.x > approximation, waitResult, maxFrames: 512)); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.rotation.eulerAngles.x > approximation, waitResult, maxFrames: 120)); if (!waitResult.Result) { throw new Exception("timeout while waiting for position change"); @@ -107,7 +107,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) UnityEngine.Assertions.Assert.AreApproximatelyEqual(1f, otherSideNetworkTransform.transform.lossyScale.y, "wrong initial value for scale"); // sanity check UnityEngine.Assertions.Assert.AreApproximatelyEqual(1f, otherSideNetworkTransform.transform.lossyScale.z, "wrong initial value for scale"); // sanity check playerTransform.localScale = new Vector3(2, 3, 4); - yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.lossyScale.x > 1f + approximation, waitResult, maxFrames: 512)); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.lossyScale.x > 1f + approximation, waitResult, maxFrames: 120)); if (!waitResult.Result) { throw new Exception("timeout while waiting for position change"); From 44365744aae26d95057df01d9db9c43ccfdb85ff Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 27 Jul 2021 19:00:06 -0500 Subject: [PATCH 036/106] refactor Removing temporary fix for issue with NetworkBuffer. Adding a test to the NetworkTransformTests to see if frame rate has anything to do with the seemingly random failures only on MAC. --- .../Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs | 2 +- .../Tests/Runtime/NetworkTransformTests.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs index 92255f8390..794ebe6c09 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs @@ -217,7 +217,7 @@ public void CloseQueue() if (m_CurrentItem.NetworkBuffer != null) { - ((PooledNetworkBuffer)m_CurrentItem.NetworkBuffer).Dispose(); + m_CurrentItem.NetworkBuffer.Dispose(); m_CurrentItem.NetworkBuffer = null; } } diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs index a1d10ebbf3..b058aeab39 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs @@ -28,6 +28,7 @@ public NetworkTransformTests(bool testWithHost) [UnitySetUp] public override IEnumerator Setup() { + Application.targetFrameRate = 60; yield return StartSomeClientsAndServerWithPlayers(useHost: m_TestWithHost, nbClients: NbClients, updatePlayerPrefab: playerPrefab => { var networkTransform = playerPrefab.AddComponent(); @@ -144,6 +145,7 @@ public IEnumerator TestCantChangeTransformFromOtherSideAuthority(NetworkAuthorit [UnityTearDown] public override IEnumerator Teardown() { + Application.targetFrameRate = -1; yield return base.Teardown(); UnityEngine.Object.Destroy(m_PlayerPrefab); } From 3bb2dc6ff5caf3cc5862a35aae16dfff6012d206 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 28 Jul 2021 11:34:25 -0500 Subject: [PATCH 037/106] test Updating this adjustment to see if this makes it through the mac tests. --- .../Tests/Runtime/MultiInstanceHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstanceHelpers.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstanceHelpers.cs index 76b8c61398..828cddedb8 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstanceHelpers.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstanceHelpers.cs @@ -451,7 +451,7 @@ public static IEnumerator WaitForCondition(Func predicate, CoroutineResult while (Time.frameCount - startFrameNumber <= maxFrames && !predicate()) { - var nextFrameNumber = Time.frameCount + 1; + var nextFrameNumber = Time.frameCount + 2; yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); } From 26e94bb23918b0c39eb6cf2ab7343024fb65ab7f Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 28 Jul 2021 11:39:43 -0500 Subject: [PATCH 038/106] test Forgot to remove the applied frame rate. --- .../Tests/Runtime/NetworkTransformTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs index b058aeab39..a1d10ebbf3 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkTransformTests.cs @@ -28,7 +28,6 @@ public NetworkTransformTests(bool testWithHost) [UnitySetUp] public override IEnumerator Setup() { - Application.targetFrameRate = 60; yield return StartSomeClientsAndServerWithPlayers(useHost: m_TestWithHost, nbClients: NbClients, updatePlayerPrefab: playerPrefab => { var networkTransform = playerPrefab.AddComponent(); @@ -145,7 +144,6 @@ public IEnumerator TestCantChangeTransformFromOtherSideAuthority(NetworkAuthorit [UnityTearDown] public override IEnumerator Teardown() { - Application.targetFrameRate = -1; yield return base.Teardown(); UnityEngine.Object.Destroy(m_PlayerPrefab); } From f092e45a056851d2a459b69c01031469368020a2 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 29 Jul 2021 17:54:37 -0500 Subject: [PATCH 039/106] refactor Cleaning up merge from develop. Starting to work on the local notification side of things. --- .../SceneManagement/NetworkSceneManager.cs | 42 +++---- .../Runtime/Spawning/NetworkSpawnManager.cs | 8 +- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 8 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 105 +++++++++--------- .../ProjectSettings/EditorBuildSettings.asset | 1 + 5 files changed, 87 insertions(+), 77 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 5bb10620a7..05dd641e96 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -29,7 +29,7 @@ public class NetworkSceneManager /// public delegate void SceneSwitchStartedDelegate(AsyncOperation operation); - public delegate void AdditiveSceneEventDelegate(AsyncOperation operation, string sceneName, bool isLoading); + public delegate void SceneEventDelegate(AsyncOperation operation, SceneEventData.SceneEventTypes sceneEventType, LoadSceneMode loadSceneMode, string sceneName); /// /// Delegate for when a client has reported to the server that it has completed scene transition @@ -54,7 +54,7 @@ public class NetworkSceneManager /// public event SceneSwitchedDelegate OnSceneSwitched; - public event AdditiveSceneEventDelegate OnAdditiveSceneEvent; + public event SceneEventDelegate OnSceneEvent; /// /// Event that is invoked when a local scene switch has started @@ -103,7 +103,7 @@ public class NetworkSceneManager private MessageQueueContainer.MessageType m_MessageType = MessageQueueContainer.MessageType.SceneEvent; private NetworkChannel m_ChannelType = NetworkChannel.Internal; - private NetworkUpdateStage m_NetworkUpdateStage = NetworkUpdateLoop.UpdateStage; + private NetworkUpdateStage m_NetworkUpdateStage = NetworkUpdateStage.EarlyUpdate; internal NetworkSceneManager(NetworkManager networkManager) { @@ -116,18 +116,21 @@ internal NetworkSceneManager(NetworkManager networkManager) internal void SendSceneEventData(ulong[] targetClientIds) { - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, targetClientIds, m_NetworkUpdateStage); - - if (context != null) + if (targetClientIds.Length > 1) { - using (var nonNullContext = (InternalCommandContext)context) + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, targetClientIds, m_NetworkUpdateStage); + + if (context != null) { - SceneEventData.OnWrite(nonNullContext.NetworkWriter); - } + using (var nonNullContext = (InternalCommandContext)context) + { + SceneEventData.OnWrite(nonNullContext.NetworkWriter); + } - return; + return; + } + throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientIds {targetClientIds}!"); } - throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientIds {targetClientIds}!"); } /// @@ -246,7 +249,7 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn OnNotifyServerAllClientsLoadedScene?.Invoke(switchSceneProgress, timedOut); // Send notification to all clients that everyone is done loading var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.AllClientsLoadedScene, NetworkChannel.Internal, - m_NetworkManager.ConnectedClientsIds, NetworkUpdateLoop.UpdateStage); + m_NetworkManager.ConnectedClientsIds, NetworkUpdateStage.EarlyUpdate); if (context != null) { @@ -302,7 +305,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) { m_ScenesLoaded.Remove(sceneName); } - OnAdditiveSceneEvent?.Invoke(sceneUnload, sceneName, false); + OnSceneEvent?.Invoke(sceneUnload,SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); //Return our scene progress instance return switchSceneProgress; @@ -330,7 +333,7 @@ private void OnClientUnloadScene() sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); - OnAdditiveSceneEvent?.Invoke(sceneUnload, sceneName, false); + OnSceneEvent?.Invoke(sceneUnload, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); } /// @@ -424,6 +427,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene m_ScenesLoaded.Clear(); // NSS TODO: Make a single unified notification callback OnSceneSwitchStarted?.Invoke(sceneLoad); + OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); } else { @@ -436,7 +440,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene throw new Exception($"{sceneName} is being loaded twice?!"); } // NSS TODO: Make a single unified notification callback - OnAdditiveSceneEvent?.Invoke(sceneLoad, sceneName, true); + OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); } } @@ -466,9 +470,8 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) { if (currentActiveScene.name != loadedSceneName) { - SceneManager.UnloadSceneAsync(loadedSceneName); + OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(loadedSceneName), SceneEventData.SceneEventTypes.Event_Unload, LoadSceneMode.Additive, loadedSceneName); } - } m_ScenesLoaded.Clear(); @@ -502,10 +505,7 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) { OnSceneSwitchStarted?.Invoke(sceneLoad); } - else - { - OnAdditiveSceneEvent?.Invoke(sceneLoad, sceneName, true); - } + OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); } /// diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs index 635c540221..3ba1a548a3 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs @@ -301,6 +301,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo if (SpawnedObjects.ContainsKey(networkId)) { + Debug.LogWarning($"Trying to spawn NetworkObjectId {networkId} that already exists!"); return; } @@ -507,6 +508,7 @@ internal void ServerDestroySpawnedSceneObjects() if (NetworkManager.PrefabHandler != null && NetworkManager.PrefabHandler.ContainsHandler(sobj)) { NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(sobj); + OnDespawnObject(sobj, false); } else { @@ -530,11 +532,7 @@ internal void DestroyNonSceneObjects() if (NetworkManager.PrefabHandler.ContainsHandler(networkObjects[i])) { NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObjects[i]); - - if (SpawnedObjects.ContainsKey(networkObjects[i].NetworkObjectId)) - { - OnDespawnObject(networkObjects[i], false); - } + OnDespawnObject(networkObjects[i], false); } else { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 0d85a3d47e..567b547fb4 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -95,10 +95,15 @@ private void DeregisterCustomPrefabHandler() } } + public override void OnNetworkDespawn() + { + DeregisterCustomPrefabHandler(); + } + private void OnDisable() { StopCoroutine(SpawnObjects()); - DeRegisterCustomPrefabHandler(); + CleanNetworkObjects(); } @@ -245,6 +250,7 @@ private GameObject AddNewInstance() var obj = Instantiate(m_ObjectToSpawn); var genericNetworkObjectBehaviour = obj.GetComponent(); genericNetworkObjectBehaviour.HasHandler = EnableHandler; + genericNetworkObjectBehaviour.IsRegisteredPoolObject = true; m_ObjectPool.Add(obj); return obj; } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 24f6298ad3..3497279a98 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -4,6 +4,7 @@ using UnityEngine.SceneManagement; using MLAPI; using MLAPI.Spawning; +using MLAPI.SceneManagement; namespace TestProject.ManualTests { @@ -41,15 +42,6 @@ public class NetworkPrefabPoolAdditive : NetworkBehaviour private MyAdditiveCustomPrefabSpawnHandler m_AdditiveCustomPrefabSpawnHandler; - /// - /// Called when enabled, if already connected we register any custom prefab spawn handler here - /// - private void OnEnable() - { - //This registers early under the condition of a scene transition - RegisterCustomPrefabHandler(); - } - /// /// Handles registering the custom prefab handler /// @@ -73,7 +65,7 @@ private void RegisterCustomPrefabHandler() } } - private void DeRegisterCustomPrefabHandler() + private void DeregisterCustomPrefabHandler() { // Register the custom spawn handler? if (EnableHandler && NetworkManager && NetworkManager.PrefabHandler != null && m_AdditiveCustomPrefabSpawnHandler != null) @@ -96,13 +88,13 @@ private void OnDestroy() { StopCoroutine(SpawnObjects()); } - DeRegisterCustomPrefabHandler(); + DeregisterCustomPrefabHandler(); if (NetworkManager != null && NetworkManager.SceneManager != null) { - NetworkManager.SceneManager.OnAdditiveSceneEvent -= OnAdditiveSceneEvent; + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; } } @@ -110,23 +102,38 @@ private void OnDestroy() private void Start() { SpawnsPerSecond = 3; - NetworkManager.SceneManager.OnAdditiveSceneEvent += OnAdditiveSceneEvent; - //Call this again in case we didn't have access to the NetworkManager already (i.e. first scene loaded) - RegisterCustomPrefabHandler(); + NetworkManager.SceneManager.OnSceneEvent += OnSceneEvent; } + + /// /// 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. /// /// /// - private void OnAdditiveSceneEvent(AsyncOperation operation, string sceneName, bool isLoading) + private void OnSceneEvent(AsyncOperation operation,SceneEventData.SceneEventTypes sceneEventType,LoadSceneMode loadSceneMode, string sceneName) { - if (!isLoading && gameObject.scene.name == sceneName) + switch(sceneEventType) { - OnUnloadScene(); + case SceneEventData.SceneEventTypes.Event_Unload: + { + //if(loadSceneMode == LoadSceneMode.Additive) + //{ + // OnUnloadScene(); + //} + break; + } + case SceneEventData.SceneEventTypes.Event_Load: + { + if (loadSceneMode == LoadSceneMode.Single && gameObject.scene.name == sceneName) + { + OnUnloadScene(); + } + break; + } } } @@ -184,7 +191,7 @@ private void OnUnloadScene() StopCoroutine(SpawnObjects()); } // De-register the custom prefab handler - DeRegisterCustomPrefabHandler(); + DeregisterCustomPrefabHandler(); CleanNetworkObjects(); } @@ -214,42 +221,40 @@ public override void OnNetworkSpawn() /// public void InitializeObjectPool() { - // Start by defining the server only network prefab for pooling - m_ObjectToSpawn = ServerObjectToPool; + // Base construction and registration of the custom prefab handler. + RegisterCustomPrefabHandler(); - // If we are a host, then we have to get the NetworkPrefab Override (if one exists) - if (IsHost && !EnableHandler) - { - m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); - } - // If we are a client and we are using the custom prefab override handler, then we need to use that for our pool - // This also checks to see if the ClientObjectToPool is set, if not then we are just using the custom prefab override handler - // to assure the client-side uses the NetworkObject pool as opposed to always spawning and destroying NetworkObjects. - else if (IsClient && EnableHandler && ClientObjectToPool != null) - { - m_ObjectToSpawn = ClientObjectToPool; - } + // Default to the server side object + m_ObjectToSpawn = ServerObjectToPool; - // If we are enabling the handler, then we can control which NetworkObject will be used for spawning. - // If we are the server but do not have a handler, then we use a less efficient server-side only pool (clients will instantiate and destroy on their side) - if (EnableHandler || IsServer) + // Host and Client need to do an extra step + if (IsClient) { - // In order to account for any NetworkPrefab override defined within the NetworkManager, we do one last check to assure we are creating a pool - // of the right NetworkPrefab objects, otherwise GetNetworkPrefabOverride will return back the same m_ObjectToSpawn - // NOTE: We filter out the case where we are a server, as the server will send the original NetworkPrefab GlobalObjectIdHash. - // If we enable this for dedicated server, then the server would spawn the override prefab which will cause the client to create a pool that is - // never used and the client(s) will spawn and destroy GameObjects outside of the pool. - if (EnableHandler && IsClient) + if (EnableHandler && ClientObjectToPool != null) + { + m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(ClientObjectToPool); + } + else { m_ObjectToSpawn = NetworkManager.GetNetworkPrefabOverride(m_ObjectToSpawn); - NetworkManager.PrefabHandler.AddHandler(m_ObjectToSpawn, m_AdditiveCustomPrefabSpawnHandler); } + // Since the host should spawn the override, we need to register the host to link it to the originally registered ServerObjectToPool + if (IsHost && EnableHandler && ServerObjectToPool != m_ObjectToSpawn) + { + // While this seems redundant, we could theoretically have several objects that we could potentially be spawning + NetworkManager.PrefabHandler.RegisterHostGlobalObjectIdHashValues(ServerObjectToPool, new List() { m_ObjectToSpawn }); + } + } + + if (EnableHandler || IsServer) + { m_ObjectPool = new List(PoolSize); for (int i = 0; i < PoolSize; i++) { - AddNewInstance(); + var gameObject = AddNewInstance(); + gameObject.SetActive(false); } } } @@ -284,11 +289,11 @@ public GameObject GetObject() private GameObject AddNewInstance() { var obj = Instantiate(m_ObjectToSpawn); - var genericBehaviour = obj.GetComponent(); - if (genericBehaviour) + var genericNetworkObjectBehaviour = obj.GetComponent(); + if (genericNetworkObjectBehaviour) { - genericBehaviour.ShouldMoveRandomly(RandomMovement); - genericBehaviour.IsRegisteredPoolObject = true; + genericNetworkObjectBehaviour.ShouldMoveRandomly(RandomMovement); + genericNetworkObjectBehaviour.IsRegisteredPoolObject = true; } // Example of how to keep your pooled NetworkObjects in the same scene as your spawn generator (additive scenes only) @@ -413,7 +418,7 @@ private IEnumerator SpawnObjects() public class MyAdditiveCustomPrefabSpawnHandler : INetworkPrefabInstanceHandler { private NetworkPrefabPoolAdditive m_PrefabPool; - public NetworkObject HandleNetworkPrefabSpawn(ulong ownerClientId, Vector3 position, Quaternion rotation) + public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) { var obj = m_PrefabPool.GetObject(); if (obj != null) @@ -424,7 +429,7 @@ public NetworkObject HandleNetworkPrefabSpawn(ulong ownerClientId, Vector3 posit } return null; } - public void HandleNetworkPrefabDestroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { var genericBehaviour = networkObject.gameObject.GetComponent(); if (genericBehaviour.IsRegisteredPoolObject) diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index abf2e77544..044559a227 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -65,6 +65,7 @@ EditorBuildSettings: - enabled: 1 path: Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene4.unity guid: dc7e17c86f5ca81478043be306027c13 + - enabled: 1 path: Assets/Tests/Manual/PrefabPool/PrefabPoolExample.unity guid: e4732b28c18f52c4dbe06c8a37f7997f m_configObjects: {} From c67dc73de9ac0dbf425ff1b94331fbd4708f08b4 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 1 Aug 2021 20:26:00 -0500 Subject: [PATCH 040/106] refactor Work in progress for local notifications, how network prefab handler pools destroy objects, and how NetworkObjects marked for or not marked for do not destroy on load are handled. --- .../Runtime/Core/NetworkManager.cs | 4 +- .../SceneManagement/NetworkSceneManager.cs | 28 ++++++++++---- .../Runtime/Spawning/NetworkSpawnManager.cs | 18 ++------- .../SceneTransitioningBase1.unity | 4 +- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 37 ++++++++++++++++++- .../Scripts/NetworkPrefabPoolAdditive.cs | 29 +++------------ 6 files changed, 69 insertions(+), 51 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 77ac27363b..84a55cc822 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -1355,7 +1355,7 @@ internal void OnClientDisconnectFromServer(ulong clientId) } } - // TODO: Could(should?) be replaced with more memory per client, by storing the visiblity + // TODO: Could(should?) be replaced with more memory per client, by storing the visibility foreach (var sobj in SpawnManager.SpawnedObjectsList) { @@ -1438,7 +1438,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? // Don't send the CONNECTION_APPROVED message if this is the host that connected locally if (ownerClientId != ServerClientId) { - var context = MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.ConnectionApproved, NetworkChannel.Internal, new ulong[] clientIds{ownerClientId}, + var context = MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.ConnectionApproved, NetworkChannel.Internal, new ulong[]{ownerClientId}, NetworkUpdateStage.EarlyUpdate); if (context != null) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 34170d51c2..7c6ca1c8ea 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -376,6 +376,8 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo // Preserve the objects that should not be destroyed during the scene event MoveObjectsToDontDestroyOnLoad(); + + } // Begin the scene event @@ -417,17 +419,20 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { + OnSceneEvent?.Invoke(null, SceneEventData.SceneEventTypes.Event_Unload, SceneEventData.LoadSceneMode, SceneManager.GetActiveScene().name); foreach (var additiveSceneName in m_ScenesLoaded) { if (currentActiveScene.name != additiveSceneName) { - SceneManager.UnloadSceneAsync(additiveSceneName); + OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(additiveSceneName), SceneEventData.SceneEventTypes.Event_Unload, LoadSceneMode.Additive, additiveSceneName); } } m_ScenesLoaded.Clear(); + + // NSS TODO: Make a single unified notification callback OnSceneSwitchStarted?.Invoke(sceneLoad); - OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); + } else { @@ -439,9 +444,9 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene { throw new Exception($"{sceneName} is being loaded twice?!"); } - // NSS TODO: Make a single unified notification callback - OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); } + + OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); } /// @@ -574,7 +579,7 @@ private void OnServerLoadedScene() sceneObjectsToSpawn++; } } - + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, new ulong[] { clientId }, m_NetworkUpdateStage); if (context != null) { @@ -594,7 +599,7 @@ private void OnServerLoadedScene() } else { - throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientId {clientIdAsArray}!"); + throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientId {clientId}!"); } } } @@ -775,7 +780,7 @@ internal void RemoveClientFromSceneSwitchProgresses(ulong clientId) private void MoveObjectsToDontDestroyOnLoad() { // Move ALL NetworkObjects to the temp scene - var objectsToKeep = m_NetworkManager.SpawnManager.SpawnedObjectsList; + var objectsToKeep = new HashSet(m_NetworkManager.SpawnManager.SpawnedObjectsList); foreach (var sobj in objectsToKeep) { @@ -785,7 +790,14 @@ private void MoveObjectsToDontDestroyOnLoad() sobj.gameObject.transform.parent = null; } - UnityEngine.Object.DontDestroyOnLoad(sobj.gameObject); + if (!sobj.DestroyWithScene) + { + UnityEngine.Object.DontDestroyOnLoad(sobj.gameObject); + } + else + { + sobj.Despawn(true); + } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs index 3c2803b106..6787570cb2 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs @@ -499,22 +499,10 @@ internal void ServerDestroySpawnedSceneObjects() foreach (var sobj in spawnedObjects) { - if ((sobj.IsSceneObject != null && sobj.IsSceneObject == true) || sobj.DestroyWithScene) + if ((sobj.IsSceneObject != null && sobj.IsSceneObject == true)) { - // This **needs** to be here until we overhaul NetworkSceneManager due to dependencies - // that occur shortly after NetworkSceneManager invokes ServerDestroySpawnedSceneObjects - // within the NetworkSceneManager.SwitchScene method. - - if (NetworkManager.PrefabHandler != null && NetworkManager.PrefabHandler.ContainsHandler(sobj)) - { - NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(sobj); - OnDespawnObject(sobj, false); - } - else - { - SpawnedObjectsList.Remove(sobj); - UnityEngine.Object.Destroy(sobj.gameObject); - } + SpawnedObjectsList.Remove(sobj); + UnityEngine.Object.Destroy(sobj.gameObject); } } } diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 77998bcb50..c601667305 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -174,6 +174,8 @@ MonoBehaviour: m_EditorClassIdentifier: m_SwitchSceneButtonObject: {fileID: 1347823141} m_SceneToSwitchTo: SceneTransitioningBase2 + m_EnableAutoSwitch: 0 + m_AutoSwitchTimeOut: 60 --- !u!1 &37242881 GameObject: m_ObjectHideFlags: 0 @@ -3308,7 +3310,7 @@ MonoBehaviour: m_MinValue: 0 m_MaxValue: 550 m_WholeNumbers: 1 - m_Value: 0 + m_Value: 4 m_OnValueChanged: m_PersistentCalls: m_Calls: diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 567b547fb4..8c0bf86b60 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -1,9 +1,11 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; +using UnityEngine.SceneManagement; using UnityEngine.UI; using MLAPI; using MLAPI.Spawning; +using MLAPI.SceneManagement; namespace TestProject.ManualTests { @@ -95,16 +97,48 @@ private void DeregisterCustomPrefabHandler() } } - public override void OnNetworkDespawn() + private void OnSceneEvent(AsyncOperation operation, SceneEventData.SceneEventTypes sceneEventType, LoadSceneMode loadSceneMode, string sceneName) { + switch (sceneEventType) + { + case SceneEventData.SceneEventTypes.Event_Unload: + { + if (loadSceneMode == LoadSceneMode.Single && (gameObject.scene.name == sceneName)) + { + OnUnloadScene(); + } + break; + } + } + } + + /// + /// Detect when we are switching scenes in order + /// to assure we stop spawning objects + /// + private void OnUnloadScene() + { + if (IsServer) + { + StopCoroutine(SpawnObjects()); + } + CleanNetworkObjects(); DeregisterCustomPrefabHandler(); + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; + } + + public override void OnNetworkDespawn() + { + // DeregisterCustomPrefabHandler(); } private void OnDisable() { + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; StopCoroutine(SpawnObjects()); CleanNetworkObjects(); + } private void CleanNetworkObjects() @@ -161,6 +195,7 @@ private void Start() /// public override void OnNetworkSpawn() { + NetworkManager.SceneManager.OnSceneEvent += OnSceneEvent; InitializeObjectPool(); if (IsServer) { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 3497279a98..c1058ef24c 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -118,17 +118,9 @@ private void OnSceneEvent(AsyncOperation operation,SceneEventData.SceneEventType { switch(sceneEventType) { - case SceneEventData.SceneEventTypes.Event_Unload: - { - //if(loadSceneMode == LoadSceneMode.Additive) - //{ - // OnUnloadScene(); - //} - break; - } case SceneEventData.SceneEventTypes.Event_Load: { - if (loadSceneMode == LoadSceneMode.Single && gameObject.scene.name == sceneName) + if (loadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneName) || !SpawnInSourceScene) ) { OnUnloadScene(); } @@ -149,23 +141,13 @@ private void CleanNetworkObjects() genericBehaviour.IsRemovedFromPool = true; if (IsServer) { - if (SpawnInSourceScene) + if (networkObject.IsSpawned) { - if (networkObject.IsSpawned) - { - networkObject.Despawn(true); - } - else - { - DestroyImmediate(obj); - } + networkObject.Despawn(true); } else { - if (!networkObject.IsSpawned) - { - DestroyImmediate(obj); - } + DestroyImmediate(obj); } } else //Client @@ -190,10 +172,9 @@ private void OnUnloadScene() { StopCoroutine(SpawnObjects()); } + // De-register the custom prefab handler DeregisterCustomPrefabHandler(); - - CleanNetworkObjects(); } /// From ccd659e3363f4d40e39adeaa0530ffcdb3046a4c Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 2 Aug 2021 11:36:47 -0500 Subject: [PATCH 041/106] fix Checking for NetworkManager to exist before disabling. --- testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 8c0bf86b60..bede30da93 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -134,7 +134,10 @@ public override void OnNetworkDespawn() private void OnDisable() { - NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; + if (NetworkManager != null) + { + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; + } StopCoroutine(SpawnObjects()); CleanNetworkObjects(); From 49a0b3d3528792bf9a2e904f49d1bba1ac9ae0d8 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 2 Aug 2021 22:12:07 -0500 Subject: [PATCH 042/106] fix This includes fixes for the namespace changes. --- .../Runtime/Core/NetworkManager.cs | 4 ++++ .../Runtime/SceneManagement/NetworkSceneManager.cs | 1 + .../Runtime/SceneManagement/SceneEventData.cs | 6 +++--- .../AdditiveSceneToggleHandler.cs | 4 ++-- .../SceneTransitioningAdditive/NetworkManagerMonitor.cs | 4 +--- .../SwitchSceneHandlerAdditive.cs | 4 ++-- .../Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs | 8 ++++---- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index a6cd12be33..80bb440152 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif using Unity.Multiplayer.Netcode.Logging; using Unity.Multiplayer.Netcode.Configuration; using Unity.Multiplayer.Netcode.Profiling; @@ -19,6 +22,7 @@ using Unity.Profiling; using Debug = UnityEngine.Debug; + namespace Unity.Multiplayer.Netcode { /// diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 471d26fef7..0bc3384fbc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -6,6 +6,7 @@ using Unity.Multiplayer.Netcode.Exceptions; using Unity.Multiplayer.Netcode.Logging; using Unity.Multiplayer.Netcode.Messaging; +using Unity.Multiplayer.Netcode.Serialization; using Unity.Multiplayer.Netcode.Serialization.Pooled; using UnityEngine; using UnityEngine.SceneManagement; diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index d8538ab6ce..ac873facd8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -2,13 +2,13 @@ using System; using System.IO; using System.Linq; -using MLAPI.Serialization.Pooled; +using Unity.Multiplayer.Netcode.Serialization.Pooled; using UnityEngine; using UnityEngine.SceneManagement; -using MLAPI.Serialization; +using Unity.Multiplayer.Netcode.Serialization; -namespace MLAPI.SceneManagement +namespace Unity.Multiplayer.Netcode.SceneManagement { [Serializable] public class SceneEventData : IDisposable diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index ed9c08bbb2..3192f5a0b5 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -5,8 +5,8 @@ using UnityEditor; #endif -using MLAPI; -using MLAPI.SceneManagement; +using Unity.Multiplayer.Netcode; +using Unity.Multiplayer.Netcode.SceneManagement; namespace TestProject.ManualTests { diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs index 3ea3f9fd0a..eea7eef2ce 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs @@ -1,7 +1,5 @@ -using System.Collections; -using System.Collections.Generic; using UnityEngine; -using MLAPI; +using Unity.Multiplayer.Netcode; public class NetworkManagerMonitor : MonoBehaviour { diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index 2e1af8e1b0..8d953de707 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -5,8 +5,8 @@ using UnityEditor; #endif -using MLAPI; -using MLAPI.SceneManagement; +using Unity.Multiplayer.Netcode; +using Unity.Multiplayer.Netcode.SceneManagement; namespace TestProject.ManualTests { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index c1058ef24c..9caced9b0c 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; -using MLAPI; -using MLAPI.Spawning; -using MLAPI.SceneManagement; +using Unity.Multiplayer.Netcode; +using Unity.Multiplayer.Netcode.Spawning; +using Unity.Multiplayer.Netcode.SceneManagement; namespace TestProject.ManualTests { @@ -377,7 +377,7 @@ private IEnumerator SpawnObjects() var no = go.GetComponent(); if (!no.IsSpawned) { - no.Spawn(null, true); + no.Spawn(true); } } } From c9fb565ce54e2fff3b9d9772c249e1bd1bf102a1 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 3 Aug 2021 09:19:42 -0500 Subject: [PATCH 043/106] fix Standards check for no longer needed using statments --- .../Runtime/Configuration/NetworkConfig.cs | 1 - com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs | 2 +- .../Runtime/Messaging/InternalMessageHandler.cs | 5 +---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs index b84cafe430..ffdfb5bb3b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using UnityEngine; -using UnityEngine.SceneManagement; #if UNITY_EDITOR using UnityEditor; #endif diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs index 168c61e25b..514c19cbdd 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs @@ -10,7 +10,7 @@ using Unity.Multiplayer.Netcode.Transports; using Unity.Multiplayer.Netcode.Serialization; using UnityEngine; -using UnityEngine.SceneManagement; + namespace Unity.Multiplayer.Netcode { diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index fb97e1f273..a92a20c36f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -1,12 +1,9 @@ -using System; + using System.IO; using Unity.Multiplayer.Netcode.Connection; using Unity.Multiplayer.Netcode.Logging; -using Unity.Multiplayer.Netcode.SceneManagement; using Unity.Multiplayer.Netcode.Serialization.Pooled; using UnityEngine; -using UnityEngine.Events; -using UnityEngine.SceneManagement; using Unity.Multiplayer.Netcode.Configuration; using Unity.Multiplayer.Netcode.Profiling; using Unity.Multiplayer.Netcode.Serialization; From 77e9e07e9014e8a3816bd9d8d0e37d06c78b35b6 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 3 Aug 2021 10:12:38 -0500 Subject: [PATCH 044/106] fix temporary fix for scene loading test. --- testproject/Assets/Tests/Runtime/SceneLoadingTest.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs index 5c80324e7b..ebc9674805 100644 --- a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs +++ b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs @@ -1,3 +1,4 @@ +#if IGNORETHISTEST using System.Collections; using UnityEngine; using NUnit.Framework; @@ -7,7 +8,7 @@ namespace TestProject.RuntimeTests { -#if IGNORETHISTEST + /// /// This is nothing more than a template to follow in order to /// use a scene to configure your NetworkManager as well as how @@ -257,5 +258,6 @@ private IEnumerator TearDown() yield return null; } } -#endif + } +#endif From 221129870538e7987348e00052de0268ec19e960 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 3 Aug 2021 15:52:32 -0500 Subject: [PATCH 045/106] refactor Updating additive pooling with additive scene unloading. Added the ability to specify whether NetworkObjects should be destroyed on scene unload or not. If the SpawnInSourceScene is set to true and the DestroyOnUnload is not set, then the already spawned NetworkObjects are moved to the currently active scene to allow them to persist until they naturally are destroyed. Fixed issues with most objects already destroyed messages. Still have a few that pop up from time to time. Fixed issue with client not sending responses back to server. Refactored NetworkPrefabHandler and INetworkPrefabInstanceHandler so that the destroy method returns a bool value which determines if the NetworkObject should actually be destroyed or not (there are edge case scenarios this fixes). Updated several tests to reflect these changes. --- .../Runtime/Core/NetworkObject.cs | 11 ++-- .../SceneManagement/NetworkSceneManager.cs | 30 ++-------- .../Runtime/Spawning/NetworkPrefabHandler.cs | 11 ++-- .../Runtime/Spawning/NetworkSpawnManager.cs | 11 +++- .../Runtime/NetworkPrefabHandlerTests.cs | 4 +- .../Tests/Runtime/NetworkSceneManagerTests.cs | 2 +- .../AdditiveScene1.unity | 1 + .../AdditiveScene3.unity | 1 + .../Scripts/GenericNetworkObjectBehaviour.cs | 8 +-- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 25 ++++---- .../Scripts/NetworkPrefabPoolAdditive.cs | 60 +++++++++++++++++-- .../NetworkSceneManagerCallbackTests.cs | 2 +- .../Manual/Scripts/SwitchSceneHandler.cs | 2 +- .../CustomPrefabSpawnerForPerformanceTests.cs | 3 +- .../Support/SpawnRpcDespawnInstanceHandler.cs | 4 +- 15 files changed, 108 insertions(+), 67 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs index 514c19cbdd..c4c04d3310 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs @@ -155,16 +155,16 @@ internal set /// Used for late-joining client synchronization purposes. /// The scene that has dependencies to this NetworkObject /// If not set then it is ignored. - /// (see NetworkObject.SetSceneAsDependency for more information) + /// for more information. /// public string DependentSceneName { get; internal set; } /// /// For late-joining client synchronization and additive scene(s) purposes - /// This provides the ability to associate a NetworkObject with a scene that is not + /// This provides the ability to associate a with a scene that is not /// currently spawned in but may have dependencies within the dependent scene - /// Example: NetworkObject pool generator with custom Network Prefab override handler - /// needs to initialize before this NetworkObject is spawned. + /// Example: pool generator with custom Network Prefab override handler + /// needs to initialize before this is spawned. /// /// scene that has dependencies to the NetworkObject public void SetSceneAsDependency(string sceneName) @@ -435,7 +435,8 @@ public static void NetworkHide(List networkObjects, ulong clientI private void OnDestroy() { - if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned) + if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned + && (IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true) )) { throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead."); } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 0bc3384fbc..e22170550b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -116,7 +116,7 @@ internal NetworkSceneManager(NetworkManager networkManager) internal void SendSceneEventData(ulong[] targetClientIds) { - if (targetClientIds.Length > 1) + if (targetClientIds.Length >= 1) { var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, targetClientIds, m_NetworkUpdateStage); @@ -267,8 +267,6 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn return switchSceneProgress; } - - /// /// Unloads an additively loaded scene /// @@ -376,8 +374,6 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo // Preserve the objects that should not be destroyed during the scene event MoveObjectsToDontDestroyOnLoad(); - - } // Begin the scene event @@ -387,19 +383,6 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo return switchSceneProgress; } - /// - /// Switches to a scene with a given name. Can only be called from Server - /// - /// The name of the scene to switch to - /// The mode to load the scene (Additive vs Single) - /// NSS TODO: This is a topic for MTT discussion. Do we want to keep this divergence from Unity's SceneManager API? - /// It is proposed we completely remove this call or mark it as deprecated with message to use Load and Unload - /// SceneSwitchProgress - public SceneSwitchProgress SwitchScene(string sceneName, LoadSceneMode loadSceneMode = LoadSceneMode.Single) - { - return LoadScene(sceneName, loadSceneMode); - } - /// /// Commonly shared code between switching and additively loading a scene /// @@ -415,8 +398,6 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); - - if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { OnSceneEvent?.Invoke(null, SceneEventData.SceneEventTypes.Event_Unload, SceneEventData.LoadSceneMode, SceneManager.GetActiveScene().name); @@ -703,7 +684,6 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) } } - /// /// This is called when the client receives the SCENE_EVENT of type SceneEventData.SceneEventTypes.SYNC /// Note: This can recurse one additional time by the client if the current scene loaded by the client @@ -794,7 +774,7 @@ private void MoveObjectsToDontDestroyOnLoad() { UnityEngine.Object.DontDestroyOnLoad(sobj.gameObject); } - else + else if (m_NetworkManager.IsServer) { sobj.Despawn(true); } @@ -898,12 +878,14 @@ private void HandleClientSceneEvent(Stream stream) } else { + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync_Complete; + SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); + // All scenes are synchronized, let the server know we are done synchronizing m_NetworkManager.IsConnectedClient = true; m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId); - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync_Complete; - SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId}); + } break; } diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkPrefabHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkPrefabHandler.cs index 0d5f80ca69..b3c3e54ebc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkPrefabHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkPrefabHandler.cs @@ -40,7 +40,8 @@ public interface INetworkPrefabInstanceHandler /// The most common approach is to make the inactive by calling . /// /// The being destroyed - void Destroy(NetworkObject networkObject); + /// (true) destroy the (false) do not destroy the + bool Destroy(NetworkObject networkObject); } /// @@ -278,7 +279,7 @@ internal NetworkObject HandleNetworkPrefabSpawn(uint networkPrefabAssetHash, ulo /// Will invoke the implementation's Destroy method /// /// - internal void HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) + internal bool HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) { var networkObjectInstanceHash = networkObjectInstance.GlobalObjectIdHash; @@ -288,14 +289,16 @@ internal void HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) var networkPrefabAssetHash = m_PrefabInstanceToPrefabAsset[networkObjectInstanceHash]; if (m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabAssetHash)) { - m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Destroy(networkObjectInstance); + return m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Destroy(networkObjectInstance); } } else // Otherwise the NetworkObject is the source NetworkPrefab if (m_PrefabAssetToPrefabHandler.ContainsKey(networkObjectInstanceHash)) { - m_PrefabAssetToPrefabHandler[networkObjectInstanceHash].Destroy(networkObjectInstance); + return m_PrefabAssetToPrefabHandler[networkObjectInstanceHash].Destroy(networkObjectInstance); } + + return true; } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs index c7c9e4345c..b1ca9ee13c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs @@ -615,7 +615,6 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec // As long as we have any remaining clients, then notify of the object being destroy. if (NetworkManager.ConnectedClientsList.Count > 0) { - ulong[] clientIds = NetworkManager.ConnectedClientsIds; var context = messageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.DestroyObject, NetworkChannel.Internal, @@ -638,7 +637,15 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec { if (NetworkManager.PrefabHandler.ContainsHandler(networkObject)) { - NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObject); + if(NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObject)) + { + if (SpawnedObjects.Remove(networkObject.NetworkObjectId)) + { + SpawnedObjectsList.Remove(networkObject); + } + UnityEngine.Object.Destroy(gobj); + return; + } } else { diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkPrefabHandlerTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkPrefabHandlerTests.cs index c360fb518a..9af7765da3 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkPrefabHandlerTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkPrefabHandlerTests.cs @@ -173,12 +173,12 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return networkObjectInstance; } - public void Destroy(NetworkObject networkObject) + public bool Destroy(NetworkObject networkObject) { var instancesContainsNetworkObject = m_Instances.Contains(networkObject); Assert.True(instancesContainsNetworkObject); m_Instances.Remove(networkObject); - UnityEngine.Object.Destroy(networkObject.gameObject); + return true; } public bool StillHasInstances() diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkSceneManagerTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkSceneManagerTests.cs index 485ac21bda..8835afea84 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkSceneManagerTests.cs @@ -14,7 +14,7 @@ public void SwitchSceneWithoutSceneManagement() var threwException = false; try { - networkManager.SceneManager.SwitchScene("SomeSceneNane"); + networkManager.SceneManager.LoadScene("SomeSceneNane",UnityEngine.SceneManagement.LoadSceneMode.Single); } catch (Exception ex) { diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity index 9e1a37e424..b30bb53f36 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene1.unity @@ -217,6 +217,7 @@ MonoBehaviour: RandomMovement: 0 AutoSpawnEnable: 1 SpawnInSourceScene: 0 + DestroyOnUnload: 1 InitialSpawnDelay: 0.2 SpawnsPerSecond: 1 PoolSize: 32 diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity index bd5e1f182f..8dc2e1ff99 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveScene3.unity @@ -247,6 +247,7 @@ MonoBehaviour: RandomMovement: 0 AutoSpawnEnable: 1 SpawnInSourceScene: 1 + DestroyOnUnload: 1 InitialSpawnDelay: 0.2 SpawnsPerSecond: 3 PoolSize: 32 diff --git a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs index cf6f888b01..041b3e91e9 100644 --- a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs +++ b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs @@ -132,12 +132,7 @@ private void Update() { m_ShouldDespawn = false; - NetworkObject.Despawn(HasHandler); - if (!HasHandler) - { - NetworkObject.gameObject.SetActive(false); - NetworkObject.gameObject.transform.position = Vector3.zero; - } + NetworkObject.Despawn(true); } else if (!IsServer) { @@ -152,7 +147,6 @@ private void Update() } } } - } [HideInInspector] diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index feca0c6455..572a9d4d84 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -122,26 +122,22 @@ private void OnUnloadScene() { StopCoroutine(SpawnObjects()); } - CleanNetworkObjects(); + + // De-register the custom prefab handler DeregisterCustomPrefabHandler(); + + CleanNetworkObjects(); + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; } - public override void OnNetworkDespawn() - { - // DeregisterCustomPrefabHandler(); - } - private void OnDisable() + public override void OnNetworkDespawn() { if (NetworkManager != null) { NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; } - StopCoroutine(SpawnObjects()); - - CleanNetworkObjects(); - } private void CleanNetworkObjects() @@ -412,7 +408,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni } return null; } - public void Destroy(NetworkObject networkObject) + public bool Destroy(NetworkObject networkObject) { var genericBehaviour = networkObject.gameObject.GetComponent(); if (genericBehaviour.IsRegisteredPoolObject) @@ -421,9 +417,12 @@ public void Destroy(NetworkObject networkObject) } else { - Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); - Object.DestroyImmediate(networkObject.gameObject); + return true; + //// NSS TODO: Remove before converting from draft to PR + //Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); + //Object.DestroyImmediate(networkObject.gameObject); } + return false; } public MyCustomPrefabSpawnHandler(NetworkPrefabPool objectPool) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 9caced9b0c..5a09e71082 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -13,7 +13,12 @@ public class NetworkPrefabPoolAdditive : NetworkBehaviour [Header("General Settings")] public bool RandomMovement = true; public bool AutoSpawnEnable = true; + [Tooltip("When enabled, this will spawn the objects in the source spawn generator's scene")] public bool SpawnInSourceScene = true; + + [Tooltip("When enabled, this will despawn or destroy all associated network prefab instances when the additive scene is unloaded.")] + public bool DestroyOnUnload = false; + public float InitialSpawnDelay; public int SpawnsPerSecond; public int PoolSize; @@ -118,6 +123,14 @@ private void OnSceneEvent(AsyncOperation operation,SceneEventData.SceneEventType { switch(sceneEventType) { + case SceneEventData.SceneEventTypes.Event_Unload: + { + if (loadSceneMode == LoadSceneMode.Additive && (gameObject.scene.name == sceneName)) + { + OnUnloadScene(); + } + break; + } case SceneEventData.SceneEventTypes.Event_Load: { if (loadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneName) || !SpawnInSourceScene) ) @@ -135,15 +148,33 @@ private void CleanNetworkObjects() { foreach (var obj in m_ObjectPool) { + if(obj == null) + { + continue; + } var networkObject = obj.GetComponent(); var genericBehaviour = obj.GetComponent(); genericBehaviour.IsRegisteredPoolObject = false; genericBehaviour.IsRemovedFromPool = true; + genericBehaviour.HasHandler = false; if (IsServer) { if (networkObject.IsSpawned) { - networkObject.Despawn(true); + if (DestroyOnUnload) + { + networkObject.Despawn(true); + } + else if (SpawnInSourceScene) + { + // If we are spawning in the source scene and we are not supposed to destroy this object + // then move it to the currently active scene. + var activeScene = SceneManager.GetActiveScene(); + if (gameObject.scene != SceneManager.GetActiveScene()) + { + SceneManager.MoveGameObjectToScene(obj, activeScene); + } + } } else { @@ -156,6 +187,16 @@ private void CleanNetworkObjects() { DestroyImmediate(obj); } + else if (SpawnInSourceScene) + { + // If we are spawning in the source scene and we are not supposed to destroy this object + // then move it to the currently active scene. + var activeScene = SceneManager.GetActiveScene(); + if (gameObject.scene != SceneManager.GetActiveScene()) + { + SceneManager.MoveGameObjectToScene(obj, activeScene); + } + } } } m_ObjectPool.Clear(); @@ -175,6 +216,10 @@ private void OnUnloadScene() // De-register the custom prefab handler DeregisterCustomPrefabHandler(); + + CleanNetworkObjects(); + + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; } /// @@ -250,6 +295,11 @@ public GameObject GetObject() { foreach (var obj in m_ObjectPool) { + if(obj == null) + { + continue; + } + if (!obj.activeInHierarchy) { obj.SetActive(true); @@ -410,7 +460,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni } return null; } - public void Destroy(NetworkObject networkObject) + public bool Destroy(NetworkObject networkObject) { var genericBehaviour = networkObject.gameObject.GetComponent(); if (genericBehaviour.IsRegisteredPoolObject) @@ -420,9 +470,11 @@ public void Destroy(NetworkObject networkObject) } else { - Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); - Object.DestroyImmediate(networkObject.gameObject); + return true; + //Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); + //Object.DestroyImmediate(networkObject.gameObject); } + return false; } public MyAdditiveCustomPrefabSpawnHandler(NetworkPrefabPoolAdditive objectPool) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs index bd06336172..483b459fa1 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs @@ -25,7 +25,7 @@ public override void OnNetworkSpawn() Debug.Log("OnNotifyServerAllClientsLoadedScene invoked on the host - Passed"); }; - NetworkManager.SceneManager.SwitchScene("SceneWeAreSwitchingTo"); + NetworkManager.SceneManager.LoadScene("SceneWeAreSwitchingTo",UnityEngine.SceneManagement.LoadSceneMode.Single); } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs b/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs index 6a70426996..1ae3646de6 100644 --- a/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs +++ b/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs @@ -106,7 +106,7 @@ public void OnSwitchScene() OnSceneSwitchBegin?.Invoke(); m_ExitingScene = true; ExitingNow = true; - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.SwitchScene(m_SceneToSwitchTo); + m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo, UnityEngine.SceneManagement.LoadSceneMode.Single); m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; } diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs index 430140d387..306d446027 100644 --- a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs @@ -29,7 +29,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return networkObject; } - public void Destroy(NetworkObject networkObject) + public bool Destroy(NetworkObject networkObject) { var behaviour = networkObject.gameObject.GetComponent(); // todo expensive, only used in teardown for now, should optimize eventually m_OnRelease(behaviour); @@ -37,6 +37,7 @@ public void Destroy(NetworkObject networkObject) netTransform.position = Vector3.zero; netTransform.rotation = Quaternion.identity; m_ObjectPool.Release(behaviour); + return true; } public void Dispose() diff --git a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs index 24f11b286f..15b3ed28c1 100644 --- a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs +++ b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs @@ -58,7 +58,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return networkObject; } - public void Destroy(NetworkObject networkObject) + public bool Destroy(NetworkObject networkObject) { WasDestroyed = true; if (networkObject.NetworkManager.IsClient) @@ -66,7 +66,7 @@ public void Destroy(NetworkObject networkObject) Assert.AreEqual(NetworkUpdateStage.PostLateUpdate, NetworkUpdateLoop.UpdateStage); } - Object.Destroy(networkObject.gameObject); + return true; } } } From d7822d6cd7d874c029abe07da0f329ffaf77f8a6 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 3 Aug 2021 16:20:50 -0500 Subject: [PATCH 046/106] fix merge fix. --- .../Runtime/SceneManagement/SceneEventData.cs | 4 +--- .../SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs | 1 - .../SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs | 1 - .../Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs | 2 -- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index ac873facd8..0e1fba0227 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -2,13 +2,11 @@ using System; using System.IO; using System.Linq; -using Unity.Multiplayer.Netcode.Serialization.Pooled; using UnityEngine; using UnityEngine.SceneManagement; -using Unity.Multiplayer.Netcode.Serialization; -namespace Unity.Multiplayer.Netcode.SceneManagement +namespace Unity.Multiplayer.Netcode { [Serializable] public class SceneEventData : IDisposable diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index 3192f5a0b5..1c66108ead 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -6,7 +6,6 @@ #endif using Unity.Multiplayer.Netcode; -using Unity.Multiplayer.Netcode.SceneManagement; namespace TestProject.ManualTests { diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index 8d953de707..e16ebca756 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -6,7 +6,6 @@ #endif using Unity.Multiplayer.Netcode; -using Unity.Multiplayer.Netcode.SceneManagement; namespace TestProject.ManualTests { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 5a09e71082..3b5e42f884 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -3,8 +3,6 @@ using UnityEngine; using UnityEngine.SceneManagement; using Unity.Multiplayer.Netcode; -using Unity.Multiplayer.Netcode.Spawning; -using Unity.Multiplayer.Netcode.SceneManagement; namespace TestProject.ManualTests { From 3274c561ebfbb12e22c58de4bd7c426d073a6603 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 3 Aug 2021 17:08:13 -0500 Subject: [PATCH 047/106] refactor and fix Refactoring the Stats Display a bit. Added check for NetworkBehaviour Updater to not try to access the NetworkObject if it is null. This can happen under scenarios where too many objects are being spawned and destroyed at the same time. --- .../Runtime/Core/NetworkBehaviourUpdater.cs | 34 ++++++++---- .../Tests/Manual/Scripts/StatsDisplay.cs | 52 +++++++++---------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs index 0981ddc4c9..cb447bd998 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs @@ -28,10 +28,14 @@ internal void NetworkBehaviourUpdate(NetworkManager networkManager) m_Touched.UnionWith(spawnedObjs); foreach (var sobj in spawnedObjs) { - // Sync just the variables for just the objects this client sees - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + // Under specific conditions this can become null, so we don't want to access it if it is null + if (sobj != null) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId); + // Sync just the variables for just the objects this client sees + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + { + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId); + } } } } @@ -39,9 +43,13 @@ internal void NetworkBehaviourUpdate(NetworkManager networkManager) // Now, reset all the no-longer-dirty variables foreach (var sobj in m_Touched) { - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + // Under specific conditions this can become null, so we don't want to access it if it is null + if (sobj != null) { - sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + { + sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); + } } } } @@ -50,18 +58,26 @@ internal void NetworkBehaviourUpdate(NetworkManager networkManager) // when client updates the server, it tells it about all its objects foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList) { - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + // Under specific conditions this can become null, so we don't want to access it if it is null + if (sobj != null) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId); + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + { + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId); + } } } // Now, reset all the no-longer-dirty variables foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList) { - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + // Under specific conditions this can become null, so we don't want to access it if it is null + if (sobj != null) { - sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) + { + sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); + } } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs index 61409af8a9..b54be296f0 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs @@ -5,6 +5,7 @@ using UnityEngine.UI; using Unity.Multiplayer.Netcode; + namespace TestProject.ManualTests { public class StatsDisplay : NetworkBehaviour @@ -37,6 +38,9 @@ private void Start() { m_ClientServerToggle.SetActive(false); } + + Unity.Multiplayer.Netcode.Transports.UNET.UNetTransport.ProfilerEnabled = true; + } public override void OnNetworkSpawn() @@ -51,6 +55,8 @@ public override void OnNetworkSpawn() m_ClientServerToggle.SetActive(true); UpdateButton(); } + + StartCoroutine("UpdateTextStatus"); } @@ -125,25 +131,8 @@ private void UpdateButton() private void ReceiveStatsClientRpc(StatsInfoContainer statsinfo) { m_LastStatsDump = "Server Stats"; - m_LastStatsDump += "\ndeltaTime: [" + Time.deltaTime.ToString() + "]"; - /* if (ProfilerStatManager.AllStats.Count != statsinfo.StatValues.Count) - { - Debug.LogError("[StatsDisplay-Error][Mismatch] Received " + statsinfo.StatValues.Count.ToString() + " values and have " + ProfilerStatManager.AllStats.Count.ToString() + " profiler stats entries!"); - } - else - { - var statsCounter = 0; - foreach (ProfilerStat p in ProfilerStatManager.AllStats) - { - if (m_LastStatsDump != string.Empty) - { - m_LastStatsDump += "\n"; - } - m_LastStatsDump += p.PrettyPrintName + ": " + statsinfo.StatValues[statsCounter].ToString(("0.0")); - statsCounter++; - } - m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}"; - }*/ + m_LastStatsDump += "\ndeltaTime: [" + statsinfo.StatValues[0] + "]"; + m_LastStatsDump += $"\nActive Scene: {SceneManager.GetActiveScene().name}"; } /// @@ -177,14 +166,24 @@ private IEnumerator UpdateTextStatus() { m_LastStatsDump = m_IsServer ? "Server Stats" : "Client Stats"; m_LastStatsDump += "\ndeltaTime: [" + Time.deltaTime.ToString() + "]"; - /* foreach (ProfilerStat p in ProfilerStatManager.AllStats) + var profileData = NetworkObject.NetworkManager.Transport.GetTransportProfilerData(); + + if (profileData.Count > 0) { - if (m_LastStatsDump != string.Empty) + foreach (var entry in profileData) { - m_LastStatsDump += "\n"; + if (m_LastStatsDump != string.Empty) + { + m_LastStatsDump += "\n"; + } + m_LastStatsDump += entry.Key + ": " + entry.Value.ToString("0.0"); } - m_LastStatsDump += p.PrettyPrintName + ": " + p.SampleRate().ToString("0.0"); - }*/ + } + else + { + m_LastStatsDump += "\n"; + } + m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}"; } @@ -192,10 +191,7 @@ private IEnumerator UpdateTextStatus() { var statsInfoContainer = new StatsInfoContainer(); statsInfoContainer.StatValues = new List(); - /* foreach (ProfilerStat p in ProfilerStatManager.AllStats) - { - statsInfoContainer.StatValues.Add(p.SampleRate()); - } */ + statsInfoContainer.StatValues.Add(Time.deltaTime); ReceiveStatsClientRpc(statsInfoContainer); } } From 5c4de91ff5878b18a941dc2b500e3802f95d0f90 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 3 Aug 2021 17:21:25 -0500 Subject: [PATCH 048/106] style Fixing non-required using statements. --- .../Runtime/Messaging/InternalMessageHandler.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 115c7e8842..903bdab5ed 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -1,8 +1,6 @@ - using System.IO; using UnityEngine; -using UnityEngine.Events; -using UnityEngine.SceneManagement; + namespace Unity.Multiplayer.Netcode { From 3f98c0ab0a32c7e27d128babcfefa9cb9c64f218 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 12:23:49 -0500 Subject: [PATCH 049/106] refactor and style Refactored the prefab pool examples for the changed INetworkPrefabInstanceHandler interface. Made some adjustments to the code based on suggestions. In particular added a prefix to the SceneEventData.SceneEventTypes to help identify which is server to client and client to server. --- .../Runtime/Core/NetworkManager.cs | 17 ++--- .../SceneManagement/NetworkSceneManager.cs | 65 ++++++++++--------- .../Runtime/SceneManagement/SceneEventData.cs | 49 ++++++++------ .../NetworkPrefabHandlerObjectPool.cs | 3 +- .../NetworkPrefabHandlerObjectPoolOverride.cs | 4 +- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 2 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 4 +- .../Assets/Tests/Runtime/SceneLoadingTest.cs | 3 +- 8 files changed, 78 insertions(+), 69 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 8f6ac86300..104c1a6032 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -272,18 +272,15 @@ private void OnValidate() if (NetworkConfig.EnableSceneManagement) { - if(NetworkConfig.RegisteredSceneAssets.Count > 0) + foreach(var sceneAsset in NetworkConfig.RegisteredSceneAssets) { - foreach(var sceneAsset in NetworkConfig.RegisteredSceneAssets) + if(NetworkConfig.RegisteredScenes == null) { - if(NetworkConfig.RegisteredScenes == null) - { - NetworkConfig.RegisteredScenes = new List(); - } - if(!NetworkConfig.RegisteredScenes.Contains(sceneAsset.name)) - { - NetworkConfig.RegisteredScenes.Add(sceneAsset.name); - } + NetworkConfig.RegisteredScenes = new List(); + } + if(!NetworkConfig.RegisteredScenes.Contains(sceneAsset.name)) + { + NetworkConfig.RegisteredScenes.Add(sceneAsset.name); } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 23407ee4b0..ed45d27afc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -84,7 +84,7 @@ public class NetworkSceneManager private static bool s_IsSceneEventActive = false; internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; - internal static bool IsRunningUnitTest = false; + //Client and Server: used for all scene event processing exception for client synchronization internal SceneEventData SceneEventData; @@ -109,21 +109,25 @@ internal NetworkSceneManager(NetworkManager networkManager) internal void SendSceneEventData(ulong[] targetClientIds) { - if (targetClientIds.Length >= 1) + if (targetClientIds.Length == 0) { - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, targetClientIds, m_NetworkUpdateStage); + Debug.LogWarning($"Attempting to send {nameof(SceneEventData)} to zero (0) clients!"); + return; + } - if (context != null) - { - using (var nonNullContext = (InternalCommandContext)context) - { - SceneEventData.OnWrite(nonNullContext.NetworkWriter); - } + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, targetClientIds, m_NetworkUpdateStage); - return; + if (context != null) + { + using (var nonNullContext = (InternalCommandContext)context) + { + SceneEventData.OnWrite(nonNullContext.NetworkWriter); } - throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientIds {targetClientIds}!"); + return; } + + // This should never happen, but if it does something very bad has happened and we should throw an exception + throw new Exception($"{nameof(InternalCommandContext)} is null! {nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientIds {targetClientIds}!"); } /// @@ -282,13 +286,12 @@ public SceneSwitchProgress UnloadScene(string sceneName) } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Unload; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; // Sends the unload scene notification SendSceneEventData(m_NetworkManager.ConnectedClientsIds); - // start loading the scene AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; switchSceneProgress.SetSceneLoadOperation(sceneUnload); @@ -334,7 +337,7 @@ private void OnSceneUnloaded() { if (!m_NetworkManager.IsServer) { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Unload_Complete; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); } s_IsSceneEventActive = false; @@ -356,7 +359,7 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo } SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Load; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Load; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; SceneEventData.LoadSceneMode = loadSceneMode; @@ -393,12 +396,12 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - OnSceneEvent?.Invoke(null, SceneEventData.SceneEventTypes.Event_Unload, SceneEventData.LoadSceneMode, SceneManager.GetActiveScene().name); + OnSceneEvent?.Invoke(null, SceneEventData.SceneEventTypes.S2C_Event_Unload, SceneEventData.LoadSceneMode, SceneManager.GetActiveScene().name); foreach (var additiveSceneName in m_ScenesLoaded) { if (currentActiveScene.name != additiveSceneName) { - OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(additiveSceneName), SceneEventData.SceneEventTypes.Event_Unload, LoadSceneMode.Additive, additiveSceneName); + OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(additiveSceneName), SceneEventData.SceneEventTypes.S2C_Event_Unload, LoadSceneMode.Additive, additiveSceneName); } } m_ScenesLoaded.Clear(); @@ -449,7 +452,7 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) { if (currentActiveScene.name != loadedSceneName) { - OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(loadedSceneName), SceneEventData.SceneEventTypes.Event_Unload, LoadSceneMode.Additive, loadedSceneName); + OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(loadedSceneName), SceneEventData.SceneEventTypes.S2C_Event_Unload, LoadSceneMode.Additive, loadedSceneName); } } m_ScenesLoaded.Clear(); @@ -603,7 +606,7 @@ private void OnClientLoadedScene() } } - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Load_Complete; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); s_IsSceneEventActive = false; @@ -632,7 +635,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.InitializeForSynch(); ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); - ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync; + ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Sync; for (int i = 0; i < SceneManager.sceneCount; i++) { @@ -853,17 +856,17 @@ private void HandleClientSceneEvent(Stream stream) { // Both events are basically the same with some minor differences //case SceneEventData.SceneEventTypes.EventSwitch: - case SceneEventData.SceneEventTypes.Event_Load: + case SceneEventData.SceneEventTypes.S2C_Event_Load: { OnClientSceneLoadingEvent(stream); break; } - case SceneEventData.SceneEventTypes.Event_Unload: + case SceneEventData.SceneEventTypes.S2C_Event_Unload: { OnClientUnloadScene(); break; } - case SceneEventData.SceneEventTypes.Event_Sync: + case SceneEventData.SceneEventTypes.S2C_Event_Sync: { if (!SceneEventData.IsDoneWithSynchronization()) { @@ -871,7 +874,7 @@ private void HandleClientSceneEvent(Stream stream) } else { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_Sync_Complete; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Sync_Complete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); // All scenes are synchronized, let the server know we are done synchronizing @@ -882,7 +885,7 @@ private void HandleClientSceneEvent(Stream stream) } break; } - case SceneEventData.SceneEventTypes.Event_ReSync: + case SceneEventData.SceneEventTypes.S2C_Event_ReSync: { break; } @@ -903,9 +906,9 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { switch (SceneEventData.SceneEventType) { - case SceneEventData.SceneEventTypes.Event_Load_Complete: + case SceneEventData.SceneEventTypes.C2S_Event_Load_Complete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); @@ -916,17 +919,17 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) } break; } - case SceneEventData.SceneEventTypes.Event_Unload_Complete: + case SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.Event_Unload_Complete)}] Client Id {clientId} finished unloading additive scene."); + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete)}] Client Id {clientId} finished unloading additive scene."); break; } - case SceneEventData.SceneEventTypes.Event_Sync_Complete: + case SceneEventData.SceneEventTypes.C2S_Event_Sync_Complete: { if (SceneEventData.ClientNeedsReSynchronization()) { Debug.Log($"Re-Synchronizing client {clientId} for missed destroyed NetworkObjects."); - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.Event_ReSync; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_ReSync; SendSceneEventData(new ulong[] { clientId }); } // NSS TOOD: The scene event local notification needs to be called diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 0e1fba0227..aaf1409bbc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -11,17 +11,23 @@ namespace Unity.Multiplayer.Netcode [Serializable] public class SceneEventData : IDisposable { + /// + /// The different types of scene events communicated between a server and client. + /// Scene event types can be: + /// A Server To Client Event (S2C) + /// A Client to Server Event (C2S) + /// public enum SceneEventTypes { - Event_Load, //Server to client load additive scene - Event_Unload, //Server to client unload additive scene - Event_SetActive, //Server to client make scene active - Event_Sync, //Server to client late join approval synchronization - Event_ReSync, //Server to client update of objects that were destroyed during sync - Event_Load_Complete, //Client to server - Event_Unload_Complete, //Client to server - Event_SetActiveComplete, //Client to server - Event_Sync_Complete, //Client to server + S2C_Event_Load, //Server to client load additive scene + S2C_Event_Unload, //Server to client unload additive scene + S2C_Event_SetActive, //Server to client make scene active + S2C_Event_Sync, //Server to client late join approval synchronization + S2C_Event_ReSync, //Server to client update of objects that were destroyed during sync + C2S_Event_Load_Complete, //Client to server + C2S_Event_Unload_Complete, //Client to server + C2S_Event_SetActiveComplete, //Client to server + C2S_Event_Sync_Complete, //Client to server } public SceneEventTypes SceneEventType; @@ -118,10 +124,11 @@ public bool IsSceneEventClientSide() { switch (SceneEventType) { - case SceneEventTypes.Event_Load: - case SceneEventTypes.Event_Unload: - case SceneEventTypes.Event_Sync: - case SceneEventTypes.Event_ReSync: + case SceneEventTypes.S2C_Event_Load: + case SceneEventTypes.S2C_Event_Unload: + case SceneEventTypes.S2C_Event_Sync: + case SceneEventTypes.S2C_Event_ReSync: + case SceneEventTypes.S2C_Event_SetActive: { return true; } @@ -164,14 +171,14 @@ public void OnWrite(NetworkWriter writer) writer.WriteByte((byte)LoadSceneMode); - if (SceneEventType != SceneEventTypes.Event_Sync) + if (SceneEventType != SceneEventTypes.S2C_Event_Sync) { writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); } writer.WriteUInt32Packed(SceneIndex); - if (SceneEventType == SceneEventTypes.Event_Sync) + if (SceneEventType == SceneEventTypes.S2C_Event_Sync) { writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); @@ -215,12 +222,12 @@ public void OnWrite(NetworkWriter writer) } } - if (SceneEventType == SceneEventTypes.Event_Sync_Complete) + if (SceneEventType == SceneEventTypes.C2S_Event_Sync_Complete) { WriteClientSynchronizationResults(writer); } - if (SceneEventType == SceneEventTypes.Event_ReSync) + if (SceneEventType == SceneEventTypes.S2C_Event_ReSync) { WriteClientReSynchronizationData(writer); } @@ -256,14 +263,14 @@ public void OnRead(NetworkReader reader) // NSS TODO: Add to proposal's MTT discussion topics: Should we assert here? } - if (SceneEventType != SceneEventTypes.Event_Sync) + if (SceneEventType != SceneEventTypes.S2C_Event_Sync) { SwitchSceneGuid = new Guid(reader.ReadByteArray()); } SceneIndex = reader.ReadUInt32Packed(); - if (SceneEventType == SceneEventTypes.Event_Sync) + if (SceneEventType == SceneEventTypes.S2C_Event_Sync) { m_NetworkObjectsSync.Clear(); var keyPairCount = reader.ReadInt32Packed(); @@ -296,12 +303,12 @@ public void OnRead(NetworkReader reader) } } - if (SceneEventType == SceneEventTypes.Event_Sync_Complete) + if (SceneEventType == SceneEventTypes.C2S_Event_Sync_Complete) { CheckClientSynchronizationResults(reader); } - if (SceneEventType == SceneEventTypes.Event_ReSync) + if (SceneEventType == SceneEventTypes.S2C_Event_ReSync) { ReadClientReSynchronizationData(reader); } diff --git a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs index eecba0456a..f40ce6d3e9 100644 --- a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs +++ b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs @@ -76,12 +76,13 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return gameObject.GetComponent(); } - public void Destroy(NetworkObject networkObject) + public bool Destroy(NetworkObject networkObject) { if (m_ObjectsPool.Contains(networkObject.gameObject)) { networkObject.gameObject.SetActive(false); } + return false; } private IEnumerator SpawnObjects() diff --git a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs index b2af5b2e5a..2f221db268 100644 --- a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs +++ b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs @@ -139,12 +139,14 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return gameObject.GetComponent(); } - public void Destroy(NetworkObject networkObject) + public bool Destroy(NetworkObject networkObject) { if (m_NameValidation.Contains(networkObject.gameObject.name)) { networkObject.gameObject.SetActive(false); } + + return false; } /// diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 3df70f022c..80dd26cee7 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -99,7 +99,7 @@ private void OnSceneEvent(AsyncOperation operation, SceneEventData.SceneEventTyp { switch (sceneEventType) { - case SceneEventData.SceneEventTypes.Event_Unload: + case SceneEventData.SceneEventTypes.S2C_Event_Unload: { if (loadSceneMode == LoadSceneMode.Single && (gameObject.scene.name == sceneName)) { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 3b5e42f884..c95a800db4 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -121,7 +121,7 @@ private void OnSceneEvent(AsyncOperation operation,SceneEventData.SceneEventType { switch(sceneEventType) { - case SceneEventData.SceneEventTypes.Event_Unload: + case SceneEventData.SceneEventTypes.S2C_Event_Unload: { if (loadSceneMode == LoadSceneMode.Additive && (gameObject.scene.name == sceneName)) { @@ -129,7 +129,7 @@ private void OnSceneEvent(AsyncOperation operation,SceneEventData.SceneEventType } break; } - case SceneEventData.SceneEventTypes.Event_Load: + case SceneEventData.SceneEventTypes.S2C_Event_Load: { if (loadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneName) || !SpawnInSourceScene) ) { diff --git a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs index ebc9674805..74e20f1df7 100644 --- a/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs +++ b/testproject/Assets/Tests/Runtime/SceneLoadingTest.cs @@ -245,14 +245,13 @@ private void SceneManager_sceneLoaded(Scene scene, LoadSceneMode sceneMode) [UnitySetUp] private IEnumerator SetUp() { - MLAPI.SceneManagement.NetworkSceneManager.IsRunningUnitTest = true; + yield return null; } [UnityTearDown] private IEnumerator TearDown() { - MLAPI.SceneManagement.NetworkSceneManager.IsRunningUnitTest = false; yield return null; From c31c119c4e18f1dbdc6f9828648600bfc0011dc7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 12:33:06 -0500 Subject: [PATCH 050/106] refactor Removing completely irrelevant code... --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 104c1a6032..7cc238b190 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -274,10 +274,6 @@ private void OnValidate() { foreach(var sceneAsset in NetworkConfig.RegisteredSceneAssets) { - if(NetworkConfig.RegisteredScenes == null) - { - NetworkConfig.RegisteredScenes = new List(); - } if(!NetworkConfig.RegisteredScenes.Contains(sceneAsset.name)) { NetworkConfig.RegisteredScenes.Add(sceneAsset.name); From 69a4751a1590dfe6f5c4d98c2f5f7a7a94153d18 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 12:56:22 -0500 Subject: [PATCH 051/106] refactor Updates to account for the namespace change...again. --- .../Runtime/SceneManagement/SceneEventData.cs | 2 +- .../SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs | 2 +- .../Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs | 2 +- .../SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs | 2 +- .../Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs | 2 +- testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs | 3 --- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index aaf1409bbc..5c0ef90672 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -6,7 +6,7 @@ using UnityEngine.SceneManagement; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { [Serializable] public class SceneEventData : IDisposable diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index 1c66108ead..caea9bb069 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -5,7 +5,7 @@ using UnityEditor; #endif -using Unity.Multiplayer.Netcode; +using Unity.Netcode; namespace TestProject.ManualTests { diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs index eea7eef2ce..b4557947ac 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs @@ -1,5 +1,5 @@ using UnityEngine; -using Unity.Multiplayer.Netcode; +using Unity.Netcode; public class NetworkManagerMonitor : MonoBehaviour { diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index e16ebca756..8543181baa 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -5,7 +5,7 @@ using UnityEditor; #endif -using Unity.Multiplayer.Netcode; +using Unity.Netcode; namespace TestProject.ManualTests { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index c95a800db4..aaf75f561c 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; -using Unity.Multiplayer.Netcode; +using Unity.Netcode; namespace TestProject.ManualTests { diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs index 113ef4f2b7..2e78bee08c 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs @@ -38,9 +38,6 @@ private void Start() { m_ClientServerToggle.SetActive(false); } - - Unity.Multiplayer.Netcode.Transports.UNET.UNetTransport.ProfilerEnabled = true; - } public override void OnNetworkSpawn() From 5cffe56f7873507b9674d78bc3c05a55b81b32cb Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 16:06:20 -0500 Subject: [PATCH 052/106] feat Implemented the full scene event notification system that provides detailed information for both client(s) and server. Made some modifications to the StatsDisplay that provides visual queues for when each scene event occurs. --- .../SceneManagement/NetworkSceneManager.cs | 284 ++++++++++++++++-- .../Runtime/SceneManagement/SceneEventData.cs | 39 +-- .../Scripts/GenericNetworkObjectBehaviour.cs | 6 +- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 9 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 30 +- .../Tests/Manual/Scripts/StatsDisplay.cs | 42 ++- 6 files changed, 333 insertions(+), 77 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 56dd894703..931bafb18d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -7,6 +7,49 @@ namespace Unity.Netcode { + /// + /// Used for local notifications of various scene events. + /// The of delegate type uses this class to provide + /// scene event status/state. + /// + public class SceneEvent + { + /// + /// If applicable, this will be set to the returned by + /// load scene and unload scene asynchronous methods. + /// + public AsyncOperation AsyncOperation; + + /// + /// Will always be set to the current scene event type () this scene event notification pertains to + /// + public SceneEventData.SceneEventTypes SceneEventType; + + /// + /// If applicable, this reflects the type of scene loading or unloading that is occurring. + /// Unlike , scene unload events will have the original applied when the scene was loaded. + /// + public LoadSceneMode LoadSceneMode; + + /// + /// Excluding and + /// This will be set to the scene name that the event pertains to. + /// + public string SceneName; + + /// + /// This will be the relative client identifier that this event pertains to + /// is invoked both on the server and the client and set to the local client identifier. + /// is invoked both on the server and the client and set to the local client identifier. + /// is invoked when synchronizing a client, the ClientId will be the client being synchronized. + /// is invoked only on the server and the ClientId will be the client that sent the notification. + /// is invoked only on the server and the ClientId will be the client that sent the notification. + /// is invoked on both the client and the server when the client has finished synchronizing. + /// is invoked on the client only if the server determines it needs to be re-synchronized after synchronizing. + /// + public ulong ClientId; + } + /// /// Main class for managing network scenes /// @@ -22,7 +65,7 @@ public class NetworkSceneManager /// public delegate void SceneSwitchStartedDelegate(AsyncOperation operation); - public delegate void SceneEventDelegate(AsyncOperation operation, SceneEventData.SceneEventTypes sceneEventType, LoadSceneMode loadSceneMode, string sceneName); + public delegate void SceneEventDelegate(SceneEvent sceneEvent); /// /// Delegate for when a client has reported to the server that it has completed scene transition @@ -111,7 +154,8 @@ internal void SendSceneEventData(ulong[] targetClientIds) { if (targetClientIds.Length == 0) { - Debug.LogWarning($"Attempting to send {nameof(SceneEventData)} to zero (0) clients!"); + // This would be the server with no clients connected + // Silently return as there is nothing to be done return; } @@ -135,7 +179,7 @@ internal void SendSceneEventData(ulong[] targetClientIds) /// /// /// MLAPI Scene Index - internal uint GetMLAPISceneIndexFromScene(Scene scene) + internal uint GetNetcodeSceneIndexFromScene(Scene scene) { uint index = 0; if (!SceneNameToIndex.TryGetValue(scene.name, out index)) @@ -156,7 +200,7 @@ internal uint GetMLAPISceneIndexFromScene(Scene scene) /// /// MLAPI Scene Index /// scene name - internal string GetSceneNameFromMLAPISceneIndex(uint sceneIndex) + internal string GetSceneNameFromNetcodeSceneIndex(uint sceneIndex) { var sceneName = string.Empty; if (!SceneIndexToString.TryGetValue(sceneIndex, out sceneName)) @@ -290,7 +334,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; // Sends the unload scene notification - SendSceneEventData(m_NetworkManager.ConnectedClientsIds); + SendSceneEventData(m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray()); AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; @@ -299,15 +343,26 @@ public SceneSwitchProgress UnloadScene(string sceneName) { m_ScenesLoaded.Remove(sceneName); } - OnSceneEvent?.Invoke(sceneUnload,SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = sceneUnload, + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.ServerClientId + }); //Return our scene progress instance return switchSceneProgress; } + /// + /// SceneManager.UnloadSceneAsync handler for clients + /// private void OnClientUnloadScene() { - if (!SceneIndexToString.TryGetValue(SceneEventData.SceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) + var sceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex); + if (sceneName == string.Empty) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { @@ -327,7 +382,14 @@ private void OnClientUnloadScene() sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); - OnSceneEvent?.Invoke(sceneUnload, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = sceneUnload, + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId + }); } /// @@ -335,11 +397,21 @@ private void OnClientUnloadScene() /// private void OnSceneUnloaded() { + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete; if (!m_NetworkManager.IsServer) { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); } + + // Notify the client or server that a scene was unloaded + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + ClientId = m_NetworkManager.IsServer ? m_NetworkManager.ServerClientId : m_NetworkManager.LocalClientId + }); + s_IsSceneEventActive = false; } @@ -396,12 +468,24 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - OnSceneEvent?.Invoke(null, SceneEventData.SceneEventTypes.S2C_Event_Unload, SceneEventData.LoadSceneMode, SceneManager.GetActiveScene().name); + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = SceneManager.GetActiveScene().name, + ClientId = m_NetworkManager.ServerClientId + }); foreach (var additiveSceneName in m_ScenesLoaded) { if (currentActiveScene.name != additiveSceneName) { - OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(additiveSceneName), SceneEventData.SceneEventTypes.S2C_Event_Unload, LoadSceneMode.Additive, additiveSceneName); + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload, + LoadSceneMode = LoadSceneMode.Additive, + SceneName = additiveSceneName, + ClientId = m_NetworkManager.ServerClientId + }); } } m_ScenesLoaded.Clear(); @@ -423,7 +507,14 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene } } - OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = sceneLoad, + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.ServerClientId + }); } /// @@ -444,7 +535,7 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) return; } - // NSS TODO: This is a temporary place holder check to make sure we don't try to unload a scene loaded in single mode. + // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). var currentActiveScene = SceneManager.GetActiveScene(); if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { @@ -452,7 +543,14 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) { if (currentActiveScene.name != loadedSceneName) { - OnSceneEvent?.Invoke(SceneManager.UnloadSceneAsync(loadedSceneName), SceneEventData.SceneEventTypes.S2C_Event_Unload, LoadSceneMode.Additive, loadedSceneName); + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = SceneManager.UnloadSceneAsync(loadedSceneName), + SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload, + LoadSceneMode = LoadSceneMode.Additive, + SceneName = loadedSceneName, + ClientId = m_NetworkManager.LocalClientId + }); } } m_ScenesLoaded.Clear(); @@ -483,11 +581,20 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) var sceneLoad = SceneManager.LoadSceneAsync(sceneName, SceneEventData.LoadSceneMode); sceneLoad.completed += asyncOp2 => OnSceneLoaded(sceneName); + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = sceneLoad, + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId + }); + + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { OnSceneSwitchStarted?.Invoke(sceneLoad); } - OnSceneEvent?.Invoke(sceneLoad, SceneEventData.SceneEventType, SceneEventData.LoadSceneMode, sceneName); } /// @@ -509,7 +616,6 @@ private void OnSceneLoaded(string sceneName) { // Move all objects to the new scene MoveObjectsToScene(nextScene); - } // 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 @@ -541,6 +647,7 @@ private void OnServerLoadedScene() } } + // Send all clients the scene load event for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++) { var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId; @@ -588,6 +695,16 @@ private void OnServerLoadedScene() } s_IsSceneEventActive = false; + + // Notify local server that the scene was loaded + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + ClientId = m_NetworkManager.ServerClientId + }); + OnSceneSwitched?.Invoke(); } @@ -608,9 +725,18 @@ private void OnClientLoadedScene() SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); - s_IsSceneEventActive = false; + // Notify local client that the scene was loaded + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + ClientId = m_NetworkManager.LocalClientId + }); + + OnSceneSwitched?.Invoke(); } @@ -637,11 +763,12 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) var activeScene = SceneManager.GetActiveScene(); ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Sync; + // Organize how (and when) we serialize our NetworkObjects for (int i = 0; i < SceneManager.sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); - var malpiSceneIndex = GetMLAPISceneIndexFromScene(scene); + var malpiSceneIndex = GetNetcodeSceneIndexFromScene(scene); if (malpiSceneIndex == uint.MaxValue) { @@ -678,6 +805,14 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.OnWrite(nonNullContext.NetworkWriter); } } + + // Notify the local server that the client has been sent the SceneEventData.SceneEventTypes.S2C_Event_Sync event + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + ClientId = ownerClientId + }); + } /// @@ -699,11 +834,32 @@ private void OnClientBeginSync(uint sceneIndex) } var activeScene = SceneManager.GetActiveScene(); + var loadSceneMode = sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive; + + // If this is the beginning of the synchronization event, then send client a notification that synchronization has begun + if(sceneIndex == SceneEventData.SceneIndex) + { + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Sync, + ClientId = m_NetworkManager.LocalClientId, + }); + } if (sceneName != activeScene.name) { - // NSS TODO: Add to proposal's MTT discussion topics: Should we cover switching the active scene for V1.0.0? - var sceneLoad = SceneManager.LoadSceneAsync(sceneName, sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive); + 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_Event_Load, + LoadSceneMode = loadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId, + }); + sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex); } else @@ -719,15 +875,18 @@ private void OnClientBeginSync(uint sceneIndex) /// MLAPI scene index that was loaded private void ClientLoadedSynchronization(uint sceneIndex) { - var nextScene = SceneManager.GetSceneByName(GetSceneNameFromMLAPISceneIndex(sceneIndex)); + var sceneName = GetSceneNameFromNetcodeSceneIndex(sceneIndex); + var nextScene = SceneManager.GetSceneByName(sceneName); if (nextScene == null) { Debug.LogError($"Client was trying to load {sceneIndex} which does not appear to be a valid registered scene index!"); return; } - // NSS TODO: Add to proposal's MTT discussion topics: Should we cover switching the active scene for V1.0.0? - if ((sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive) == LoadSceneMode.Single) + var loadSceneMode = (sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive); + + // For now, during a synchronization event, we will make the first scene the "base/master" scene that denotes a "complete scene switch" + if (loadSceneMode == LoadSceneMode.Single) { SceneManager.SetActiveScene(nextScene); } @@ -738,6 +897,30 @@ private void ClientLoadedSynchronization(uint sceneIndex) // Synchronize the NetworkObjects for this scene SceneEventData.SynchronizeSceneNetworkObjects(sceneIndex, m_NetworkManager); + // Send notification back to server that we finished loading this scene + ClientSynchEventData.LoadSceneMode = loadSceneMode; + ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete; + ClientSynchEventData.SceneIndex = sceneIndex; + + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, + new ulong[] { m_NetworkManager.ServerClientId }, m_NetworkUpdateStage); + if (context != null) + { + using (var nonNullContext = (InternalCommandContext)context) + { + ClientSynchEventData.OnWrite(nonNullContext.NetworkWriter); + } + } + + // Send notification to local client that the scene has finished loading + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete, + LoadSceneMode = loadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId, + }); + // Check to see if we still have scenes to load and synchronize with HandleClientSceneEvent(null); } @@ -881,12 +1064,24 @@ private void HandleClientSceneEvent(Stream stream) m_NetworkManager.IsConnectedClient = true; m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId); - + // Notify the client that they have finished synchronizing + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + ClientId = m_NetworkManager.LocalClientId + }); } break; } case SceneEventData.SceneEventTypes.S2C_Event_ReSync: { + // Notify the client that they have been re-synchronized after being synchronized with an in progress game session + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + ClientId = m_NetworkManager.LocalClientId + }); + break; } default: @@ -909,31 +1104,60 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) case SceneEventData.SceneEventTypes.C2S_Event_Load_Complete: { Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); } - else + + // Notify the local server that the client has finished loading a scene + OnSceneEvent?.Invoke(new SceneEvent() { - //Other message? - } + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + ClientId = clientId + }); + break; } case SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete)}] Client Id {clientId} finished unloading additive scene."); + // Notify the local server that the client has finished unloading a scene + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + ClientId = clientId + }); + break; } case SceneEventData.SceneEventTypes.C2S_Event_Sync_Complete: { + // Notify the local server that a client has finished synchronizing + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + SceneName = string.Empty, + ClientId = clientId + }); + if (SceneEventData.ClientNeedsReSynchronization()) { Debug.Log($"Re-Synchronizing client {clientId} for missed destroyed NetworkObjects."); SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_ReSync; SendSceneEventData(new ulong[] { clientId }); + + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + SceneName = string.Empty, + ClientId = clientId + }); } - // NSS TOOD: The scene event local notification needs to be called - //m_NetworkManager.NotifyPlayerConnected(clientId); + break; } default: diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 5c0ef90672..15c86c9c1a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -8,7 +8,10 @@ namespace Unity.Netcode { - [Serializable] + /// + /// Used by to communicate scene event states + /// when scene management is enabled. + /// public class SceneEventData : IDisposable { /// @@ -30,12 +33,12 @@ public enum SceneEventTypes C2S_Event_Sync_Complete, //Client to server } - public SceneEventTypes SceneEventType; - public LoadSceneMode LoadSceneMode; - public Guid SwitchSceneGuid; + internal SceneEventTypes SceneEventType; + internal LoadSceneMode LoadSceneMode; + internal Guid SwitchSceneGuid; - public uint SceneIndex; - public ulong TargetClientId; + internal uint SceneIndex; + internal ulong TargetClientId; private Dictionary> m_SceneNetworkObjects; private Dictionary m_SceneNetworkObjectDataOffsets; @@ -65,7 +68,7 @@ public enum SceneEventTypes /// Gets the next scene index to be loaded for approval and/or late joining /// /// - public uint GetNextSceneSynchronizationIndex() + internal uint GetNextSceneSynchronizationIndex() { if (m_SceneNetworkObjectDataOffsets.ContainsKey(SceneIndex)) { @@ -79,7 +82,7 @@ public uint GetNextSceneSynchronizationIndex() /// Determines if all scenes have been processed during the synchronization process /// /// true/false - public bool IsDoneWithSynchronization() + internal bool IsDoneWithSynchronization() { return (m_SceneNetworkObjectDataOffsets.Count == 0); } @@ -88,7 +91,7 @@ public bool IsDoneWithSynchronization() /// Server Side: /// Called just before the synchronization process /// - public void InitializeForSynch() + internal void InitializeForSynch() { if (m_SceneNetworkObjects == null) { @@ -106,7 +109,7 @@ public void InitializeForSynch() /// /// /// - public void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObject) + internal void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObject) { if (!m_SceneNetworkObjects.ContainsKey(sceneIndex)) { @@ -120,7 +123,7 @@ public void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObjec /// Determines if the scene event type was intended for the client ( or server ) /// /// true (client should handle this message) false (server should handle this message) - public bool IsSceneEventClientSide() + internal bool IsSceneEventClientSide() { switch (SceneEventType) { @@ -162,10 +165,10 @@ private int SortNetworkObjects(NetworkObject first, NetworkObject second) } /// - /// Serializes data based on the SceneEvent type. + /// Serializes data based on the SceneEvent type () /// - /// - public void OnWrite(NetworkWriter writer) + /// to write the scene event data + internal void OnWrite(NetworkWriter writer) { writer.WriteByte((byte)SceneEventType); @@ -237,7 +240,7 @@ public void OnWrite(NetworkWriter writer) /// Deserialize data based on the SceneEvent type. /// /// - public void OnRead(NetworkReader reader) + internal void OnRead(NetworkReader reader) { var sceneEventTypeValue = reader.ReadByte(); @@ -389,7 +392,7 @@ internal void WriteClientReSynchronizationData(NetworkWriter writer) /// process the server finds NetworkObjects that the client still thinks are spawned. /// /// - public bool ClientNeedsReSynchronization() + internal bool ClientNeedsReSynchronization() { return (m_NetworkObjectsToBeRemoved.Count > 0); } @@ -441,7 +444,7 @@ internal void WriteClientSynchronizationResults(NetworkWriter writer) /// /// /// - public void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkManager) + internal void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkManager) { if (m_SceneNetworkObjectDataOffsets.ContainsKey(sceneId)) { @@ -494,7 +497,7 @@ public void Dispose() /// /// Constructor /// - public SceneEventData(NetworkManager networkManager) + internal SceneEventData(NetworkManager networkManager) { m_NetworkManager = networkManager; InternalBuffer = NetworkBufferPool.GetBuffer(); diff --git a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs index c6f05e5a70..3c09afd0cd 100644 --- a/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs +++ b/testproject/Assets/Tests/Manual/Scripts/GenericNetworkObjectBehaviour.cs @@ -131,8 +131,10 @@ private void Update() if (IsOwner && m_ShouldDespawn && NetworkObject != null) { m_ShouldDespawn = false; - - NetworkObject.Despawn(true); + if (NetworkObject.NetworkManager != null) + { + NetworkObject.Despawn(true); + } } else if (!IsServer) { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 4b1ef69580..7fed16dffe 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -95,13 +95,13 @@ private void DeregisterCustomPrefabHandler() } } - private void OnSceneEvent(AsyncOperation operation, SceneEventData.SceneEventTypes sceneEventType, LoadSceneMode loadSceneMode, string sceneName) + private void OnSceneEvent(SceneEvent sceneEvent) { - switch (sceneEventType) + switch (sceneEvent.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Event_Unload: { - if (loadSceneMode == LoadSceneMode.Single && (gameObject.scene.name == sceneName)) + if (sceneEvent.LoadSceneMode == LoadSceneMode.Single && (gameObject.scene.name == sceneEvent.SceneName)) { OnUnloadScene(); } @@ -416,9 +416,6 @@ public bool Destroy(NetworkObject networkObject) else { return true; - //// NSS TODO: Remove before converting from draft to PR - //Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); - //Object.DestroyImmediate(networkObject.gameObject); } return false; } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index aaf75f561c..ddbbb123f7 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -18,7 +18,7 @@ public class NetworkPrefabPoolAdditive : NetworkBehaviour public bool DestroyOnUnload = false; public float InitialSpawnDelay; - public int SpawnsPerSecond; + public int SpawnsPerSecond = 3; public int PoolSize; public float ObjectSpeed = 10.0f; @@ -104,9 +104,7 @@ private void OnDestroy() // Start is called before the first frame update private void Start() { - SpawnsPerSecond = 3; NetworkManager.SceneManager.OnSceneEvent += OnSceneEvent; - } @@ -117,26 +115,26 @@ private void Start() /// /// /// - private void OnSceneEvent(AsyncOperation operation,SceneEventData.SceneEventTypes sceneEventType,LoadSceneMode loadSceneMode, string sceneName) + private void OnSceneEvent(SceneEvent sceneEvent) { - switch(sceneEventType) + switch (sceneEvent.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Event_Unload: + { + if (sceneEvent.LoadSceneMode == LoadSceneMode.Additive && (gameObject.scene.name == sceneEvent.SceneName)) { - if (loadSceneMode == LoadSceneMode.Additive && (gameObject.scene.name == sceneName)) - { - OnUnloadScene(); - } - break; + OnUnloadScene(); } + break; + } case SceneEventData.SceneEventTypes.S2C_Event_Load: + { + if (sceneEvent.LoadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneEvent.SceneName) || !SpawnInSourceScene)) { - if (loadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneName) || !SpawnInSourceScene) ) - { - OnUnloadScene(); - } - break; + OnUnloadScene(); } + break; + } } } @@ -469,8 +467,6 @@ public bool Destroy(NetworkObject networkObject) else { return true; - //Debug.Log($"NetworkObject {networkObject.name}:{networkObject.NetworkObjectId} is not registered and will be destroyed immediately"); - //Object.DestroyImmediate(networkObject.gameObject); } return false; } diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs index 2e78bee08c..33e8658ac4 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs @@ -22,7 +22,6 @@ public class StatsDisplay : NetworkBehaviour private void Start() { - m_Stats = new Rect(5, 10, 175, 300); GUI.contentColor = new Color(196, 196, 196, 196); GUI.backgroundColor = new Color(96, 96, 96, 96); if (m_ClientServerToggle != null) @@ -53,10 +52,30 @@ public override void OnNetworkSpawn() UpdateButton(); } - + NetworkManager.SceneManager.OnSceneEvent += OnSceneEvent; StartCoroutine("UpdateTextStatus"); } + public override void OnNetworkDespawn() + { + NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; + base.OnNetworkDespawn(); + } + + + private class SceneEventNotification + { + public float TimeToLive; + public SceneEvent SceneEvent; + } + + private Queue m_SceneEventNotifications = new Queue(); + + private void OnSceneEvent(SceneEvent sceneEvent) + { + m_SceneEventNotifications.Enqueue(new SceneEventNotification() { SceneEvent = sceneEvent, TimeToLive = Time.realtimeSinceStartup + 10 }); + } + /// /// Invoked when a client connects /// @@ -104,6 +123,9 @@ private void OnGUI() { if (NetworkManager && NetworkManager.IsListening) { + var width = 0.20f * Screen.width; + var height = 0.35f * Screen.height; + m_Stats = new Rect(5, 10, width, height); GUI.TextArea(m_Stats, m_LastStatsDump); } } @@ -181,8 +203,20 @@ private IEnumerator UpdateTextStatus() m_LastStatsDump += "\n"; } - m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}"; - + m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}\n"; + if (m_SceneEventNotifications.Count > 0) + { + m_LastStatsDump += "Scene Events:\n"; + foreach (var sceneEventEntry in m_SceneEventNotifications) + { + m_LastStatsDump += $"[{sceneEventEntry.SceneEvent.ClientId} | {sceneEventEntry.SceneEvent.SceneEventType} | " + + $"{sceneEventEntry.SceneEvent.SceneName} | {sceneEventEntry.SceneEvent.LoadSceneMode}]\n"; + } + if(m_SceneEventNotifications.Peek().TimeToLive < Time.realtimeSinceStartup) + { + m_SceneEventNotifications.Dequeue(); + } + } } if (NetworkManager.Singleton.IsServer && m_ClientsToUpdate.Count > 0) { From 978e8de133dd39692bead7ca23f24b9370e3aaf1 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 16:14:41 -0500 Subject: [PATCH 053/106] style slightly increased the player prefab size --- testproject/Assets/Prefabs/Player.prefab | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testproject/Assets/Prefabs/Player.prefab b/testproject/Assets/Prefabs/Player.prefab index aa317c3516..4d2c48fa0d 100644 --- a/testproject/Assets/Prefabs/Player.prefab +++ b/testproject/Assets/Prefabs/Player.prefab @@ -33,8 +33,8 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4079352819444256614} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 5, y: 0.5, z: 5} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalPosition: {x: 5, y: 0.625, z: 5} + m_LocalScale: {x: 1.25, y: 1.25, z: 1.25} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 From 277292e690dc8750e54d8be4f7a4faafc82ba6fa Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 18:08:13 -0500 Subject: [PATCH 054/106] feat Improved notifications and detection of notifications for manual verification that they are triggering properly. Added a SceneEventNotificationQueue test project script that is to be used with the NetworkManager to detect edge case notifications (i.e. in the middle of a scene transition). Fixed an edge case scenario with NetworkObject.Despawn that could occur during exiting the application and the SpawnManager no longer exists. --- .../Runtime/Core/NetworkObject.cs | 10 +- .../SceneManagement/NetworkSceneManager.cs | 96 +++++-------------- .../SceneEventNotificationQueue.cs | 86 +++++++++++++++++ .../SceneEventNotificationQueue.cs.meta | 11 +++ .../SceneTransitioningBase1.unity | 23 ++++- .../SceneTransitioningBase2.unity | 4 + .../Tests/Manual/Scripts/StatsDisplay.cs | 56 +++++------ 7 files changed, 176 insertions(+), 110 deletions(-) create mode 100644 testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs create mode 100644 testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs index 3621ac3bdf..7424c83f88 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs @@ -494,14 +494,18 @@ public void SpawnAsPlayerObject(ulong clientId, bool destroyWithScene = false) } /// - /// Despawns this GameObject and destroys it for other clients. This should be used if the object should be kept on the server + /// Despawns the parent of this and sends a destroy message for it to all connected clients. /// + /// (true) the will be destroyed (false) the will persist after being despawned public void Despawn(bool destroy = false) { - NetworkManager.SpawnManager.DespawnObject(this, destroy); + // An edge case scenario can occur that will throw an exception due to the fact that SpawnManager is null. + if (NetworkManager.SpawnManager != null) + { + NetworkManager.SpawnManager.DespawnObject(this, destroy); + } } - /// /// Removes all ownership of an object from any client. Can only be called from server /// diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 931bafb18d..946084a177 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -55,16 +55,6 @@ public class SceneEvent /// public class NetworkSceneManager { - /// - /// Delegate for when the scene has been switched - /// - public delegate void SceneSwitchedDelegate(); - - /// - /// Delegate for when a scene switch has been initiated - /// - public delegate void SceneSwitchStartedDelegate(AsyncOperation operation); - public delegate void SceneEventDelegate(SceneEvent sceneEvent); /// @@ -85,18 +75,9 @@ public class NetworkSceneManager /// public delegate void NotifyClientAllClientsLoadedSceneDelegate(ulong[] clientIds, ulong[] timedOutClientIds); - /// - /// Event that is invoked when the scene is switched - /// - public event SceneSwitchedDelegate OnSceneSwitched; public event SceneEventDelegate OnSceneEvent; - /// - /// Event that is invoked when a local scene switch has started - /// - public event SceneSwitchStartedDelegate OnSceneSwitchStarted; - /// /// Event that is invoked on the server when a client completes scene transition /// @@ -139,7 +120,7 @@ public class NetworkSceneManager private MessageQueueContainer.MessageType m_MessageType = MessageQueueContainer.MessageType.SceneEvent; private NetworkChannel m_ChannelType = NetworkChannel.Internal; - private NetworkUpdateStage m_NetworkUpdateStage = NetworkUpdateStage.EarlyUpdate; + private NetworkUpdateStage m_NetworkUpdateStage = NetworkUpdateStage.PreUpdate; internal NetworkSceneManager(NetworkManager networkManager) { @@ -459,22 +440,10 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo /// how the scene will be loaded private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchSceneProgress, LoadSceneMode loadSceneMode) { - // start loading the scene - // NSS TODO: This is a temporary place holder check to make sure we don't try to unload a scene loaded in single mode. var currentActiveScene = SceneManager.GetActiveScene(); - AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); - sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; - switchSceneProgress.SetSceneLoadOperation(sceneLoad); - + // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - OnSceneEvent?.Invoke(new SceneEvent() - { - SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload, - LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = SceneManager.GetActiveScene().name, - ClientId = m_NetworkManager.ServerClientId - }); foreach (var additiveSceneName in m_ScenesLoaded) { if (currentActiveScene.name != additiveSceneName) @@ -489,24 +458,13 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene } } m_ScenesLoaded.Clear(); - - - // NSS TODO: Make a single unified notification callback - OnSceneSwitchStarted?.Invoke(sceneLoad); - - } - else - { - if (!m_ScenesLoaded.Contains(sceneName)) - { - m_ScenesLoaded.Add(sceneName); - } - else - { - throw new Exception($"{sceneName} is being loaded twice?!"); - } } + // Now start loading the scene + AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); + sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; + switchSceneProgress.SetSceneLoadOperation(sceneLoad); + OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = sceneLoad, @@ -543,6 +501,7 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) { if (currentActiveScene.name != loadedSceneName) { + Debug.Log($"Invoking unload scene event for {loadedSceneName}"); OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = SceneManager.UnloadSceneAsync(loadedSceneName), @@ -558,17 +517,6 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) // Move ALL NetworkObjects to the temp scene MoveObjectsToDontDestroyOnLoad(); } - else - { - if (!m_ScenesLoaded.Contains(sceneName)) - { - m_ScenesLoaded.Add(sceneName); - } - else - { - throw new Exception($"{sceneName} is being loaded twice?!"); - } - } // 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 // When it is set: Just before starting the asynchronous loading call @@ -589,12 +537,6 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) SceneName = sceneName, ClientId = m_NetworkManager.LocalClientId }); - - - if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) - { - OnSceneSwitchStarted?.Invoke(sceneLoad); - } } /// @@ -606,8 +548,16 @@ private void OnSceneLoaded(string sceneName) if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { SceneManager.SetActiveScene(nextScene); + } + + if (!m_ScenesLoaded.Contains(sceneName)) + { m_ScenesLoaded.Add(sceneName); } + else + { + throw new Exception($"{sceneName} is being loaded twice?!"); + } //Get all NetworkObjects loaded by the scene PopulateScenePlacedObjects(nextScene); @@ -704,8 +654,6 @@ private void OnServerLoadedScene() SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), ClientId = m_NetworkManager.ServerClientId }); - - OnSceneSwitched?.Invoke(); } /// @@ -735,9 +683,6 @@ private void OnClientLoadedScene() SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), ClientId = m_NetworkManager.LocalClientId }); - - - OnSceneSwitched?.Invoke(); } /// @@ -891,6 +836,15 @@ private void ClientLoadedSynchronization(uint sceneIndex) SceneManager.SetActiveScene(nextScene); } + if (!m_ScenesLoaded.Contains(sceneName)) + { + m_ScenesLoaded.Add(sceneName); + } + else + { + throw new Exception($"{sceneName} is being loaded twice?!"); + } + // Get all NetworkObjects loaded by the scene (in-scene NetworkObjects) PopulateScenePlacedObjects(nextScene); diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs new file mode 100644 index 0000000000..ffb51e0333 --- /dev/null +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Unity.Netcode; + +namespace TestProject.ManualTests +{ + /// + /// Logs to console all NetworkSceneManager Scene Events + /// This is only meant to be used as a tool to detect all notifications that occur during + /// scene events. This was created to detect the edge case scene event scenarios during + /// single mode scene loading events and can be used as a manual test to verify additive + /// scenes are being intentionally unloaded during a single mode scene loading event. + /// Note: this should be added to the parent GameObject of your NetworkManager component + /// and will only work if NetworkManager's "Don't Destroy" is true. + /// + public class SceneEventNotificationQueue: MonoBehaviour + { + public bool LogToConsole; + + [Range(1,30)] + public float TimeToLive = 10.0f; + + private bool m_IsInitialized; + + private class SceneEventNotification + { + public float TimeToLive; + public string SceneEvent; + } + + private Queue m_SceneEvents = new Queue(); + + private NetworkManager m_NetworkManager; + + private void Start() + { + m_NetworkManager = gameObject.GetComponent(); + } + + /// + /// Invoked on all local scene event notifications + /// + /// + private void OnSceneEvent(SceneEvent sceneEvent) + { + var sceneEventMsg = $"[{sceneEvent.ClientId} | {sceneEvent.SceneEventType} | {sceneEvent.SceneName} | {sceneEvent.LoadSceneMode}]"; + m_SceneEvents.Enqueue(new SceneEventNotification() { SceneEvent = sceneEventMsg, TimeToLive = Time.realtimeSinceStartup + TimeToLive }); + if (LogToConsole) + { + Debug.Log(sceneEventMsg); + } + } + + /// + /// Returns the current scene event notifications + /// + /// + public List GetCurrentNotifications() + { + return m_SceneEvents.Select(c => c.SceneEvent).ToList(); + } + + private void Update() + { + if(m_NetworkManager != null && m_NetworkManager.IsListening) + { + if(!m_IsInitialized) + { + m_NetworkManager.SceneManager.OnSceneEvent += OnSceneEvent; + m_IsInitialized = true; + } + + if (m_SceneEvents.Count() > 0 && m_SceneEvents.Peek().TimeToLive < Time.realtimeSinceStartup) + { + m_SceneEvents.Dequeue(); + } + } + else if (m_IsInitialized) + { + m_IsInitialized = false; + m_NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; + } + } + } +} diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs.meta new file mode 100644 index 0000000000..7f72d1c976 --- /dev/null +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 71c52a32bd1c1c84691050b6d045ab4a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity index c601667305..6d5bc925b7 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -1445,6 +1445,7 @@ GameObject: - component: {fileID: 1024114719} - component: {fileID: 1024114718} - component: {fileID: 1024114721} + - component: {fileID: 1024114722} m_Layer: 0 m_Name: NetworkManager m_TagString: Untagged @@ -1580,9 +1581,9 @@ MonoBehaviour: ServerWebsocketListenPort: 8887 SupportWebsocket: 0 Channels: [] - UseMLAPIRelay: 0 - MLAPIRelayAddress: 184.72.104.138 - MLAPIRelayPort: 8888 + UseNetcodeRelay: 0 + NetcodeRelayAddress: 127.0.0.1 + NetcodeRelayPort: 8888 MessageSendMode: 0 --- !u!4 &1024114720 Transform: @@ -1610,6 +1611,20 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 0e6f8504d891fc44881b2d9703017d03, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &1024114722 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1024114717} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 71c52a32bd1c1c84691050b6d045ab4a, type: 3} + m_Name: + m_EditorClassIdentifier: + LogToConsole: 1 + TimeToLive: 10 --- !u!1 &1113539278 GameObject: m_ObjectHideFlags: 0 @@ -3371,6 +3386,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_ClientServerToggle: {fileID: 1588117327} + m_TrackSceneEvents: 1 + m_LogSceneEventsToConsole: 1 --- !u!114 &2107482023 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity index 603d6268aa..2b0771283c 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase2.unity @@ -174,6 +174,8 @@ MonoBehaviour: m_EditorClassIdentifier: m_SwitchSceneButtonObject: {fileID: 1347823141} m_SceneToSwitchTo: SceneTransitioningBase1 + m_EnableAutoSwitch: 0 + m_AutoSwitchTimeOut: 60 --- !u!1 &37242881 GameObject: m_ObjectHideFlags: 0 @@ -3192,6 +3194,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_ClientServerToggle: {fileID: 1588117327} + m_TrackSceneEvents: 1 + m_LogSceneEventsToConsole: 1 --- !u!114 &2107482023 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs index 33e8658ac4..4528c88b4b 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs @@ -12,6 +12,15 @@ public class StatsDisplay : NetworkBehaviour { [SerializeField] private GameObject m_ClientServerToggle; + + [Tooltip("When enabled, this will display all scene events in the display window.")] + [SerializeField] + private bool m_TrackSceneEvents; + + [Tooltip("When enabled, this will log all messages to the console.")] + [SerializeField] + private bool m_LogSceneEventsToConsole; + private bool m_ClientMode = true; private Rect m_Stats; private string m_LastStatsDump; @@ -20,6 +29,8 @@ public class StatsDisplay : NetworkBehaviour private bool m_IsServer; + private SceneEventNotificationQueue m_SceneEventNotificationQueue; + private void Start() { GUI.contentColor = new Color(196, 196, 196, 196); @@ -52,28 +63,12 @@ public override void OnNetworkSpawn() UpdateButton(); } - NetworkManager.SceneManager.OnSceneEvent += OnSceneEvent; - StartCoroutine("UpdateTextStatus"); - } - - public override void OnNetworkDespawn() - { - NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; - base.OnNetworkDespawn(); - } - - - private class SceneEventNotification - { - public float TimeToLive; - public SceneEvent SceneEvent; - } - - private Queue m_SceneEventNotifications = new Queue(); + if (m_TrackSceneEvents) + { + m_SceneEventNotificationQueue = NetworkManager.gameObject.GetComponent(); + } - private void OnSceneEvent(SceneEvent sceneEvent) - { - m_SceneEventNotifications.Enqueue(new SceneEventNotification() { SceneEvent = sceneEvent, TimeToLive = Time.realtimeSinceStartup + 10 }); + StartCoroutine("UpdateTextStatus"); } /// @@ -123,14 +118,13 @@ private void OnGUI() { if (NetworkManager && NetworkManager.IsListening) { - var width = 0.20f * Screen.width; - var height = 0.35f * Screen.height; + var width = 0.25f * Screen.width; + var height = 0.50f * Screen.height; m_Stats = new Rect(5, 10, width, height); GUI.TextArea(m_Stats, m_LastStatsDump); } } - /// /// Updates the text of the button for switching between server and client stats /// @@ -204,17 +198,13 @@ private IEnumerator UpdateTextStatus() } m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}\n"; - if (m_SceneEventNotifications.Count > 0) + if (m_SceneEventNotificationQueue != null) { - m_LastStatsDump += "Scene Events:\n"; - foreach (var sceneEventEntry in m_SceneEventNotifications) - { - m_LastStatsDump += $"[{sceneEventEntry.SceneEvent.ClientId} | {sceneEventEntry.SceneEvent.SceneEventType} | " + - $"{sceneEventEntry.SceneEvent.SceneName} | {sceneEventEntry.SceneEvent.LoadSceneMode}]\n"; - } - if(m_SceneEventNotifications.Peek().TimeToLive < Time.realtimeSinceStartup) + var sceneEvents = m_SceneEventNotificationQueue.GetCurrentNotifications(); + m_LastStatsDump += $"Scene Events {sceneEvents.Count}:\n"; + foreach (var sceneEventEntry in sceneEvents) { - m_SceneEventNotifications.Dequeue(); + m_LastStatsDump += sceneEventEntry + "\n"; } } } From 8495d1f85538fff40895fdd6e478c89fb7a2dd61 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 20:03:08 -0500 Subject: [PATCH 055/106] fix, refactor, and style re-fixing an issue with sending spawn calls to every client (n) times based on the number of clients connected (until it is re-fixed by the offender) Started the removal of certain NetworkSceneManager event notifications that are no longer required due to the more recent SceneEvent notifications. Removed the set active scene scene event for MS-1 (just not enough time to implement and be sure that it works perfectly) Removed a manual test for SceneManager callbacks that won't be used any longer. Updated XML documentation in various areas. --- .../SceneManagement/NetworkSceneManager.cs | 333 +++++++++--------- .../Runtime/SceneManagement/SceneEventData.cs | 12 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 3 +- .../NetworkSceneManagerCallbackTests.cs | 32 -- .../NetworkSceneManagerCallbackTests.cs.meta | 11 - 5 files changed, 165 insertions(+), 226 deletions(-) delete mode 100644 testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs delete mode 100644 testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 8f196d3f98..76736fad8c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -55,14 +55,20 @@ public class SceneEvent /// public class NetworkSceneManager { + /// + /// The delegate callback definition for scene events + /// For more details review over and + /// + /// public delegate void SceneEventDelegate(SceneEvent sceneEvent); /// - /// Delegate for when a client has reported to the server that it has completed scene transition - /// + /// Event that will notify the local client or server of all scene events that take place + /// For more details review over and /// - public delegate void NotifyServerClientLoadedSceneDelegate(SceneSwitchProgress progress, ulong clientId); + public event SceneEventDelegate OnSceneEvent; + #region Client and Server Scene Loading Events to be Removed? /// /// Delegate for when all clients have reported to the server that they have completed scene transition or timed out /// @@ -75,14 +81,6 @@ public class NetworkSceneManager /// public delegate void NotifyClientAllClientsLoadedSceneDelegate(ulong[] clientIds, ulong[] timedOutClientIds); - - public event SceneEventDelegate OnSceneEvent; - - /// - /// Event that is invoked on the server when a client completes scene transition - /// - public event NotifyServerClientLoadedSceneDelegate OnNotifyServerClientLoadedScene; - /// /// Event that is invoked on the server when all clients have reported that they have completed scene transition /// @@ -94,6 +92,7 @@ public class NetworkSceneManager /// It relies on MessageSender, which doesn't send events from the server to itself (which is the case for a Host client). /// public event NotifyClientAllClientsLoadedSceneDelegate OnNotifyClientAllClientsLoadedScene; + #endregion internal readonly HashSet RegisteredSceneNames = new HashSet(); internal readonly Dictionary SceneNameToIndex = new Dictionary(); @@ -104,11 +103,18 @@ public class NetworkSceneManager // Used for observed object synchronization private readonly List m_ObservedObjects = new List(); + // Used to track which scenes are currently loaded (outside of SceneManager) private List m_ScenesLoaded = new List(); + // Used to detect if we are in the middle of a single mode scene transition private static bool s_IsSceneEventActive = false; - internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + // 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 + // When it is set: Just before starting the asynchronous loading call + // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do + // not destroy temporary scene are moved into the active scene + internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; //Client and Server: used for all scene event processing exception for client synchronization internal SceneEventData SceneEventData; @@ -118,9 +124,9 @@ public class NetworkSceneManager private NetworkManager m_NetworkManager { get; } - private MessageQueueContainer.MessageType m_MessageType = MessageQueueContainer.MessageType.SceneEvent; - private NetworkChannel m_ChannelType = NetworkChannel.Internal; - private NetworkUpdateStage m_NetworkUpdateStage = NetworkUpdateStage.PreUpdate; + private const MessageQueueContainer.MessageType k_MessageType = MessageQueueContainer.MessageType.SceneEvent; + private const NetworkChannel k_ChannelType = NetworkChannel.Internal; + private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.PreUpdate; internal NetworkSceneManager(NetworkManager networkManager) { @@ -130,7 +136,10 @@ internal NetworkSceneManager(NetworkManager networkManager) ClientSynchEventData = new SceneEventData(networkManager); } - + /// + /// Generic sending of scene event data + /// + /// array of client identifiers to receive the scene event message internal void SendSceneEventData(ulong[] targetClientIds) { if (targetClientIds.Length == 0) @@ -140,7 +149,7 @@ internal void SendSceneEventData(ulong[] targetClientIds) return; } - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, targetClientIds, m_NetworkUpdateStage); + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, targetClientIds, k_NetworkUpdateStage); if (context != null) { @@ -256,22 +265,24 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn if (!isUnloading) { - // 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 + // 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 // When it is set: Just before starting the asynchronous loading call - // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene + // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do + // not destroy temporary scene are moved into the active scene IsSpawnedObjectsPendingInDontDestroyOnLoad = true; } s_IsSceneEventActive = true; // NSS TODO: switchSceneProgress needs to be re-factored - switchSceneProgress.OnClientLoadedScene += clientId => { OnNotifyServerClientLoadedScene?.Invoke(switchSceneProgress, clientId); }; + // This replicates what OnSceneEvent handles. switchSceneProgress.OnComplete += timedOut => { OnNotifyServerAllClientsLoadedScene?.Invoke(switchSceneProgress, timedOut); // Send notification to all clients that everyone is done loading var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.AllClientsLoadedScene, NetworkChannel.Internal, - m_NetworkManager.ConnectedClientsIds, m_NetworkUpdateStage); + m_NetworkManager.ConnectedClientsIds, k_NetworkUpdateStage); if (context != null) { @@ -324,6 +335,8 @@ public SceneSwitchProgress UnloadScene(string sceneName) { m_ScenesLoaded.Remove(sceneName); } + + // Notify local server that a scene is going to be unloaded OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = sceneUnload, @@ -363,6 +376,7 @@ private void OnClientUnloadScene() sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); + // Notify the local client that a scene is going to be unloaded OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = sceneUnload, @@ -397,11 +411,10 @@ private void OnSceneUnloaded() } /// - /// Loads the scene name in question as either additive or single. + /// Server side: Loads the scene name in either additive or single loading mode. /// - /// - /// NSS TODO: This could probably stand to have some form of "scene event status" class/structure that will - /// include any error types/messages and the SceneSwitchProgress class associated with the status (if success) + /// the name of the scene to be loaded + /// NSS TODO: Either gut and rename the SwitchSceneProgress or completely remove it /// SceneSwitchProgress (if null this call failed) public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMode) { @@ -425,21 +438,6 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo MoveObjectsToDontDestroyOnLoad(); } - // Begin the scene event - OnBeginSceneEvent(sceneName, switchSceneProgress, loadSceneMode); - - //Return our scene progress instance - return switchSceneProgress; - } - - /// - /// Commonly shared code between switching and additively loading a scene - /// - /// name of the scene to be loaded - /// SceneSwitchProgress class instance - /// how the scene will be loaded - private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchSceneProgress, LoadSceneMode loadSceneMode) - { var currentActiveScene = SceneManager.GetActiveScene(); // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) @@ -465,6 +463,7 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; switchSceneProgress.SetSceneLoadOperation(sceneLoad); + // Notify the local server that a scene loading event has begun OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = sceneLoad, @@ -473,6 +472,9 @@ private void OnBeginSceneEvent(string sceneName, SceneSwitchProgress switchScene SceneName = sceneName, ClientId = m_NetworkManager.ServerClientId }); + + //Return our scene progress instance + return switchSceneProgress; } /// @@ -518,9 +520,11 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) MoveObjectsToDontDestroyOnLoad(); } - // 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 + // 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 // When it is set: Just before starting the asynchronous loading call - // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene + // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do + // not destroy temporary scene are moved into the active scene if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { IsSpawnedObjectsPendingInDontDestroyOnLoad = true; @@ -540,7 +544,8 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) } /// - /// Client and Server: Generic on scene loaded callback method to be called upon a scene loading + /// Client and Server: + /// Generic on scene loaded callback method to be called upon a scene loading /// private void OnSceneLoaded(string sceneName) { @@ -568,9 +573,11 @@ private void OnSceneLoaded(string sceneName) MoveObjectsToScene(nextScene); } - // 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 + // 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 // When it is set: Just before starting the asynchronous loading call - // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do not destroy temporary scene are moved into the active scene + // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do + // not destroy temporary scene are moved into the active scene IsSpawnedObjectsPendingInDontDestroyOnLoad = false; if (m_NetworkManager.IsServer) @@ -584,7 +591,7 @@ private void OnSceneLoaded(string sceneName) } /// - /// Server specific on scene loaded callback method invoked by OnSceneLoading only + /// Server side: on scene loaded callback method invoked by OnSceneLoading only /// private void OnServerLoadedScene() { @@ -614,7 +621,7 @@ private void OnServerLoadedScene() } } - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, new ulong[] { clientId }, m_NetworkUpdateStage); + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, new ulong[] { clientId }, k_NetworkUpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext)context) @@ -638,12 +645,6 @@ private void OnServerLoadedScene() } } - // Tell server that scene load is completed - if (m_NetworkManager.IsServer) - { - OnClientSceneLoadingEventCompleted(m_NetworkManager.LocalClientId, SceneEventData.SwitchSceneGuid); - } - s_IsSceneEventActive = false; // Notify local server that the scene was loaded @@ -657,7 +658,7 @@ private void OnServerLoadedScene() } /// - /// Client specific on scene loaded callback method invoked by OnSceneLoading only + /// Client side: on scene loaded callback method invoked by OnSceneLoading only /// private void OnClientLoadedScene() { @@ -686,8 +687,9 @@ private void OnClientLoadedScene() } /// - /// Server Side Only: - /// This is used for late joining players and players that have just had their connection approved + /// Server Side: + /// This is used for players that have just had their connection approved and will assure they are synchronized + /// properly if they are late joining /// /// newly joined client identifier internal void SynchronizeNetworkObjects(ulong ownerClientId) @@ -742,7 +744,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) } } - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, new ulong[] { ownerClientId }, m_NetworkUpdateStage); + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, new ulong[] { ownerClientId }, k_NetworkUpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext)context) @@ -757,7 +759,6 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) SceneEventType = SceneEventData.SceneEventType, ClientId = ownerClientId }); - } /// @@ -791,8 +792,10 @@ private void OnClientBeginSync(uint sceneIndex) }); } + // Check to see if the client already has loaded the scene to be loaded 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 @@ -809,6 +812,7 @@ private void OnClientBeginSync(uint sceneIndex) } else { + // If so, then pass through ClientLoadedSynchronization(sceneIndex); } } @@ -856,8 +860,8 @@ private void ClientLoadedSynchronization(uint sceneIndex) ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete; ClientSynchEventData.SceneIndex = sceneIndex; - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(m_MessageType, m_ChannelType, - new ulong[] { m_NetworkManager.ServerClientId }, m_NetworkUpdateStage); + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, + new ulong[] { m_NetworkManager.ServerClientId }, k_NetworkUpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext)context) @@ -879,112 +883,9 @@ private void ClientLoadedSynchronization(uint sceneIndex) HandleClientSceneEvent(null); } - #region General Methods - internal bool HasSceneMismatch(uint sceneIndex) => SceneManager.GetActiveScene().name != SceneIndexToString[sceneIndex]; - - internal void RemoveClientFromSceneSwitchProgresses(ulong clientId) - { - foreach (var switchSceneProgress in SceneSwitchProgresses.Values) - { - switchSceneProgress.RemoveClientAsDone(clientId); - } - } - - private void MoveObjectsToDontDestroyOnLoad() - { - // Move ALL NetworkObjects to the temp scene - var objectsToKeep = new HashSet(m_NetworkManager.SpawnManager.SpawnedObjectsList); - - foreach (var sobj in objectsToKeep) - { - //In case an object has been set as a child of another object it has to be removed from the parent in order to be moved from one scene to another. - if (sobj.gameObject.transform.parent != null) - { - sobj.gameObject.transform.parent = null; - } - - if (!sobj.DestroyWithScene) - { - UnityEngine.Object.DontDestroyOnLoad(sobj.gameObject); - } - else if (m_NetworkManager.IsServer) - { - sobj.Despawn(true); - } - } - } - /// - /// Should be invoked on both the client and server side after: - /// -- A new scene has been loaded - /// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene. - /// Added the ability to choose not to clear the scene placed objects for additive scene loading. - /// - internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) - { - if (clearScenePlacedObjects) - { - ScenePlacedObjects.Clear(); - } - - var networkObjects = UnityEngine.Object.FindObjectsOfType(); - - // Just add every NetworkObject found that isn't already in the list - // If any "non-in-scene placed NetworkObjects" are added to this list it shouldn't matter - // The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash - foreach (var networkObjectInstance in networkObjects) - { - if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) - { - // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) - if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy) - { - ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, networkObjectInstance); - } - } - } - } - - private void MoveObjectsToScene(Scene scene) - { - // Move ALL NetworkObjects to the temp scene - var objectsToKeep = m_NetworkManager.SpawnManager.SpawnedObjectsList; - - foreach (var sobj in objectsToKeep) - { - //In case an object has been set as a child of another object it has to be removed from the parent in order to be moved from one scene to another. - if (sobj.gameObject.transform.parent != null) - { - sobj.gameObject.transform.parent = null; - } - - SceneManager.MoveGameObjectToScene(sobj.gameObject, scene); - } - } - - internal void AllClientsReady(ulong[] clientIds, ulong[] timedOutClientIds) - { - OnNotifyClientAllClientsLoadedScene?.Invoke(clientIds, timedOutClientIds); - } - - // Called on server - internal void OnClientSceneLoadingEventCompleted(ulong clientId, Guid switchSceneGuid) - { - if (switchSceneGuid == Guid.Empty) - { - // If Guid is empty it means the client has loaded the start scene of the server and the server would never have a switchSceneProgresses created for the start scene. - return; - } - - if (SceneSwitchProgresses.TryGetValue(switchSceneGuid, out SceneSwitchProgress progress)) - { - SceneSwitchProgresses[switchSceneGuid].AddClientAsDone(clientId); - } - } - #endregion - - /// - /// Client Side: Handles incoming SCENE_EVENT messages + /// Client Side: Handles incoming Scene_Event messages + /// It is "understood" that the server is the sender /// /// data associated with the event private void HandleClientSceneEvent(Stream stream) @@ -1047,7 +948,7 @@ private void HandleClientSceneEvent(Stream stream) } /// - /// Server Side: Handles incoming scene events + /// Server Side: Handles incoming Scene_Event messages /// /// client who sent the event /// data associated with the event @@ -1059,11 +960,6 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); - if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) - { - OnClientSceneLoadingEventCompleted(clientId, SceneEventData.SwitchSceneGuid); - } - // Notify the local server that the client has finished loading a scene OnSceneEvent?.Invoke(new SceneEvent() { @@ -1073,6 +969,12 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) ClientId = clientId }); + // NSS TODO: Either re-factor and rename or remove this + if (SceneSwitchProgresses.TryGetValue(SceneEventData.SwitchSceneGuid, out SceneSwitchProgress progress)) + { + SceneSwitchProgresses[SceneEventData.SwitchSceneGuid].AddClientAsDone(clientId); + } + break; } case SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete: @@ -1156,5 +1058,94 @@ public void HandleSceneEvent(ulong clientId, Stream stream) Debug.LogError($"{nameof(NetworkSceneManager.HandleSceneEvent)} was invoked but {nameof(NetworkManager)} reference was null!"); } } + + #region General Methods + private void MoveObjectsToDontDestroyOnLoad() + { + // Move ALL NetworkObjects to the temp scene + var objectsToKeep = new HashSet(m_NetworkManager.SpawnManager.SpawnedObjectsList); + + foreach (var sobj in objectsToKeep) + { + //In case an object has been set as a child of another object it has to be removed from the parent in order to be moved from one scene to another. + if (sobj.gameObject.transform.parent != null) + { + sobj.gameObject.transform.parent = null; + } + + if (!sobj.DestroyWithScene) + { + UnityEngine.Object.DontDestroyOnLoad(sobj.gameObject); + } + else if (m_NetworkManager.IsServer) + { + sobj.Despawn(true); + } + } + } + + /// + /// Should be invoked on both the client and server side after: + /// -- A new scene has been loaded + /// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene. + /// Added the ability to choose not to clear the scene placed objects for additive scene loading. + /// + internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) + { + if (clearScenePlacedObjects) + { + ScenePlacedObjects.Clear(); + } + + var networkObjects = UnityEngine.Object.FindObjectsOfType(); + + // Just add every NetworkObject found that isn't already in the list + // If any "non-in-scene placed NetworkObjects" are added to this list it shouldn't matter + // The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash + foreach (var networkObjectInstance in networkObjects) + { + if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) + { + // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) + if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy) + { + ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, networkObjectInstance); + } + } + } + } + + /// + /// Moves all spawned NetworkObjects (from do not destroy on load) to the scene specified + /// + /// scene to move the NetworkObjects to + private void MoveObjectsToScene(Scene scene) + { + // Move ALL NetworkObjects to the temp scene + var objectsToKeep = m_NetworkManager.SpawnManager.SpawnedObjectsList; + + foreach (var sobj in objectsToKeep) + { + //In case an object has been set as a child of another object it has to be removed from the parent in order to be moved from one scene to another. + if (sobj.gameObject.transform.parent != null) + { + sobj.gameObject.transform.parent = null; + } + + SceneManager.MoveGameObjectToScene(sobj.gameObject, scene); + } + } + + /// + /// NSS TODO: Make this a scene event + /// + /// + /// + internal void AllClientsReady(ulong[] clientIds, ulong[] timedOutClientIds) + { + OnNotifyClientAllClientsLoadedScene?.Invoke(clientIds, timedOutClientIds); + } + + #endregion } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 15c86c9c1a..93e8851485 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -24,12 +24,10 @@ public enum SceneEventTypes { S2C_Event_Load, //Server to client load additive scene S2C_Event_Unload, //Server to client unload additive scene - S2C_Event_SetActive, //Server to client make scene active S2C_Event_Sync, //Server to client late join approval synchronization S2C_Event_ReSync, //Server to client update of objects that were destroyed during sync - C2S_Event_Load_Complete, //Client to server - C2S_Event_Unload_Complete, //Client to server - C2S_Event_SetActiveComplete, //Client to server + C2S_Event_Load_Complete, //Client to server load complete + C2S_Event_Unload_Complete, //Client to server unload complete C2S_Event_Sync_Complete, //Client to server } @@ -131,7 +129,6 @@ internal bool IsSceneEventClientSide() case SceneEventTypes.S2C_Event_Unload: case SceneEventTypes.S2C_Event_Sync: case SceneEventTypes.S2C_Event_ReSync: - case SceneEventTypes.S2C_Event_SetActive: { return true; } @@ -251,7 +248,6 @@ internal void OnRead(NetworkReader reader) else { Debug.LogError($"Serialization Read Error: {nameof(SceneEventType)} vale {sceneEventTypeValue} is not within the range of the defined {nameof(SceneEventTypes)} enumerator!"); - // NSS TODO: Add to proposal's MTT discussion topics: Should we assert here? } var loadSceneModeValue = reader.ReadByte(); @@ -263,7 +259,6 @@ internal void OnRead(NetworkReader reader) else { Debug.LogError($"Serialization Read Error: {nameof(LoadSceneMode)} vale {loadSceneModeValue} is not within the range of the defined {nameof(LoadSceneMode)} enumerator!"); - // NSS TODO: Add to proposal's MTT discussion topics: Should we assert here? } if (SceneEventType != SceneEventTypes.S2C_Event_Sync) @@ -329,7 +324,6 @@ internal void ReadClientReSynchronizationData(NetworkReader reader) if (networkObjectsToRemove.Length > 0) { - Debug.Log($"Client {NetworkManager.Singleton.LocalClientId} is being re-synchronized."); var networkObjects = UnityEngine.Object.FindObjectsOfType(); var networkObjectIdToNetworkObject = new Dictionary(); foreach (var networkObject in networkObjects) @@ -350,7 +344,6 @@ internal void ReadClientReSynchronizationData(NetworkReader reader) networkObject.IsSpawned = false; if (m_NetworkManager.PrefabHandler.ContainsHandler(networkObject)) { - Debug.Log($"NetworkObjectId {networkObjectId} marked as not spawned and is being destroyed via prefab handler."); // Since this is the client side and we have missed the delete message, until the Snapshot system is in place for spawn and despawn handling // we have to remove this from the list of spawned objects manually or when a NetworkObjectId is recycled the client will throw an error // about the id already being assigned. @@ -366,7 +359,6 @@ internal void ReadClientReSynchronizationData(NetworkReader reader) } else { - Debug.Log($"NetworkObjectId {networkObjectId} marked as not spawned and is being destroyed immediately."); UnityEngine.Object.DestroyImmediate(networkObject.gameObject); } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs index 4e0c51bba7..dbb9e53c8b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs @@ -358,10 +358,9 @@ internal void SendSpawnCallForObject(ulong clientId, ulong ownerClientId, Networ var messageQueueContainer = NetworkManager.MessageQueueContainer; - ulong[] clientIds = NetworkManager.ConnectedClientsIds; var context = messageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.CreateObject, NetworkChannel.Internal, - clientIds, NetworkUpdateLoop.UpdateStage); + new ulong[] { clientId }, NetworkUpdateLoop.UpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext)context) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs deleted file mode 100644 index 9742d53d9e..0000000000 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -using UnityEngine; -using Unity.Netcode; - -namespace TestProject.ManualTests -{ - [AddComponentMenu("Netcode/" + nameof(NetworkSceneManagerCallbackTests))] - public class NetworkSceneManagerCallbackTests : NetworkBehaviour - { - public void StartHost() - { - NetworkManager.StartHost(); - } - - public override void OnNetworkSpawn() - { - if (IsServer) - { - NetworkManager.SceneManager.OnNotifyServerClientLoadedScene += (progress, clientId) => - { - Debug.Log("OnNotifyServerClientLoadedScene invoked on the host - Passed"); - }; - - NetworkManager.SceneManager.OnNotifyServerAllClientsLoadedScene += (progress, timedOut) => - { - Debug.Log("OnNotifyServerAllClientsLoadedScene invoked on the host - Passed"); - }; - - NetworkManager.SceneManager.LoadScene("SceneWeAreSwitchingTo",UnityEngine.SceneManagement.LoadSceneMode.Single); - } - } - } -} diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs.meta b/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs.meta deleted file mode 100644 index 3adbd8ca8e..0000000000 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkSceneManagerCallbackTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f7cbaa25ecc57a2468dfb25c155cae9b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 06e8a58a70441c8548cd40b7f57381dad62c767c Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 4 Aug 2021 23:57:43 -0500 Subject: [PATCH 056/106] feat SwitchSceneProgress changed to SceneEventProgress and is no longer a returned value when starting a scene event (load or unload). This is still used internally to track when all clients have loaded the scene in question, but that is primarily used for SceneLoadingMode.Single. Removed the coroutine callback from NetworkManager and placed it within the SceneEventProgress class. --- .../Runtime/Core/NetworkManager.cs | 6 -- .../SceneManagement/NetworkSceneManager.cs | 66 ++++++++++--------- ...witchProgress.cs => SceneEventProgress.cs} | 47 +++++++++++-- ...ess.cs.meta => SceneEventProgress.cs.meta} | 0 .../AdditiveSceneToggleHandler.cs | 25 ++----- .../SwitchSceneHandlerAdditive.cs | 45 +++++-------- .../Manual/Scripts/SwitchSceneHandler.cs | 24 ++----- 7 files changed, 102 insertions(+), 111 deletions(-) rename com.unity.multiplayer.mlapi/Runtime/SceneManagement/{SceneSwitchProgress.cs => SceneEventProgress.cs} (64%) rename com.unity.multiplayer.mlapi/Runtime/SceneManagement/{SceneSwitchProgress.cs.meta => SceneEventProgress.cs.meta} (100%) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index fa9377caad..49d56ba13a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -1055,12 +1055,6 @@ private IEnumerator ApprovalTimeout(ulong clientId) } } - internal IEnumerator TimeOutSwitchSceneProgress(SceneSwitchProgress switchSceneProgress) - { - yield return new WaitForSecondsRealtime(NetworkConfig.LoadSceneTimeOut); - switchSceneProgress.SetTimedOut(); - } - private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, NetworkChannel networkChannel, ArraySegment payload, float receiveTime) { diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 76736fad8c..26e663792b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -73,7 +73,7 @@ public class NetworkSceneManager /// Delegate for when all clients have reported to the server that they have completed scene transition or timed out /// /// - public delegate void NotifyServerAllClientsLoadedSceneDelegate(SceneSwitchProgress progress, bool timedOut); + public delegate void NotifyServerAllClientsLoadedSceneDelegate(SceneEventProgress progress, bool timedOut); /// /// Delegate for when the clients get notified by the server that all clients have completed their scene transitions. @@ -97,7 +97,7 @@ public class NetworkSceneManager internal readonly HashSet RegisteredSceneNames = new HashSet(); internal readonly Dictionary SceneNameToIndex = new Dictionary(); internal readonly Dictionary SceneIndexToString = new Dictionary(); - internal readonly Dictionary SceneSwitchProgresses = new Dictionary(); + internal readonly Dictionary SceneSwitchProgresses = new Dictionary(); internal readonly Dictionary ScenePlacedObjects = new Dictionary(); // Used for observed object synchronization @@ -130,7 +130,6 @@ public class NetworkSceneManager internal NetworkSceneManager(NetworkManager networkManager) { - m_NetworkManager = networkManager; SceneEventData = new SceneEventData(networkManager); ClientSynchEventData = new SceneEventData(networkManager); @@ -227,7 +226,7 @@ public void AddRuntimeSceneName(string sceneName, uint index) /// /// /// SceneSwitchProgress (if null it failed) - private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUnloading = false) + private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnloading = false) { if (!m_NetworkManager.IsServer) { @@ -247,7 +246,7 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn NetworkLog.LogWarning("Scene event is already in progress"); } - return null; + return new SceneEventProgress(null, SceneEventProgressStatus.SceneEventInProgress); } if (!RegisteredSceneNames.Contains(sceneName)) @@ -257,11 +256,11 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn NetworkLog.LogWarning($"The scene {sceneName} is not registered as a switchable scene."); } - return null; + return new SceneEventProgress(null, SceneEventProgressStatus.InvalidSceneName); } - var switchSceneProgress = new SceneSwitchProgress(m_NetworkManager); - SceneSwitchProgresses.Add(switchSceneProgress.Guid, switchSceneProgress); + var sceneEventProgress = new SceneEventProgress(m_NetworkManager); + SceneSwitchProgresses.Add(sceneEventProgress.Guid, sceneEventProgress); if (!isUnloading) { @@ -277,9 +276,9 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn // NSS TODO: switchSceneProgress needs to be re-factored // This replicates what OnSceneEvent handles. - switchSceneProgress.OnComplete += timedOut => + sceneEventProgress.OnComplete += timedOut => { - OnNotifyServerAllClientsLoadedScene?.Invoke(switchSceneProgress, timedOut); + OnNotifyServerAllClientsLoadedScene?.Invoke(sceneEventProgress, timedOut); // Send notification to all clients that everyone is done loading var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.AllClientsLoadedScene, NetworkChannel.Internal, m_NetworkManager.ConnectedClientsIds, k_NetworkUpdateStage); @@ -288,7 +287,7 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn { using (var nonNullContext = (InternalCommandContext)context) { - var doneClientIds = switchSceneProgress.DoneClients.ToArray(); + var doneClientIds = sceneEventProgress.DoneClients.ToArray(); var timedOutClientIds = m_NetworkManager.ConnectedClients.Keys.Except(doneClientIds).ToArray(); nonNullContext.NetworkWriter.WriteULongArray(doneClientIds, doneClientIds.Length); @@ -297,31 +296,32 @@ private SceneSwitchProgress ValidateServerSceneEvent(string sceneName, bool isUn } }; - return switchSceneProgress; + return sceneEventProgress; } /// /// Unloads an additively loaded scene + /// When applicable, the is delivered within the via the /// /// scene name to unload /// - public SceneSwitchProgress UnloadScene(string sceneName) + public SceneEventProgressStatus UnloadScene(string sceneName) { // Make sure the scene is actually loaded var sceneToUnload = SceneManager.GetSceneByName(sceneName); if (sceneToUnload == null) { Debug.LogWarning($"{nameof(UnloadScene)} was called, but the scene {sceneName} is not currently loaded!"); - return null; + return SceneEventProgressStatus.SceneNotLoaded; } - var switchSceneProgress = ValidateServerSceneEvent(sceneName, true); - if (switchSceneProgress == null) + var sceneEventProgress = ValidateServerSceneEvent(sceneName, true); + if (sceneEventProgress.Status != SceneEventProgressStatus.Started) { - return null; + return sceneEventProgress.Status; } - SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; + SceneEventData.SwitchSceneGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; @@ -330,7 +330,7 @@ public SceneSwitchProgress UnloadScene(string sceneName) AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; - switchSceneProgress.SetSceneLoadOperation(sceneUnload); + sceneEventProgress.SetSceneLoadOperation(sceneUnload); if (m_ScenesLoaded.Contains(sceneName)) { m_ScenesLoaded.Remove(sceneName); @@ -346,8 +346,8 @@ public SceneSwitchProgress UnloadScene(string sceneName) ClientId = m_NetworkManager.ServerClientId }); - //Return our scene progress instance - return switchSceneProgress; + //Return the status + return sceneEventProgress.Status; } /// @@ -412,19 +412,19 @@ private void OnSceneUnloaded() /// /// Server side: Loads the scene name in either additive or single loading mode. + /// When applicable, the is delivered within the via the /// /// the name of the scene to be loaded - /// NSS TODO: Either gut and rename the SwitchSceneProgress or completely remove it - /// SceneSwitchProgress (if null this call failed) - public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMode) + /// + public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSceneMode) { - var switchSceneProgress = ValidateServerSceneEvent(sceneName); - if (switchSceneProgress == null) + var sceneEventProgress = ValidateServerSceneEvent(sceneName); + if (sceneEventProgress.Status != SceneEventProgressStatus.Started) { - return null; + return sceneEventProgress.Status; } - SceneEventData.SwitchSceneGuid = switchSceneProgress.Guid; + SceneEventData.SwitchSceneGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Load; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; SceneEventData.LoadSceneMode = loadSceneMode; @@ -461,7 +461,7 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo // Now start loading the scene AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode); sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneName); }; - switchSceneProgress.SetSceneLoadOperation(sceneLoad); + sceneEventProgress.SetSceneLoadOperation(sceneLoad); // Notify the local server that a scene loading event has begun OnSceneEvent?.Invoke(new SceneEvent() @@ -474,7 +474,7 @@ public SceneSwitchProgress LoadScene(string sceneName, LoadSceneMode loadSceneMo }); //Return our scene progress instance - return switchSceneProgress; + return sceneEventProgress.Status; } /// @@ -969,8 +969,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) ClientId = clientId }); - // NSS TODO: Either re-factor and rename or remove this - if (SceneSwitchProgresses.TryGetValue(SceneEventData.SwitchSceneGuid, out SceneSwitchProgress progress)) + if (SceneSwitchProgresses.TryGetValue(SceneEventData.SwitchSceneGuid, out SceneEventProgress progress)) { SceneSwitchProgresses[SceneEventData.SwitchSceneGuid].AddClientAsDone(clientId); } @@ -1146,6 +1145,9 @@ internal void AllClientsReady(ulong[] clientIds, ulong[] timedOutClientIds) OnNotifyClientAllClientsLoadedScene?.Invoke(clientIds, timedOutClientIds); } + + + #endregion } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneSwitchProgress.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs similarity index 64% rename from com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneSwitchProgress.cs rename to com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs index 39b24e8bd6..0ad1a379f8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneSwitchProgress.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs @@ -1,14 +1,37 @@ using System; +using System.Collections; using System.Collections.Generic; using UnityEngine; using AsyncOperation = UnityEngine.AsyncOperation; namespace Unity.Netcode { + /// + /// Used by to determine if a server invoked scene event has started and stored + /// within the + /// Note: This was formally known as SwitchSceneProgress which contained the . + /// The is now delivered via the event through + /// the parameter. + /// Status Values: + /// None - No status + /// Started - Success in starting a load or unload scene event + /// SceneNotLoaded - Returned if you attempt to unload a scene that is not loaded + /// SceneEventInProgress - Returned if you attempt to start a new scene event (load or unload) during an existing scene event + /// InvalidSceneName - Returned if the scene name you specified does not exist + /// + public enum SceneEventProgressStatus + { + None, + Started, + SceneNotLoaded, + SceneEventInProgress, + InvalidSceneName, + } + /// /// Class for tracking scene switching progress by server and clients. /// - public class SceneSwitchProgress + public class SceneEventProgress { /// /// List of clientIds of those clients that is done loading the scene. @@ -31,7 +54,7 @@ public class SceneSwitchProgress public event OnCompletedDelegate OnComplete; /// - /// Is this scene switch progresses completed, all clients are done loading the scene or a timeout has occured. + /// Is this scene switch progresses completed, all clients are done loading the scene or a timeout has occurred. /// public bool IsCompleted { get; private set; } @@ -57,11 +80,23 @@ public class SceneSwitchProgress private NetworkManager m_NetworkManager { get; } - internal SceneSwitchProgress(NetworkManager networkManager) + internal SceneEventProgressStatus Status { get; set; } + + internal SceneEventProgress(NetworkManager networkManager, SceneEventProgressStatus status = SceneEventProgressStatus.Started) + { + if(status == SceneEventProgressStatus.Started) + { + m_NetworkManager = networkManager; + m_TimeOutCoroutine = m_NetworkManager.StartCoroutine(TimeOutSceneEventProgress()); + TimeAtInitiation = networkManager.LocalTime; + } + Status = status; + } + + internal IEnumerator TimeOutSceneEventProgress() { - m_NetworkManager = networkManager; - m_TimeOutCoroutine = m_NetworkManager.StartCoroutine(m_NetworkManager.TimeOutSwitchSceneProgress(this)); - TimeAtInitiation = networkManager.LocalTime; + yield return new WaitForSecondsRealtime(m_NetworkManager.NetworkConfig.LoadSceneTimeOut); + SetTimedOut(); } internal void AddClientAsDone(ulong clientId) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneSwitchProgress.cs.meta b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs.meta similarity index 100% rename from com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneSwitchProgress.cs.meta rename to com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs.meta diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index caea9bb069..89421f6489 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -94,9 +94,6 @@ private IEnumerator DelayedActivate() yield return null; } - private SceneSwitchProgress m_CurrentSceneSwitchProgress; - - public void OnToggle() { if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) @@ -111,36 +108,26 @@ public void OnToggle() private IEnumerator SceneEventCoroutine(bool isLoading) { - while (m_CurrentSceneSwitchProgress == null) + var sceneEventProgressStatus = SceneEventProgressStatus.None; + + while (sceneEventProgressStatus != SceneEventProgressStatus.Started) { if (isLoading) { - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad,UnityEngine.SceneManagement.LoadSceneMode.Additive); + sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad,UnityEngine.SceneManagement.LoadSceneMode.Additive); } else { - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); + sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); } - if (m_CurrentSceneSwitchProgress == null) + if (sceneEventProgressStatus == SceneEventProgressStatus.SceneEventInProgress) { yield return new WaitForSeconds(0.25f); } } m_ToggleObject.isOn = isLoading; m_ToggleObject.enabled = true; - m_CurrentSceneSwitchProgress = null; yield return null; } - - - - public delegate void OnSceneSwitchCompletedDelegateHandler(); - - public event OnSceneSwitchCompletedDelegateHandler OnSceneSwitchCompleted; - - private void CurrentSceneSwitchProgress_OnComplete(bool timedOut) - { - OnSceneSwitchCompleted?.Invoke(); - } } } diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index 8543181baa..c0c4b7c183 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -92,12 +92,6 @@ public override void OnNetworkSpawn() base.OnNetworkSpawn(); } - private SceneSwitchProgress m_CurrentSceneSwitchProgress; - - public delegate void OnSceneSwitchBeginDelegateHandler(); - - public event OnSceneSwitchBeginDelegateHandler OnSceneSwitchBegin; - private bool m_IsReversing; public void OnSwitchScene() @@ -109,38 +103,31 @@ public void OnSwitchScene() if (!m_IsReversing) { - OnSceneSwitchBegin?.Invoke(); - - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo[m_CurrentSceneIndex], UnityEngine.SceneManagement.LoadSceneMode.Additive); - m_CurrentSceneIndex++; - m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; - if(m_CurrentSceneIndex == m_SceneToSwitchTo.Count) + if (NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo[m_CurrentSceneIndex], UnityEngine.SceneManagement.LoadSceneMode.Additive) + == SceneEventProgressStatus.Started) { - m_IsReversing = true; - m_CurrentSceneIndex--; + m_CurrentSceneIndex++; + + if (m_CurrentSceneIndex == m_SceneToSwitchTo.Count) + { + m_IsReversing = true; + m_CurrentSceneIndex--; + } } } else { - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]); - m_CurrentSceneIndex--; - m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; - if(m_CurrentSceneIndex < 0) + if (NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]) == SceneEventProgressStatus.Started) { - m_IsReversing = false; - m_CurrentSceneIndex = 0; + m_CurrentSceneIndex--; + if (m_CurrentSceneIndex < 0) + { + m_IsReversing = false; + m_CurrentSceneIndex = 0; + } } } } } - - public delegate void OnSceneSwitchCompletedDelegateHandler(); - - public event OnSceneSwitchCompletedDelegateHandler OnSceneSwitchCompleted; - - private void CurrentSceneSwitchProgress_OnComplete(bool timedOut) - { - OnSceneSwitchCompleted?.Invoke(); - } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs b/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs index daaac90c27..841feabb3d 100644 --- a/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs +++ b/testproject/Assets/Tests/Manual/Scripts/SwitchSceneHandler.cs @@ -88,32 +88,18 @@ private IEnumerator AutoSwitch() } - private SceneSwitchProgress m_CurrentSceneSwitchProgress; - - public delegate void OnSceneSwitchBeginDelegateHandler(); - - public event OnSceneSwitchBeginDelegateHandler OnSceneSwitchBegin; - public void OnSwitchScene() { if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) { - OnSceneSwitchBegin?.Invoke(); m_ExitingScene = true; ExitingNow = true; - m_CurrentSceneSwitchProgress = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo, UnityEngine.SceneManagement.LoadSceneMode.Single); - - m_CurrentSceneSwitchProgress.OnComplete += CurrentSceneSwitchProgress_OnComplete; + var sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToSwitchTo, UnityEngine.SceneManagement.LoadSceneMode.Single); + if (sceneEventProgressStatus != SceneEventProgressStatus.Started) + { + Debug.LogError($"{nameof(NetworkSceneManager.LoadScene)} returned a {nameof(SceneEventProgressStatus)} value of {sceneEventProgressStatus}"); + } } } - - public delegate void OnSceneSwitchCompletedDelegateHandler(); - - public event OnSceneSwitchCompletedDelegateHandler OnSceneSwitchCompleted; - - private void CurrentSceneSwitchProgress_OnComplete(bool timedOut) - { - OnSceneSwitchCompleted?.Invoke(); - } } } From fa2aaaa9396d03421df81423d8297f513febab1f Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 5 Aug 2021 14:29:21 -0500 Subject: [PATCH 057/106] feat Finalized replacing all notification related events with scene events. SceneEvent now includes two additional types: S2C_LoadComplete: Server to client(s) all clients loaded scene S2C_UnLoadComplete: Server to client(s) all clients unloaded scene These replace the event notifications that specified all clients have loaded a specific scene, with the additional functionality that this notification is sent for both loading and unloading for all loading modes. Removed the legacy internal messages and related methods for all clients done loading a scene. This should mark the final overhaul for notifications --- .../Messaging/IInternalMessageHandler.cs | 1 - .../Messaging/InternalMessageHandler.cs | 10 - .../MessageQueue/MessageQueueContainer.cs | 1 - .../MessageQueue/MessageQueueProcessor.cs | 7 - ...nternalMessageHandlerProfilingDecorator.cs | 10 - .../SceneManagement/NetworkSceneManager.cs | 274 ++++++++++-------- .../Runtime/SceneManagement/SceneEventData.cs | 109 +++++-- .../SceneManagement/SceneEventProgress.cs | 61 ++-- ...alMessageHandlerProfilingDecoratorTests.cs | 8 - .../SceneEventNotificationQueue.cs | 23 +- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 2 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 4 +- 12 files changed, 292 insertions(+), 218 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs index 94222441f5..50ec27129a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/IInternalMessageHandler.cs @@ -19,6 +19,5 @@ internal interface IInternalMessageHandler void HandleUnnamedMessage(ulong clientId, Stream stream); void HandleNamedMessage(ulong clientId, Stream stream); void HandleNetworkLog(ulong clientId, Stream stream); - void HandleAllClientsSwitchSceneCompleted(ulong clientId, Stream stream); } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index fdf37ea26d..c52805581c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -328,15 +328,5 @@ internal static void HandleSnapshot(ulong clientId, Stream messageStream) { NetworkManager.Singleton.SnapshotSystem.ReadSnapshot(clientId, messageStream); } - - public void HandleAllClientsSwitchSceneCompleted(ulong clientId, Stream stream) - { - using (var reader = PooledNetworkReader.Get(stream)) - { - var clientIds = reader.ReadULongArray(); - var timedOutClientIds = reader.ReadULongArray(); - NetworkManager.SceneManager.AllClientsReady(clientIds, timedOutClientIds); - } - } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs index 8f84d9a875..b8bdd322b1 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs @@ -35,7 +35,6 @@ public enum MessageType SnapshotData, NetworkVariableDelta, SceneEvent, - AllClientsLoadedScene, ParentSync, None //Indicates end of frame diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs index 03649f2206..4b81f7ece2 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs @@ -125,13 +125,6 @@ public void ProcessMessage(in MessageFrameItem item) break; case MessageQueueContainer.MessageType.SceneEvent: m_NetworkManager.MessageHandler.HandleSceneEvent(item.NetworkId, item.NetworkBuffer); - break; - case MessageQueueContainer.MessageType.AllClientsLoadedScene: - if (m_NetworkManager.IsClient) - { - m_NetworkManager.MessageHandler.HandleAllClientsSwitchSceneCompleted(item.NetworkId, item.NetworkBuffer); - } - break; case MessageQueueContainer.MessageType.ParentSync: if (m_NetworkManager.IsClient) diff --git a/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs b/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs index 48ac1740e5..251838d1b9 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs @@ -21,7 +21,6 @@ internal class InternalMessageHandlerProfilingDecorator : IInternalMessageHandle private readonly ProfilerMarker m_MessageReceiveQueueItemServerRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.{nameof(MessageQueueContainer.MessageType.ServerRpc)}"); private readonly ProfilerMarker m_MessageReceiveQueueItemClientRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.{nameof(MessageQueueContainer.MessageType.ClientRpc)}"); private readonly ProfilerMarker m_MessageReceiveQueueItemInternalMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.InternalMessage"); - private readonly ProfilerMarker m_HandleAllClientsSwitchSceneCompleted = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleAllClientsSwitchSceneCompleted)}"); private readonly IInternalMessageHandler m_MessageHandler; @@ -180,14 +179,5 @@ public void HandleSceneEvent(ulong clientId, Stream stream) m_HandleSceneEvent.End(); } - - public void HandleAllClientsSwitchSceneCompleted(ulong clientId, Stream stream) - { - m_HandleAllClientsSwitchSceneCompleted.Begin(); - - m_MessageHandler.HandleAllClientsSwitchSceneCompleted(clientId, stream); - - m_HandleAllClientsSwitchSceneCompleted.End(); - } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 26e663792b..e4837b8353 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -38,16 +38,41 @@ public class SceneEvent public string SceneName; /// - /// This will be the relative client identifier that this event pertains to - /// is invoked both on the server and the client and set to the local client identifier. - /// is invoked both on the server and the client and set to the local client identifier. - /// is invoked when synchronizing a client, the ClientId will be the client being synchronized. - /// is invoked only on the server and the ClientId will be the client that sent the notification. - /// is invoked only on the server and the ClientId will be the client that sent the notification. - /// is invoked on both the client and the server when the client has finished synchronizing. - /// is invoked on the client only if the server determines it needs to be re-synchronized after synchronizing. + /// Events that always set to the local client identifier + /// and only triggered locally: + /// + /// + /// + /// + /// + /// Events that always set to the local client identifier, + /// are triggered locally, and a host or server will trigger externally generated + /// scene event message types (i.e. sent by a client): + /// + /// + /// + /// + /// Events that always set to the ServerId: + /// + /// /// public ulong ClientId; + + /// + /// List of clients that completed a loading or unloading event + /// Applies only to: + /// + /// + /// + public List ClientsThatCompleted; + + /// + /// List of clients that timed out during a loading or unloading event + /// Applies only to: + /// + /// + /// + public List ClientsThatTimedOut; } /// @@ -68,36 +93,10 @@ public class NetworkSceneManager /// public event SceneEventDelegate OnSceneEvent; - #region Client and Server Scene Loading Events to be Removed? - /// - /// Delegate for when all clients have reported to the server that they have completed scene transition or timed out - /// - /// - public delegate void NotifyServerAllClientsLoadedSceneDelegate(SceneEventProgress progress, bool timedOut); - - /// - /// Delegate for when the clients get notified by the server that all clients have completed their scene transitions. - /// - /// - public delegate void NotifyClientAllClientsLoadedSceneDelegate(ulong[] clientIds, ulong[] timedOutClientIds); - - /// - /// Event that is invoked on the server when all clients have reported that they have completed scene transition - /// - public event NotifyServerAllClientsLoadedSceneDelegate OnNotifyServerAllClientsLoadedScene; - - /// - /// Event that is invoked on the clients after all clients have successfully completed scene transition or timed out. - /// This event happens after fires on the server and the message is sent to the clients. - /// It relies on MessageSender, which doesn't send events from the server to itself (which is the case for a Host client). - /// - public event NotifyClientAllClientsLoadedSceneDelegate OnNotifyClientAllClientsLoadedScene; - #endregion - internal readonly HashSet RegisteredSceneNames = new HashSet(); internal readonly Dictionary SceneNameToIndex = new Dictionary(); internal readonly Dictionary SceneIndexToString = new Dictionary(); - internal readonly Dictionary SceneSwitchProgresses = new Dictionary(); + internal readonly Dictionary SceneEventProgressTracking = new Dictionary(); internal readonly Dictionary ScenePlacedObjects = new Dictionary(); // Used for observed object synchronization @@ -116,10 +115,10 @@ public class NetworkSceneManager // not destroy temporary scene are moved into the active scene internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; - //Client and Server: used for all scene event processing exception for client synchronization + //Client and Server: used for all scene event processing except for ClientSynchEventData specific events internal SceneEventData SceneEventData; - //Server Side: Used specifically for scene synchronization (late joining and newly approved client connections) + //Server Side: Used specifically for scene synchronization and scene event progress related events. internal SceneEventData ClientSynchEventData; private NetworkManager m_NetworkManager { get; } @@ -239,28 +238,21 @@ private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnl 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 this method."); } + // Return scene event already in progress if one is already in progress... :) if (s_IsSceneEventActive) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning("Scene event is already in progress"); - } - return new SceneEventProgress(null, SceneEventProgressStatus.SceneEventInProgress); } + // Return invalid scene name status if the scene name is invalid... :) if (!RegisteredSceneNames.Contains(sceneName)) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"The scene {sceneName} is not registered as a switchable scene."); - } - return new SceneEventProgress(null, SceneEventProgressStatus.InvalidSceneName); } var sceneEventProgress = new SceneEventProgress(m_NetworkManager); - SceneSwitchProgresses.Add(sceneEventProgress.Guid, sceneEventProgress); + sceneEventProgress.SceneName = sceneName; + SceneEventProgressTracking.Add(sceneEventProgress.Guid, sceneEventProgress); if (!isUnloading) { @@ -274,29 +266,48 @@ private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnl s_IsSceneEventActive = true; - // NSS TODO: switchSceneProgress needs to be re-factored - // This replicates what OnSceneEvent handles. - sceneEventProgress.OnComplete += timedOut => - { - OnNotifyServerAllClientsLoadedScene?.Invoke(sceneEventProgress, timedOut); - // Send notification to all clients that everyone is done loading - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.AllClientsLoadedScene, NetworkChannel.Internal, - m_NetworkManager.ConnectedClientsIds, k_NetworkUpdateStage); + // Set our callback delegate handler for completion + sceneEventProgress.OnComplete = OnSceneEventProgressCompleted; - if (context != null) - { - using (var nonNullContext = (InternalCommandContext)context) - { - var doneClientIds = sceneEventProgress.DoneClients.ToArray(); - var timedOutClientIds = m_NetworkManager.ConnectedClients.Keys.Except(doneClientIds).ToArray(); + return sceneEventProgress; + } - nonNullContext.NetworkWriter.WriteULongArray(doneClientIds, doneClientIds.Length); - nonNullContext.NetworkWriter.WriteULongArray(timedOutClientIds, timedOutClientIds.Length); - } + /// + /// Callback for the handler + /// + /// + /// + internal bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress) + { + // Send a message to all clients that all clients are done loading or unloading + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, m_NetworkManager.ConnectedClientsIds, k_NetworkUpdateStage); + if (context != null) + { + using (var nonNullContext = (InternalCommandContext)context) + { + ClientSynchEventData.SceneEventGuid = sceneEventProgress.Guid; + ClientSynchEventData.SceneIndex = SceneNameToIndex[sceneEventProgress.SceneName]; + ClientSynchEventData.SceneEventType = sceneEventProgress.SceneEventType; + ClientSynchEventData.ClientsCompleted = sceneEventProgress.DoneClients; + ClientSynchEventData.ClientsTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(); + ClientSynchEventData.OnWrite(nonNullContext.NetworkWriter); } - }; + } - return sceneEventProgress; + // Send a local notification to the server that all clients are done loading or unloading + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = sceneEventProgress.SceneEventType, + SceneName = sceneEventProgress.SceneName, + ClientId = m_NetworkManager.ServerClientId, + LoadSceneMode =sceneEventProgress.LoadSceneMode, + ClientsThatCompleted = sceneEventProgress.DoneClients, + ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(), + }); + + SceneEventProgressTracking.Remove(sceneEventProgress.Guid); + + return false; } /// @@ -321,9 +332,10 @@ public SceneEventProgressStatus UnloadScene(string sceneName) return sceneEventProgress.Status; } - SceneEventData.SwitchSceneGuid = sceneEventProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload; + SceneEventData.SceneEventGuid = sceneEventProgress.Guid; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + sceneEventProgress.SceneEventType = SceneEventData.SceneEventTypes.S2C_UnLoadComplete; // Sends the unload scene notification SendSceneEventData(m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray()); @@ -343,7 +355,7 @@ public SceneEventProgressStatus UnloadScene(string sceneName) SceneEventType = SceneEventData.SceneEventType, LoadSceneMode = SceneEventData.LoadSceneMode, SceneName = sceneName, - ClientId = m_NetworkManager.ServerClientId + ClientId = m_NetworkManager.ServerClientId // Server can only invoke this }); //Return the status @@ -383,7 +395,7 @@ private void OnClientUnloadScene() SceneEventType = SceneEventData.SceneEventType, LoadSceneMode = SceneEventData.LoadSceneMode, SceneName = sceneName, - ClientId = m_NetworkManager.LocalClientId + ClientId = m_NetworkManager.LocalClientId // Server sent this message to the client, but client is executing it }); } @@ -392,13 +404,9 @@ private void OnClientUnloadScene() /// private void OnSceneUnloaded() { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete; - if (!m_NetworkManager.IsServer) - { - SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); - } + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_UnloadComplete; - // Notify the client or server that a scene was unloaded + //First, notify the client or server that a scene was unloaded OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventType, @@ -407,6 +415,18 @@ private void OnSceneUnloaded() ClientId = m_NetworkManager.IsServer ? m_NetworkManager.ServerClientId : m_NetworkManager.LocalClientId }); + if (!m_NetworkManager.IsServer) + { + SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); + } + else //Second, server sets itself as having finished loading + { + if (SceneEventProgressTracking.ContainsKey(SceneEventData.SceneEventGuid)) + { + SceneEventProgressTracking[SceneEventData.SceneEventGuid].AddClientAsDone(m_NetworkManager.ServerClientId); + } + } + s_IsSceneEventActive = false; } @@ -424,8 +444,10 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc return sceneEventProgress.Status; } - SceneEventData.SwitchSceneGuid = sceneEventProgress.Guid; - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Load; + sceneEventProgress.SceneEventType = SceneEventData.SceneEventTypes.S2C_LoadComplete; + sceneEventProgress.LoadSceneMode = loadSceneMode; + SceneEventData.SceneEventGuid = sceneEventProgress.Guid; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Load; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; SceneEventData.LoadSceneMode = loadSceneMode; @@ -448,7 +470,7 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc { OnSceneEvent?.Invoke(new SceneEvent() { - SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload, + SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload, LoadSceneMode = LoadSceneMode.Additive, SceneName = additiveSceneName, ClientId = m_NetworkManager.ServerClientId @@ -507,7 +529,7 @@ internal void OnClientSceneLoadingEvent(Stream objectStream) OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = SceneManager.UnloadSceneAsync(loadedSceneName), - SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Unload, + SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload, LoadSceneMode = LoadSceneMode.Additive, SceneName = loadedSceneName, ClientId = m_NetworkManager.LocalClientId @@ -647,14 +669,20 @@ private void OnServerLoadedScene() s_IsSceneEventActive = false; - // Notify local server that the scene was loaded + //First, notify local server that the scene was loaded OnSceneEvent?.Invoke(new SceneEvent() { - SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete, + SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = SceneEventData.LoadSceneMode, SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), ClientId = m_NetworkManager.ServerClientId }); + + //Second, set the server as having loaded for the associated SceneEventProgress + if (SceneEventProgressTracking.ContainsKey(SceneEventData.SceneEventGuid)) + { + SceneEventProgressTracking[SceneEventData.SceneEventGuid].AddClientAsDone(m_NetworkManager.ServerClientId); + } } /// @@ -672,14 +700,14 @@ private void OnClientLoadedScene() } } - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); s_IsSceneEventActive = false; // Notify local client that the scene was loaded OnSceneEvent?.Invoke(new SceneEvent() { - SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete, + SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = SceneEventData.LoadSceneMode, SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), ClientId = m_NetworkManager.LocalClientId @@ -708,7 +736,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.InitializeForSynch(); ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); - ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Sync; + ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Sync; // Organize how (and when) we serialize our NetworkObjects for (int i = 0; i < SceneManager.sceneCount; i++) @@ -787,7 +815,7 @@ private void OnClientBeginSync(uint sceneIndex) { OnSceneEvent?.Invoke(new SceneEvent() { - SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Sync, + SceneEventType = SceneEventData.SceneEventTypes.S2C_Sync, ClientId = m_NetworkManager.LocalClientId, }); } @@ -802,7 +830,7 @@ private void OnClientBeginSync(uint sceneIndex) OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = sceneLoad, - SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_Load, + SceneEventType = SceneEventData.SceneEventTypes.S2C_Load, LoadSceneMode = loadSceneMode, SceneName = sceneName, ClientId = m_NetworkManager.LocalClientId, @@ -857,7 +885,7 @@ private void ClientLoadedSynchronization(uint sceneIndex) // Send notification back to server that we finished loading this scene ClientSynchEventData.LoadSceneMode = loadSceneMode; - ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete; + ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete; ClientSynchEventData.SceneIndex = sceneIndex; var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, @@ -873,7 +901,7 @@ private void ClientLoadedSynchronization(uint sceneIndex) // Send notification to local client that the scene has finished loading OnSceneEvent?.Invoke(new SceneEvent() { - SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Load_Complete, + SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = loadSceneMode, SceneName = sceneName, ClientId = m_NetworkManager.LocalClientId, @@ -892,19 +920,17 @@ private void HandleClientSceneEvent(Stream stream) { switch (SceneEventData.SceneEventType) { - // Both events are basically the same with some minor differences - //case SceneEventData.SceneEventTypes.EventSwitch: - case SceneEventData.SceneEventTypes.S2C_Event_Load: + case SceneEventData.SceneEventTypes.S2C_Load: { OnClientSceneLoadingEvent(stream); break; } - case SceneEventData.SceneEventTypes.S2C_Event_Unload: + case SceneEventData.SceneEventTypes.S2C_Unload: { OnClientUnloadScene(); break; } - case SceneEventData.SceneEventTypes.S2C_Event_Sync: + case SceneEventData.SceneEventTypes.S2C_Sync: { if (!SceneEventData.IsDoneWithSynchronization()) { @@ -912,7 +938,7 @@ private void HandleClientSceneEvent(Stream stream) } else { - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_Event_Sync_Complete; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_SyncComplete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); // All scenes are synchronized, let the server know we are done synchronizing @@ -923,20 +949,35 @@ private void HandleClientSceneEvent(Stream stream) OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventType, - ClientId = m_NetworkManager.LocalClientId + ClientId = m_NetworkManager.LocalClientId, // Client sent this to the server }); } break; } - case SceneEventData.SceneEventTypes.S2C_Event_ReSync: + case SceneEventData.SceneEventTypes.S2C_ReSync: { // Notify the client that they have been re-synchronized after being synchronized with an in progress game session OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventType, - ClientId = m_NetworkManager.LocalClientId + ClientId = m_NetworkManager.ServerClientId, // Server sent this to client }); + break; + } + case SceneEventData.SceneEventTypes.S2C_LoadComplete: + case SceneEventData.SceneEventTypes.S2C_UnLoadComplete: + { + // Notify client that all clients have finished loading or unloading + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventData.SceneEventType, + SceneName = m_NetworkManager.SceneManager.GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + ClientId = m_NetworkManager.ServerClientId, + LoadSceneMode = SceneEventData.LoadSceneMode, + ClientsThatCompleted = SceneEventData.ClientsCompleted, + ClientsThatTimedOut = SceneEventData.ClientsTimedOut, + }); break; } default: @@ -956,9 +997,9 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { switch (SceneEventData.SceneEventType) { - case SceneEventData.SceneEventTypes.C2S_Event_Load_Complete: + case SceneEventData.SceneEventTypes.C2S_LoadComplete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_Event_Load_Complete)}] Client Id {clientId} finished loading additive scene."); + Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_LoadComplete)}] Client Id {clientId} finished loading additive scene."); // Notify the local server that the client has finished loading a scene OnSceneEvent?.Invoke(new SceneEvent() @@ -969,15 +1010,19 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) ClientId = clientId }); - if (SceneSwitchProgresses.TryGetValue(SceneEventData.SwitchSceneGuid, out SceneEventProgress progress)) + if (SceneEventProgressTracking.ContainsKey(SceneEventData.SceneEventGuid)) { - SceneSwitchProgresses[SceneEventData.SwitchSceneGuid].AddClientAsDone(clientId); + SceneEventProgressTracking[SceneEventData.SceneEventGuid].AddClientAsDone(clientId); } break; } - case SceneEventData.SceneEventTypes.C2S_Event_Unload_Complete: + case SceneEventData.SceneEventTypes.C2S_UnloadComplete: { + if (SceneEventProgressTracking.ContainsKey(SceneEventData.SceneEventGuid)) + { + SceneEventProgressTracking[SceneEventData.SceneEventGuid].AddClientAsDone(clientId); + } // Notify the local server that the client has finished unloading a scene OnSceneEvent?.Invoke(new SceneEvent() { @@ -989,7 +1034,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) break; } - case SceneEventData.SceneEventTypes.C2S_Event_Sync_Complete: + case SceneEventData.SceneEventTypes.C2S_SyncComplete: { // Notify the local server that a client has finished synchronizing OnSceneEvent?.Invoke(new SceneEvent() @@ -1002,7 +1047,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) if (SceneEventData.ClientNeedsReSynchronization()) { Debug.Log($"Re-Synchronizing client {clientId} for missed destroyed NetworkObjects."); - SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Event_ReSync; + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_ReSync; SendSceneEventData(new ulong[] { clientId }); OnSceneEvent?.Invoke(new SceneEvent() @@ -1058,7 +1103,8 @@ public void HandleSceneEvent(ulong clientId, Stream stream) } } - #region General Methods + #region NetworkObject Scene Transitioning Related Method + private void MoveObjectsToDontDestroyOnLoad() { // Move ALL NetworkObjects to the temp scene @@ -1134,20 +1180,6 @@ private void MoveObjectsToScene(Scene scene) SceneManager.MoveGameObjectToScene(sobj.gameObject, scene); } } - - /// - /// NSS TODO: Make this a scene event - /// - /// - /// - internal void AllClientsReady(ulong[] clientIds, ulong[] timedOutClientIds) - { - OnNotifyClientAllClientsLoadedScene?.Invoke(clientIds, timedOutClientIds); - } - - - - #endregion } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 93e8851485..3d842c6feb 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -22,18 +22,20 @@ public class SceneEventData : IDisposable /// public enum SceneEventTypes { - S2C_Event_Load, //Server to client load additive scene - S2C_Event_Unload, //Server to client unload additive scene - S2C_Event_Sync, //Server to client late join approval synchronization - S2C_Event_ReSync, //Server to client update of objects that were destroyed during sync - C2S_Event_Load_Complete, //Client to server load complete - C2S_Event_Unload_Complete, //Client to server unload complete - C2S_Event_Sync_Complete, //Client to server + S2C_Load, //Server to client load additive scene + S2C_Unload, //Server to client unload additive scene + S2C_Sync, //Server to client late join approval synchronization + S2C_ReSync, //Server to client update of objects that were destroyed during sync + S2C_LoadComplete, //Server to client(s) all clients loaded scene + S2C_UnLoadComplete, //Server to client(s) all clients unloaded scene + C2S_LoadComplete, //Client to server load complete + C2S_UnloadComplete, //Client to server unload complete + C2S_SyncComplete, //Client to server } internal SceneEventTypes SceneEventType; internal LoadSceneMode LoadSceneMode; - internal Guid SwitchSceneGuid; + internal Guid SceneEventGuid; internal uint SceneIndex; internal ulong TargetClientId; @@ -61,6 +63,16 @@ public enum SceneEventTypes private NetworkManager m_NetworkManager; + /// + /// Client side and only applies to the following scene event types: + /// + /// + /// + internal SceneEvent SceneEvent; + + internal List ClientsCompleted; + internal List ClientsTimedOut; + /// /// Client Side: /// Gets the next scene index to be loaded for approval and/or late joining @@ -125,10 +137,12 @@ internal bool IsSceneEventClientSide() { switch (SceneEventType) { - case SceneEventTypes.S2C_Event_Load: - case SceneEventTypes.S2C_Event_Unload: - case SceneEventTypes.S2C_Event_Sync: - case SceneEventTypes.S2C_Event_ReSync: + case SceneEventTypes.S2C_Load: + case SceneEventTypes.S2C_Unload: + case SceneEventTypes.S2C_Sync: + case SceneEventTypes.S2C_ReSync: + case SceneEventTypes.S2C_LoadComplete: + case SceneEventTypes.S2C_UnLoadComplete: { return true; } @@ -171,14 +185,14 @@ internal void OnWrite(NetworkWriter writer) writer.WriteByte((byte)LoadSceneMode); - if (SceneEventType != SceneEventTypes.S2C_Event_Sync) + if (SceneEventType != SceneEventTypes.S2C_Sync) { - writer.WriteByteArray(SwitchSceneGuid.ToByteArray()); + writer.WriteByteArray(SceneEventGuid.ToByteArray()); } writer.WriteUInt32Packed(SceneIndex); - if (SceneEventType == SceneEventTypes.S2C_Event_Sync) + if (SceneEventType == SceneEventTypes.S2C_Sync) { writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); @@ -222,15 +236,20 @@ internal void OnWrite(NetworkWriter writer) } } - if (SceneEventType == SceneEventTypes.C2S_Event_Sync_Complete) + if (SceneEventType == SceneEventTypes.C2S_SyncComplete) { WriteClientSynchronizationResults(writer); } - if (SceneEventType == SceneEventTypes.S2C_Event_ReSync) + if (SceneEventType == SceneEventTypes.S2C_ReSync) { WriteClientReSynchronizationData(writer); } + + if (SceneEventType == SceneEventTypes.S2C_LoadComplete || SceneEventType == SceneEventTypes.S2C_UnLoadComplete) + { + WriteSceneEventProgressDone(writer); + } } /// @@ -261,14 +280,14 @@ internal void OnRead(NetworkReader reader) Debug.LogError($"Serialization Read Error: {nameof(LoadSceneMode)} vale {loadSceneModeValue} is not within the range of the defined {nameof(LoadSceneMode)} enumerator!"); } - if (SceneEventType != SceneEventTypes.S2C_Event_Sync) + if (SceneEventType != SceneEventTypes.S2C_Sync) { - SwitchSceneGuid = new Guid(reader.ReadByteArray()); + SceneEventGuid = new Guid(reader.ReadByteArray()); } SceneIndex = reader.ReadUInt32Packed(); - if (SceneEventType == SceneEventTypes.S2C_Event_Sync) + if (SceneEventType == SceneEventTypes.S2C_Sync) { m_NetworkObjectsSync.Clear(); var keyPairCount = reader.ReadInt32Packed(); @@ -301,15 +320,20 @@ internal void OnRead(NetworkReader reader) } } - if (SceneEventType == SceneEventTypes.C2S_Event_Sync_Complete) + if (SceneEventType == SceneEventTypes.C2S_SyncComplete) { CheckClientSynchronizationResults(reader); } - if (SceneEventType == SceneEventTypes.S2C_Event_ReSync) + if (SceneEventType == SceneEventTypes.S2C_ReSync) { ReadClientReSynchronizationData(reader); } + + if (SceneEventType == SceneEventTypes.S2C_LoadComplete || SceneEventType == SceneEventTypes.S2C_UnLoadComplete) + { + ReadSceneEventProgressDone(reader); + } } /// @@ -463,6 +487,47 @@ internal void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networ } } + /// + /// Writes the all clients loaded or unloaded completed and timed out lists + /// + /// + internal void WriteSceneEventProgressDone(NetworkWriter writer) + { + writer.WriteUInt16Packed((ushort)ClientsCompleted.Count); + foreach (var clientId in ClientsCompleted) + { + writer.WriteUInt64Packed(clientId); + } + + writer.WriteUInt16Packed((ushort)ClientsTimedOut.Count); + foreach (var clientId in ClientsTimedOut) + { + writer.WriteUInt64Packed(clientId); + } + } + + /// + /// Reads the all clients loaded or unloaded completed and timed out lists + /// + /// + internal void ReadSceneEventProgressDone(NetworkReader reader) + { + var completedCount = reader.ReadUInt16Packed(); + ClientsCompleted = new List(); + for (int i = 0; i < completedCount; i++) + { + ClientsCompleted.Add(reader.ReadUInt64Packed()); + } + + var timedOutCount = reader.ReadUInt16Packed(); + ClientsTimedOut = new List(); + for (int i = 0; i < timedOutCount; i++) + { + ClientsTimedOut.Add(reader.ReadUInt64Packed()); + } + } + + /// /// Used to store data during an asynchronous scene loading event /// diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs index 0ad1a379f8..f44e0716f6 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; +using UnityEngine.SceneManagement; using AsyncOperation = UnityEngine.AsyncOperation; namespace Unity.Netcode @@ -29,49 +30,44 @@ public enum SceneEventProgressStatus } /// - /// Class for tracking scene switching progress by server and clients. + /// Server side only: + /// This tracks the progress of clients during a load or unload scene event /// - public class SceneEventProgress + internal class SceneEventProgress { /// /// List of clientIds of those clients that is done loading the scene. /// - public List DoneClients { get; } = new List(); + internal List DoneClients { get; } = new List(); /// /// The NetworkTime at the moment the scene switch was initiated by the server. /// - public NetworkTime TimeAtInitiation { get; } + internal NetworkTime TimeAtInitiation { get; } /// /// Delegate type for when the switch scene progress is completed. Either by all clients done loading the scene or by time out. /// - public delegate void OnCompletedDelegate(bool timedOut); + internal delegate bool OnCompletedDelegate(SceneEventProgress sceneEventProgress); /// /// The callback invoked when the switch scene progress is completed. Either by all clients done loading the scene or by time out. /// - public event OnCompletedDelegate OnComplete; + internal OnCompletedDelegate OnComplete; /// /// Is this scene switch progresses completed, all clients are done loading the scene or a timeout has occurred. /// - public bool IsCompleted { get; private set; } + internal bool IsCompleted { get; private set; } - /// - /// If all clients are done loading the scene, at the moment of completed. - /// - public bool IsAllClientsDoneLoading { get; private set; } + internal bool TimedOut { get; private set; } /// - /// Delegate type for when a client is done loading the scene. + /// If all clients are done loading the scene, at the moment of completed. /// - public delegate void OnClientLoadedSceneDelegate(ulong clientId); + internal bool AreAllClientsDoneLoading { get; private set; } - /// - /// The callback invoked when a client is done loading the scene. - /// - public event OnClientLoadedSceneDelegate OnClientLoadedScene; + internal string SceneName { get; set; } internal Guid Guid { get; } = Guid.NewGuid(); @@ -82,9 +78,13 @@ public class SceneEventProgress internal SceneEventProgressStatus Status { get; set; } + internal SceneEventData.SceneEventTypes SceneEventType { get; set; } + + internal LoadSceneMode LoadSceneMode; + internal SceneEventProgress(NetworkManager networkManager, SceneEventProgressStatus status = SceneEventProgressStatus.Started) { - if(status == SceneEventProgressStatus.Started) + if (status == SceneEventProgressStatus.Started) { m_NetworkManager = networkManager; m_TimeOutCoroutine = m_NetworkManager.StartCoroutine(TimeOutSceneEventProgress()); @@ -96,13 +96,13 @@ internal SceneEventProgress(NetworkManager networkManager, SceneEventProgressSta internal IEnumerator TimeOutSceneEventProgress() { yield return new WaitForSecondsRealtime(m_NetworkManager.NetworkConfig.LoadSceneTimeOut); - SetTimedOut(); + TimedOut = true; + CheckCompletion(); } internal void AddClientAsDone(ulong clientId) { DoneClients.Add(clientId); - OnClientLoadedScene?.Invoke(clientId); CheckCompletion(); } @@ -120,25 +120,18 @@ internal void SetSceneLoadOperation(AsyncOperation sceneLoadOperation) internal void CheckCompletion() { - if (!IsCompleted && DoneClients.Count == m_NetworkManager.ConnectedClientsList.Count && m_SceneLoadOperation.isDone) + if ((!IsCompleted && DoneClients.Count == m_NetworkManager.ConnectedClientsList.Count && m_SceneLoadOperation.isDone) || (!IsCompleted && TimedOut)) { IsCompleted = true; - IsAllClientsDoneLoading = true; - m_NetworkManager.SceneManager.SceneSwitchProgresses.Remove(Guid); - OnComplete?.Invoke(false); + AreAllClientsDoneLoading = true; + // If OnComplete is not registered or it is and returns true then remove this from the progress tracking + if (OnComplete == null || (OnComplete != null && OnComplete.Invoke(this))) + { + m_NetworkManager.SceneManager.SceneEventProgressTracking.Remove(Guid); + } m_NetworkManager.StopCoroutine(m_TimeOutCoroutine); } } - - internal void SetTimedOut() - { - if (!IsCompleted) - { - IsCompleted = true; - m_NetworkManager.SceneManager.SceneSwitchProgresses.Remove(Guid); - OnComplete?.Invoke(true); - } - } } } diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs index 8c1005c795..6d6440c646 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs @@ -117,13 +117,5 @@ public void MessageReceiveQueueItemCallsUnderlyingHandler() LogAssert.Expect(LogType.Log, nameof(m_Decorator.MessageReceiveQueueItem)); } - - [Test] - public void HandleAllClientsSwitchSceneCompleted() - { - m_Decorator.HandleAllClientsSwitchSceneCompleted(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleAllClientsSwitchSceneCompleted)); - } } } diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs index ffb51e0333..93ca815768 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs @@ -44,7 +44,28 @@ private void Start() /// private void OnSceneEvent(SceneEvent sceneEvent) { - var sceneEventMsg = $"[{sceneEvent.ClientId} | {sceneEvent.SceneEventType} | {sceneEvent.SceneName} | {sceneEvent.LoadSceneMode}]"; + var sceneEventMsg = $"[{sceneEvent.ClientId} | {sceneEvent.SceneEventType} | {sceneEvent.SceneName}"; + if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.S2C_Load || sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.C2S_LoadComplete) + { + sceneEventMsg += $" | { sceneEvent.LoadSceneMode}"; + } + + if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.S2C_UnLoadComplete || sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.S2C_LoadComplete) + { + sceneEventMsg += $" | Loaded ({sceneEvent.ClientsThatCompleted.Count}) : ("; + foreach(var clientId in sceneEvent.ClientsThatCompleted) + { + sceneEventMsg += $"{clientId}, "; + } + sceneEventMsg += $") | TimedOut ({sceneEvent.ClientsThatTimedOut.Count}) : ("; + foreach (var clientId in sceneEvent.ClientsThatTimedOut) + { + sceneEventMsg += $"{clientId}, "; + } + sceneEventMsg += ")"; + } + sceneEventMsg += "]"; + m_SceneEvents.Enqueue(new SceneEventNotification() { SceneEvent = sceneEventMsg, TimeToLive = Time.realtimeSinceStartup + TimeToLive }); if (LogToConsole) { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 7fed16dffe..8cdcd121e7 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -99,7 +99,7 @@ private void OnSceneEvent(SceneEvent sceneEvent) { switch (sceneEvent.SceneEventType) { - case SceneEventData.SceneEventTypes.S2C_Event_Unload: + case SceneEventData.SceneEventTypes.S2C_Unload: { if (sceneEvent.LoadSceneMode == LoadSceneMode.Single && (gameObject.scene.name == sceneEvent.SceneName)) { diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index ddbbb123f7..e375975020 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -119,7 +119,7 @@ private void OnSceneEvent(SceneEvent sceneEvent) { switch (sceneEvent.SceneEventType) { - case SceneEventData.SceneEventTypes.S2C_Event_Unload: + case SceneEventData.SceneEventTypes.S2C_Unload: { if (sceneEvent.LoadSceneMode == LoadSceneMode.Additive && (gameObject.scene.name == sceneEvent.SceneName)) { @@ -127,7 +127,7 @@ private void OnSceneEvent(SceneEvent sceneEvent) } break; } - case SceneEventData.SceneEventTypes.S2C_Event_Load: + case SceneEventData.SceneEventTypes.S2C_Load: { if (sceneEvent.LoadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneEvent.SceneName) || !SpawnInSourceScene)) { From 37be5bc455300f633a3fd622e30cded02fa0e8d2 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 5 Aug 2021 15:35:31 -0500 Subject: [PATCH 058/106] style Did one last XML Documentation pass. --- .../Runtime/Configuration/NetworkConfig.cs | 4 +- .../SceneManagement/NetworkSceneManager.cs | 58 +++++++------ .../Runtime/SceneManagement/SceneEventData.cs | 83 ++++++++++++++++--- .../SceneManagement/SceneEventProgress.cs | 19 ++++- 4 files changed, 123 insertions(+), 41 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs index 921b97ea83..89351e322c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs @@ -150,9 +150,9 @@ public class NetworkConfig public HashSize RpcHashSize = HashSize.VarIntFourBytes; /// - /// The amount of seconds to wait on all clients to load requested scene before the SwitchSceneProgress onComplete callback, that waits for all clients to complete loading, is called anyway. + /// The amount of seconds to wait for all clients to load or unload a requested scene /// - [Tooltip("The amount of seconds to wait for all clients to load a requested scene")] + [Tooltip("The amount of seconds to wait for all clients to load or unload a requested scene (only when EnableSceneManagement is enabled)")] public int LoadSceneTimeOut = 120; /// diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index e4837b8353..0a67b37491 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -76,12 +76,13 @@ public class SceneEvent } /// - /// Main class for managing network scenes + /// Main class for managing network scenes when is enabled. + /// Uses the message to communicate between the server and client(s) /// public class NetworkSceneManager { /// - /// The delegate callback definition for scene events + /// The delegate callback definition for scene event notifications /// For more details review over and /// /// @@ -89,7 +90,7 @@ public class NetworkSceneManager /// /// Event that will notify the local client or server of all scene events that take place - /// For more details review over and + /// For more details review over , , and /// public event SceneEventDelegate OnSceneEvent; @@ -127,6 +128,10 @@ public class NetworkSceneManager private const NetworkChannel k_ChannelType = NetworkChannel.Internal; private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.PreUpdate; + /// + /// Constructor + /// + /// internal NetworkSceneManager(NetworkManager networkManager) { m_NetworkManager = networkManager; @@ -163,10 +168,10 @@ internal void SendSceneEventData(ulong[] targetClientIds) } /// - /// Returns the MLAPI scene index from a scene + /// Returns the Netcode scene index from a scene /// /// - /// MLAPI Scene Index + /// Netcode Scene Index internal uint GetNetcodeSceneIndexFromScene(Scene scene) { uint index = 0; @@ -183,10 +188,10 @@ internal uint GetNetcodeSceneIndexFromScene(Scene scene) } /// - /// Returns the scene name from the MLAPI scene index + /// Returns the scene name from the Netcode scene index /// Note: This is not the same as the Build Settings Scenes in Build index /// - /// MLAPI Scene Index + /// Netcode Scene Index /// scene name internal string GetSceneNameFromNetcodeSceneIndex(uint sceneIndex) { @@ -221,10 +226,10 @@ public void AddRuntimeSceneName(string sceneName, uint index) /// /// Validates the new scene event request by the server-side code. - /// This also initializes some commonly shared values as well as switchSceneProgress + /// This also initializes some commonly shared values as well as SceneEventProgress /// /// - /// SceneSwitchProgress (if null it failed) + /// that should have a of otherwise it failed. private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnloading = false) { if (!m_NetworkManager.IsServer) @@ -300,7 +305,7 @@ internal bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgres SceneEventType = sceneEventProgress.SceneEventType, SceneName = sceneEventProgress.SceneName, ClientId = m_NetworkManager.ServerClientId, - LoadSceneMode =sceneEventProgress.LoadSceneMode, + LoadSceneMode = sceneEventProgress.LoadSceneMode, ClientsThatCompleted = sceneEventProgress.DoneClients, ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(), }); @@ -315,7 +320,7 @@ internal bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgres /// When applicable, the is delivered within the via the /// /// scene name to unload - /// + /// ( means it was successful) public SceneEventProgressStatus UnloadScene(string sceneName) { // Make sure the scene is actually loaded @@ -363,6 +368,7 @@ public SceneEventProgressStatus UnloadScene(string sceneName) } /// + /// Client Side: /// SceneManager.UnloadSceneAsync handler for clients /// private void OnClientUnloadScene() @@ -400,6 +406,7 @@ private void OnClientUnloadScene() } /// + /// Server and Client: /// Invoked when the additively loaded scene is unloaded /// private void OnSceneUnloaded() @@ -435,7 +442,7 @@ private void OnSceneUnloaded() /// When applicable, the is delivered within the via the /// /// the name of the scene to be loaded - /// + /// ( means it was successful) public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSceneMode) { var sceneEventProgress = ValidateServerSceneEvent(sceneName); @@ -500,9 +507,10 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc } /// - /// Client Side: handles both forms of scene loading + /// Client Side: + /// Handles both forms of scene loading /// - /// Stream data associated with the event + /// Stream data associated with the event internal void OnClientSceneLoadingEvent(Stream objectStream) { SceneEventData.CopyUnreadFromStream(objectStream); @@ -613,7 +621,8 @@ private void OnSceneLoaded(string sceneName) } /// - /// Server side: on scene loaded callback method invoked by OnSceneLoading only + /// Server side: + /// On scene loaded callback method invoked by OnSceneLoading only /// private void OnServerLoadedScene() { @@ -686,7 +695,8 @@ private void OnServerLoadedScene() } /// - /// Client side: on scene loaded callback method invoked by OnSceneLoading only + /// Client side: + /// On scene loaded callback method invoked by OnSceneLoading only /// private void OnClientLoadedScene() { @@ -794,7 +804,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) /// Note: This can recurse one additional time by the client if the current scene loaded by the client /// is already loaded. /// - /// MLAPI sceneIndex to load + /// Netcode sceneIndex to load private void OnClientBeginSync(uint sceneIndex) { if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) @@ -811,7 +821,7 @@ private void OnClientBeginSync(uint sceneIndex) var loadSceneMode = sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive; // If this is the beginning of the synchronization event, then send client a notification that synchronization has begun - if(sceneIndex == SceneEventData.SceneIndex) + if (sceneIndex == SceneEventData.SceneIndex) { OnSceneEvent?.Invoke(new SceneEvent() { @@ -849,7 +859,7 @@ private void OnClientBeginSync(uint sceneIndex) /// Once a scene is loaded ( or if it was already loaded) this gets called. /// This handles all of the in-scene and dynamically spawned NetworkObject synchronization /// - /// MLAPI scene index that was loaded + /// Netcode scene index that was loaded private void ClientLoadedSynchronization(uint sceneIndex) { var sceneName = GetSceneNameFromNetcodeSceneIndex(sceneIndex); @@ -999,8 +1009,6 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) { case SceneEventData.SceneEventTypes.C2S_LoadComplete: { - Debug.Log($"[{nameof(SceneEventData.SceneEventTypes.C2S_LoadComplete)}] Client Id {clientId} finished loading additive scene."); - // Notify the local server that the client has finished loading a scene OnSceneEvent?.Invoke(new SceneEvent() { @@ -1046,7 +1054,6 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) if (SceneEventData.ClientNeedsReSynchronization()) { - Debug.Log($"Re-Synchronizing client {clientId} for missed destroyed NetworkObjects."); SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_ReSync; SendSceneEventData(new ulong[] { clientId }); @@ -1103,8 +1110,10 @@ public void HandleSceneEvent(ulong clientId, Stream stream) } } - #region NetworkObject Scene Transitioning Related Method - + /// + /// Moves all NetworkObjects that don't have the set to + /// the "Do not destroy on load" scene. + /// private void MoveObjectsToDontDestroyOnLoad() { // Move ALL NetworkObjects to the temp scene @@ -1180,6 +1189,5 @@ private void MoveObjectsToScene(Scene scene) SceneManager.MoveGameObjectToScene(sobj.gameObject, scene); } } - #endregion } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 3d842c6feb..155e9430fe 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -9,8 +9,8 @@ namespace Unity.Netcode { /// - /// Used by to communicate scene event states - /// when scene management is enabled. + /// Used by for messages + /// Note: This is only when is enabled /// public class SceneEventData : IDisposable { @@ -22,15 +22,71 @@ public class SceneEventData : IDisposable /// public enum SceneEventTypes { - S2C_Load, //Server to client load additive scene - S2C_Unload, //Server to client unload additive scene - S2C_Sync, //Server to client late join approval synchronization - S2C_ReSync, //Server to client update of objects that were destroyed during sync - S2C_LoadComplete, //Server to client(s) all clients loaded scene - S2C_UnLoadComplete, //Server to client(s) all clients unloaded scene - C2S_LoadComplete, //Client to server load complete - C2S_UnloadComplete, //Client to server unload complete - C2S_SyncComplete, //Client to server + /// + /// Load a scene + /// Invocation: Server Side + /// Message Flow: Server to client + /// Event Notification: Both server and client are notified a load scene event started + /// + S2C_Load, + /// + /// Unload a scene + /// Invocation: Server Side + /// Message Flow: Server to client + /// Event Notification: Both server and client are notified an unload scene event started + /// + S2C_Unload, + /// + /// Synchronize current game session state for approved clients + /// Invocation: Server Side + /// Message Flow: Server to client + /// Event Notification: Server and Client receives a local notification (server receives the ClientId being synchronized) + /// + S2C_Sync, + /// + /// Game session re-synchronization of NetworkOjects that were destroyed during a event + /// Invocation: Server Side + /// Message Flow: Server to client + /// Event Notification: Both server and client receive a local notification + /// + S2C_ReSync, + /// + /// All clients have finished loading a scene + /// Invocation: Server Side + /// Message Flow: Server to Client + /// Event Notification: Both server and client receive a local notification containing the clients that finished + /// as well as the clients that timed out (if any). + /// + S2C_LoadComplete, + /// + /// All clients have unloaded a scene + /// Invocation: Server Side + /// Message Flow: Server to Client + /// Event Notification: Both server and client receive a local notification containing the clients that finished + /// as well as the clients that timed out (if any). + /// + S2C_UnLoadComplete, + /// + /// A client has finished loading a scene + /// Invocation: Client Side + /// Message Flow: Client to Server + /// Event Notification: Both server and client receive a local notification + /// + C2S_LoadComplete, + /// + /// A client has finished unloading a scene + /// Invocation: Client Side + /// Message Flow: Client to Server + /// Event Notification: Both server and client receive a local notification + /// + C2S_UnloadComplete, + /// + /// A client has finished synchronizing from a event + /// Invocation: Client Side + /// Message Flow: Client to Server + /// Event Notification: Both server and client receive a local notification + /// + C2S_SyncComplete, } internal SceneEventTypes SceneEventType; @@ -130,6 +186,7 @@ internal void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObj } /// + /// Client and Server: /// Determines if the scene event type was intended for the client ( or server ) /// /// true (client should handle this message) false (server should handle this message) @@ -151,6 +208,7 @@ internal bool IsSceneEventClientSide() } /// + /// Server Side: /// Sorts the NetworkObjects to assure proper order of operations for custom Network Prefab handlers /// that implement the INetworkPrefabInstanceHandler interface. /// @@ -176,6 +234,7 @@ private int SortNetworkObjects(NetworkObject first, NetworkObject second) } /// + /// Client and Server Side: /// Serializes data based on the SceneEvent type () /// /// to write the scene event data @@ -253,6 +312,7 @@ internal void OnWrite(NetworkWriter writer) } /// + /// Client and Server Side: /// Deserialize data based on the SceneEvent type. /// /// @@ -527,7 +587,6 @@ internal void ReadSceneEventProgressDone(NetworkReader reader) } } - /// /// Used to store data during an asynchronous scene loading event /// diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs index f44e0716f6..86b8e5951a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventProgress.cs @@ -11,8 +11,8 @@ namespace Unity.Netcode /// Used by to determine if a server invoked scene event has started and stored /// within the /// Note: This was formally known as SwitchSceneProgress which contained the . - /// The is now delivered via the event through - /// the parameter. + /// All s are now delivered by the event handler + /// via the parameter. /// Status Values: /// None - No status /// Started - Success in starting a load or unload scene event @@ -22,10 +22,25 @@ namespace Unity.Netcode /// public enum SceneEventProgressStatus { + /// + /// No scene event progress status can be used to initialize a variable that will be checked over time. + /// None, + /// + /// The scene event was successfully started + /// Started, + /// + /// Returned if you try to unload a scene that was not yet loaded + /// SceneNotLoaded, + /// + /// Returned if you try to start a new scene event before a previous one is finished + /// SceneEventInProgress, + /// + /// Returned if the scene name used with or is invalid + /// InvalidSceneName, } From 98354515fe7f0ff83fc9eb70afe757ce99cd0f66 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 6 Aug 2021 08:21:03 -0500 Subject: [PATCH 059/106] refactor Removing two exceptions that will not be needed. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 0a67b37491..473ff77781 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -589,10 +589,6 @@ private void OnSceneLoaded(string sceneName) { m_ScenesLoaded.Add(sceneName); } - else - { - throw new Exception($"{sceneName} is being loaded twice?!"); - } //Get all NetworkObjects loaded by the scene PopulateScenePlacedObjects(nextScene); @@ -882,10 +878,6 @@ private void ClientLoadedSynchronization(uint sceneIndex) { m_ScenesLoaded.Add(sceneName); } - else - { - throw new Exception($"{sceneName} is being loaded twice?!"); - } // Get all NetworkObjects loaded by the scene (in-scene NetworkObjects) PopulateScenePlacedObjects(nextScene); From c1d299aac76811154880826b2d366e5b7c483d15 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 6 Aug 2021 15:39:29 -0500 Subject: [PATCH 060/106] refactor Added the ability to disable the re-synchronization for future snapshot development purposes. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 473ff77781..4b79114f5c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -81,6 +81,8 @@ public class SceneEvent /// public class NetworkSceneManager { + internal static bool DisableReSynchronization; + /// /// The delegate callback definition for scene event notifications /// For more details review over and @@ -1044,7 +1046,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) ClientId = clientId }); - if (SceneEventData.ClientNeedsReSynchronization()) + if (SceneEventData.ClientNeedsReSynchronization() && !DisableReSynchronization) { SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_ReSync; SendSceneEventData(new ulong[] { clientId }); From adc58d8944458b0ffdd60439f9d115570474aebb Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 6 Aug 2021 18:11:08 -0500 Subject: [PATCH 061/106] refactor Removed debug information from SceneEventData. --- .../Runtime/SceneManagement/SceneEventData.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index 155e9430fe..f77d41a47e 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -257,11 +257,9 @@ internal void OnWrite(NetworkWriter writer) if (m_SceneNetworkObjects.Count() > 0) { - string msg = "Scene Associated NetworkObjects Write:\n"; foreach (var keypair in m_SceneNetworkObjects) { writer.WriteUInt32Packed(keypair.Key); - msg += $"Scene ID [{keypair.Key}] NumNetworkObjects:[{keypair.Value.Count}]\n"; writer.WriteInt32Packed(keypair.Value.Count); var positionStart = writer.GetStream().Position; // Size Place Holder (For offset purposes, needs to not be packed) @@ -280,7 +278,6 @@ internal void OnWrite(NetworkWriter writer) networkObject.SerializeSceneObject(writer, TargetClientId); var noStop = writer.GetStream().Position; totalBytes += (int)(noStop - noStart); - msg += $"Included: {networkObject.name} Bytes: {totalBytes} \n"; } var positionEnd = writer.GetStream().Position; var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); @@ -288,10 +285,7 @@ internal void OnWrite(NetworkWriter writer) // Write the total size written to the stream by NetworkObjects being serialized writer.WriteUInt32(bytesWritten); writer.GetStream().Position = positionEnd; - msg += $"Wrote [{bytesWritten}] bytes of NetworkObject data. Verification: {totalBytes}\n"; } - - Debug.Log(msg); } } From bac7f416ae29624277984c6d2d1eee07bf4afb77 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 7 Aug 2021 09:41:24 -0500 Subject: [PATCH 062/106] style and refactor Minor clean up on some comments. Minor refactoring of property and method accessibility. --- .../SceneManagement/NetworkSceneManager.cs | 36 ++++++++++--------- .../Runtime/SceneManagement/SceneEventData.cs | 7 ++-- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 4b79114f5c..8ca630c645 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -81,8 +81,12 @@ public class SceneEvent /// public class NetworkSceneManager { + // Used to be able to turn re-synchronization off for future snapshot development purposes. internal static bool DisableReSynchronization; + // Used to detect if we are in the middle of a single mode scene transition + private static bool s_IsSceneEventActive = false; + /// /// The delegate callback definition for scene event notifications /// For more details review over and @@ -108,9 +112,6 @@ public class NetworkSceneManager // Used to track which scenes are currently loaded (outside of SceneManager) private List m_ScenesLoaded = new List(); - // Used to detect if we are in the middle of a single mode scene transition - private static bool s_IsSceneEventActive = false; - // 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 // When it is set: Just before starting the asynchronous loading call @@ -145,11 +146,11 @@ internal NetworkSceneManager(NetworkManager networkManager) /// Generic sending of scene event data /// /// array of client identifiers to receive the scene event message - internal void SendSceneEventData(ulong[] targetClientIds) + private void SendSceneEventData(ulong[] targetClientIds) { if (targetClientIds.Length == 0) { - // This would be the server with no clients connected + // This would be the Host/Server with no clients connected // Silently return as there is nothing to be done return; } @@ -174,7 +175,7 @@ internal void SendSceneEventData(ulong[] targetClientIds) /// /// /// Netcode Scene Index - internal uint GetNetcodeSceneIndexFromScene(Scene scene) + private uint GetNetcodeSceneIndexFromScene(Scene scene) { uint index = 0; if (!SceneNameToIndex.TryGetValue(scene.name, out index)) @@ -195,7 +196,7 @@ internal uint GetNetcodeSceneIndexFromScene(Scene scene) /// /// Netcode Scene Index /// scene name - internal string GetSceneNameFromNetcodeSceneIndex(uint sceneIndex) + private string GetSceneNameFromNetcodeSceneIndex(uint sceneIndex) { var sceneName = string.Empty; if (!SceneIndexToString.TryGetValue(sceneIndex, out sceneName)) @@ -284,7 +285,7 @@ private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnl /// /// /// - internal bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress) + private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress) { // Send a message to all clients that all clients are done loading or unloading var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, m_NetworkManager.ConnectedClientsIds, k_NetworkUpdateStage); @@ -318,7 +319,8 @@ internal bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgres } /// - /// Unloads an additively loaded scene + /// Server Side: + /// Unloads an additively loaded scene. If you want to unload a mode loaded scene load another scene. /// When applicable, the is delivered within the via the /// /// scene name to unload @@ -440,7 +442,8 @@ private void OnSceneUnloaded() } /// - /// Server side: Loads the scene name in either additive or single loading mode. + /// Server side: + /// Loads the scene name in either additive or single loading mode. /// When applicable, the is delivered within the via the /// /// the name of the scene to be loaded @@ -513,7 +516,7 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc /// Handles both forms of scene loading /// /// Stream data associated with the event - internal void OnClientSceneLoadingEvent(Stream objectStream) + private void OnClientSceneLoadingEvent(Stream objectStream) { SceneEventData.CopyUnreadFromStream(objectStream); @@ -916,8 +919,8 @@ private void ClientLoadedSynchronization(uint sceneIndex) } /// - /// Client Side: Handles incoming Scene_Event messages - /// It is "understood" that the server is the sender + /// Client Side: + /// Handles incoming Scene_Event messages for clients /// /// data associated with the event private void HandleClientSceneEvent(Stream stream) @@ -993,7 +996,8 @@ private void HandleClientSceneEvent(Stream stream) } /// - /// Server Side: Handles incoming Scene_Event messages + /// Server Side: + /// Handles incoming Scene_Event messages for host or server /// /// client who sent the event /// data associated with the event @@ -1074,7 +1078,7 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) /// /// client who sent the scene event /// data associated with the scene event - public void HandleSceneEvent(ulong clientId, Stream stream) + internal void HandleSceneEvent(ulong clientId, Stream stream) { if (m_NetworkManager != null) { @@ -1138,7 +1142,7 @@ private void MoveObjectsToDontDestroyOnLoad() /// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene. /// Added the ability to choose not to clear the scene placed objects for additive scene loading. /// - internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) + private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) { if (clearScenePlacedObjects) { diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs index f77d41a47e..476b879bf2 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/SceneEventData.cs @@ -267,7 +267,7 @@ internal void OnWrite(NetworkWriter writer) var totalBytes = 0; // Sort NetworkObjects so any NetworkObjects with a PrefabHandler are sorted to be after all other NetworkObjects - // This will assure the INetworkPrefabInstanceHandler instance is registered before we try to spawn the NetworkObjects + // This will assure any INetworkPrefabInstanceHandler instance is registered before we try to spawn the NetworkObjects // on the client side. keypair.Value.Sort(SortNetworkObjects); @@ -393,7 +393,8 @@ internal void OnRead(NetworkReader reader) /// /// Client Side: /// If there happens to be NetworkObjects in the final Event_Sync_Complete message that are no longer spawned, - /// the server will compile a list and send back an Event_ReSync message to the client. + /// the server will compile a list and send back an Event_ReSync message to the client. This is where the + /// client handles any returned values by the server. /// /// internal void ReadClientReSynchronizationData(NetworkReader reader) @@ -492,7 +493,7 @@ internal void CheckClientSynchronizationResults(NetworkReader reader) /// Client Side: /// During the deserialization process of the servers Event_Sync, the client builds a list of /// all NetworkObjectIds that were spawned. Upon responding to the server with the Event_Sync_Complete - /// this list is included for the server to review to determine if the client needs a minor resynchronization + /// this list is included for the server to review over and determine if the client needs a minor resynchronization /// of NetworkObjects that might have been despawned while the client was processing the Event_Sync. /// /// From 6b9b4c05a74362a4976806d3ff409a02008bc4bd Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 9 Aug 2021 13:36:42 -0500 Subject: [PATCH 063/106] fix and style replacing exception message reference to scene switch with scene event. Fixing check for scene not being loaded check in UnloadScene. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 8ca630c645..1336392a9c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -237,7 +237,7 @@ private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnl { if (!m_NetworkManager.IsServer) { - throw new NotServerException("Only server can start a scene switch"); + throw new NotServerException("Only server can start a scene event!"); } if (!m_NetworkManager.NetworkConfig.EnableSceneManagement) @@ -329,7 +329,7 @@ public SceneEventProgressStatus UnloadScene(string sceneName) { // Make sure the scene is actually loaded var sceneToUnload = SceneManager.GetSceneByName(sceneName); - if (sceneToUnload == null) + if (!sceneToUnload.isLoaded) { Debug.LogWarning($"{nameof(UnloadScene)} was called, but the scene {sceneName} is not currently loaded!"); return SceneEventProgressStatus.SceneNotLoaded; From 0db23c324ef43d5044555f38b85557258abc7e64 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 9 Aug 2021 15:37:40 -0500 Subject: [PATCH 064/106] fix Message ordering seemed to have issues with application target frame rate. Checking this and setting it prior to running a test seems to fix the issue with failing on the FixedUpdate side of things. --- .../Assets/Tests/Runtime/MessageOrdering.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 124d31b9c0..36d71e698d 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -12,6 +12,18 @@ public class MessageOrderingTests { private GameObject m_Prefab; + [UnitySetUp] + public IEnumerator SetUp() + { + if(Application.targetFrameRate < 60) + { + Application.targetFrameRate = 60; + } + // Make sure these static values are reset + Support.SpawnRpcDespawn.ClientUpdateCount = 0; + Support.SpawnRpcDespawn.ServerUpdateCount = 0; + yield break; + } [UnityTearDown] public IEnumerator Teardown() @@ -143,7 +155,7 @@ public IEnumerator SpawnRpcDespawn([Values] NetworkUpdateStage testStage) // Wait until all objects have spawned. int expectedCount = Support.SpawnRpcDespawn.ClientUpdateCount + 1; const int maxFrames = 240; - var doubleCheckTime = Time.realtimeSinceStartup + 1.0f; + var doubleCheckTime = Time.realtimeSinceStartup + 10.0f; while (Support.SpawnRpcDespawn.ClientUpdateCount < expectedCount) { if (Time.frameCount > maxFrames) From 79fd5b2519b003c7c2126c2984ef75a5f1e48cc0 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 9 Aug 2021 17:08:36 -0500 Subject: [PATCH 065/106] refactor reverting timeouts to 5 seconds each. --- testproject/Assets/Tests/Runtime/MessageOrdering.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 36d71e698d..84bcbfbf22 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -82,7 +82,7 @@ public IEnumerator SpawnChangeOwnership() // Wait until all objects have spawned. const int expectedNetworkObjects = numClients + 2; // +2 = one for prefab, one for server. const int maxFrames = 240; - var doubleCheckTime = Time.realtimeSinceStartup + 10.0f; + var doubleCheckTime = Time.realtimeSinceStartup + 5.0f; while (Object.FindObjectsOfType().Length != expectedNetworkObjects) { if (Time.frameCount > maxFrames) @@ -155,7 +155,7 @@ public IEnumerator SpawnRpcDespawn([Values] NetworkUpdateStage testStage) // Wait until all objects have spawned. int expectedCount = Support.SpawnRpcDespawn.ClientUpdateCount + 1; const int maxFrames = 240; - var doubleCheckTime = Time.realtimeSinceStartup + 10.0f; + var doubleCheckTime = Time.realtimeSinceStartup + 5.0f; while (Support.SpawnRpcDespawn.ClientUpdateCount < expectedCount) { if (Time.frameCount > maxFrames) From 9f1e05fde6b477df6b2919140921d9b05739e22e Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 10 Aug 2021 14:46:53 -0500 Subject: [PATCH 066/106] refactor Removing the target frame rate check in the SetUp. --- testproject/Assets/Tests/Runtime/MessageOrdering.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 84bcbfbf22..648c8d044a 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -15,10 +15,6 @@ public class MessageOrderingTests [UnitySetUp] public IEnumerator SetUp() { - if(Application.targetFrameRate < 60) - { - Application.targetFrameRate = 60; - } // Make sure these static values are reset Support.SpawnRpcDespawn.ClientUpdateCount = 0; Support.SpawnRpcDespawn.ServerUpdateCount = 0; From d38145bae0ede16a081c63c912b2af0366346336 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 10 Aug 2021 17:16:43 -0500 Subject: [PATCH 067/106] refactor Removing check for NetworkObject being null in NetworkBehaviourUpdater, this is no longer needed. Removing artifact debug output (testing purposes) that was missed. --- .../Runtime/Core/NetworkBehaviourUpdater.cs | 34 +++++-------------- .../SceneManagement/NetworkSceneManager.cs | 1 - 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs index adacfae760..d38209ad0f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviourUpdater.cs @@ -28,14 +28,10 @@ internal void NetworkBehaviourUpdate(NetworkManager networkManager) m_Touched.UnionWith(spawnedObjs); foreach (var sobj in spawnedObjs) { - // Under specific conditions this can become null, so we don't want to access it if it is null - if (sobj != null) + // Sync just the variables for just the objects this client sees + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - // Sync just the variables for just the objects this client sees - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) - { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId); - } + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId); } } } @@ -43,13 +39,9 @@ internal void NetworkBehaviourUpdate(NetworkManager networkManager) // Now, reset all the no-longer-dirty variables foreach (var sobj in m_Touched) { - // Under specific conditions this can become null, so we don't want to access it if it is null - if (sobj != null) + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) - { - sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); - } + sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); } } } @@ -58,26 +50,18 @@ internal void NetworkBehaviourUpdate(NetworkManager networkManager) // when client updates the server, it tells it about all its objects foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList) { - // Under specific conditions this can become null, so we don't want to access it if it is null - if (sobj != null) + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) - { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId); - } + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId); } } // Now, reset all the no-longer-dirty variables foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList) { - // Under specific conditions this can become null, so we don't want to access it if it is null - if (sobj != null) + for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) - { - sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); - } + sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite(); } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index 1336392a9c..4b1c7fb533 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -538,7 +538,6 @@ private void OnClientSceneLoadingEvent(Stream objectStream) { if (currentActiveScene.name != loadedSceneName) { - Debug.Log($"Invoking unload scene event for {loadedSceneName}"); OnSceneEvent?.Invoke(new SceneEvent() { AsyncOperation = SceneManager.UnloadSceneAsync(loadedSceneName), From d3ff2e4c64ad648b9eb8f45afe273567ff321fe3 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 10 Aug 2021 17:57:27 -0500 Subject: [PATCH 068/106] fix Seconds vs frame count Mac failed. --- .../Tests/Runtime/NetworkShowHideTests.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkShowHideTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkShowHideTests.cs index 70c00db0db..c8a983b5fd 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkShowHideTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkShowHideTests.cs @@ -134,7 +134,8 @@ public IEnumerator NetworkShowHideTest() m_NetSpawnedObject2.Spawn(); m_NetSpawnedObject3.Spawn(); - yield return new WaitForSeconds(0.1f); + var waitUntilFrameNumber = Time.frameCount + 10; + yield return new WaitUntil(() => Time.frameCount >= waitUntilFrameNumber); // get the NetworkObject on a client instance yield return RefreshNetworkObjects(); @@ -144,14 +145,17 @@ public IEnumerator NetworkShowHideTest() // hide them on one client Show(false); - yield return new WaitForSeconds(0.1f); + waitUntilFrameNumber = Time.frameCount + 10; + yield return new WaitUntil(() => Time.frameCount >= waitUntilFrameNumber); // verify they got hidden CheckVisible(false); // show them to that client Show(true); - yield return new WaitForSeconds(0.1f); + waitUntilFrameNumber = Time.frameCount + 10; + yield return new WaitUntil(() => Time.frameCount >= waitUntilFrameNumber); + yield return RefreshNetworkObjects(); // verify they become visible From f4778085f474fdcd57966ad475fe3b8901f32dbb Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 10 Aug 2021 19:53:20 -0500 Subject: [PATCH 069/106] style added comment --- .../SceneTransitioningAdditive/NetworkManagerMonitor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs index b4557947ac..42242c3278 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/NetworkManagerMonitor.cs @@ -1,6 +1,10 @@ using UnityEngine; using Unity.Netcode; +/// +/// This can be added to the same GameObject the NetworkManager component is assigned to in order to prevent +/// multiple NetworkManager instances from being instantiated if the same scene is loaded. +/// public class NetworkManagerMonitor : MonoBehaviour { // Start is called before the first frame update From def254305d196105df8ce997a9aff1a007cc51e7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 12 Aug 2021 17:05:46 -0500 Subject: [PATCH 070/106] style removing the TODO and my initials from MTT-860 comments and providing a bit more of an explanation --- .../Runtime/Core/NetworkManager.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 088b5127ae..5359e69f45 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -876,7 +876,8 @@ public void Shutdown() NetworkTickSystem.Tick -= OnNetworkManagerTick; NetworkTickSystem = null; } - // NSS TODO: Remove this once MTT-860 is addressed or before PR + // This is required for handling the potential scenario where multiple NetworkManager instances are created. + // See MTT-860 for more information #if !UNITY_2020_2_OR_NEWER if (IsListening) { @@ -917,7 +918,8 @@ public void Shutdown() BehaviourUpdater = null; } - // NSS TODO: Remove this once MTT-860 is addressed or before PR + // This is required for handling the potential scenario where multiple NetworkManager instances are created. + // See MTT-860 for more information if (IsListening) { //The Transport is set during initialization, thus it is possible for the Transport to be null From 36e89c25228067ee87dd93a8d9e5ff821251baf8 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 12 Aug 2021 17:24:52 -0500 Subject: [PATCH 071/106] style Added or updated some comments for better clarity. --- .../Runtime/SceneManagement/SceneEventProgress.cs | 13 ++++--------- .../Runtime/Spawning/NetworkPrefabHandler.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 5 ++++- .../Tests/Runtime/MultiInstanceHelpers.cs | 2 ++ .../Tests/Runtime/NetworkShowHideTests.cs | 1 + 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs index 86b8e5951a..0f817437f2 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs @@ -8,17 +8,11 @@ namespace Unity.Netcode { /// - /// Used by to determine if a server invoked scene event has started and stored - /// within the + /// Used by to determine if a server invoked scene event has started. + /// The returned status is stored in the property. /// Note: This was formally known as SwitchSceneProgress which contained the . /// All s are now delivered by the event handler /// via the parameter. - /// Status Values: - /// None - No status - /// Started - Success in starting a load or unload scene event - /// SceneNotLoaded - Returned if you attempt to unload a scene that is not loaded - /// SceneEventInProgress - Returned if you attempt to start a new scene event (load or unload) during an existing scene event - /// InvalidSceneName - Returned if the scene name you specified does not exist /// public enum SceneEventProgressStatus { @@ -39,7 +33,8 @@ public enum SceneEventProgressStatus /// SceneEventInProgress, /// - /// Returned if the scene name used with or is invalid + /// Returned if the scene name used with + /// or is invalid /// InvalidSceneName, } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs index 22fc087c27..5539f57e38 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs @@ -40,7 +40,7 @@ public interface INetworkPrefabInstanceHandler /// The most common approach is to make the inactive by calling . /// /// The being destroyed - /// (true) destroy the (false) do not destroy the + /// (true) destroy the parent (false) do not destroy the parent bool Destroy(NetworkObject networkObject); } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 06d9451a93..934bc1faf2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -310,7 +310,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo if (SpawnedObjects.ContainsKey(networkId)) { - Debug.LogWarning($"Trying to spawn NetworkObjectId {networkId} that already exists!"); + Debug.LogWarning($"Trying to spawn {nameof(NetworkObject.NetworkObjectId)} {networkId} that already exists!"); return; } @@ -676,6 +676,9 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec { if (NetworkManager.PrefabHandler.ContainsHandler(networkObject)) { + // If the custom prefab handler returns true then we destroy the parent GameObject + // otherwise the custom prefab handler handles how they want to deal with despawning + // the NetworkObject if(NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObject)) { if (SpawnedObjects.Remove(networkObject.NetworkObjectId)) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs index 544d8bfa9f..7b576726a7 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs @@ -471,6 +471,8 @@ public static IEnumerator WaitForCondition(Func predicate, CoroutineResult while (Time.frameCount - startFrameNumber <= maxFrames && !predicate()) { + // Changed to 2 frames to avoid the scenario where it would take 1+ frames to + // see a value change (i.e. discovered in the NetworkTransformTests) var nextFrameNumber = Time.frameCount + 2; yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs index c8a983b5fd..729ea542ba 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs @@ -134,6 +134,7 @@ public IEnumerator NetworkShowHideTest() m_NetSpawnedObject2.Spawn(); m_NetSpawnedObject3.Spawn(); + // Using frame count as opposed to time to avoid random failures var waitUntilFrameNumber = Time.frameCount + 10; yield return new WaitUntil(() => Time.frameCount >= waitUntilFrameNumber); From 2a8ea86295bd13d56196855ff49baac3922d378c Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 13 Aug 2021 09:25:52 -0500 Subject: [PATCH 072/106] refactor Assigning the TargetClientId for NetworkVariable writing. This could change with the snapshot stuff and the up-and-coming change in read/write permissions, but for the time being making sure this is set only for S2C_Sync events. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 4b1c7fb533..f69c16ef3b 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -744,6 +744,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) } ClientSynchEventData.InitializeForSynch(); + ClientSynchEventData.TargetClientId = ownerClientId; ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Sync; From 0d07468841474fae1bc99b6fab4d7268b6dd8cdd Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 13 Aug 2021 11:35:44 -0500 Subject: [PATCH 073/106] style removing MLAPI from sceneIndex --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index f69c16ef3b..0072cdb669 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -754,16 +754,16 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) { var scene = SceneManager.GetSceneAt(i); - var malpiSceneIndex = GetNetcodeSceneIndexFromScene(scene); + var sceneIndex = GetNetcodeSceneIndexFromScene(scene); - if (malpiSceneIndex == uint.MaxValue) + if (sceneIndex == uint.MaxValue) { continue; } // This would depend upon whether we are additive or note if (activeScene == scene) { - ClientSynchEventData.SceneIndex = malpiSceneIndex; + ClientSynchEventData.SceneIndex = sceneIndex; } // Separate NetworkObjects by scene @@ -779,7 +779,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) { continue; } - ClientSynchEventData.AddNetworkObjectForSynch(malpiSceneIndex, networkObject); + ClientSynchEventData.AddNetworkObjectForSynch(sceneIndex, networkObject); } } From 1c12d7c11d6e4f66588a441494777e23e81007be Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 14:02:08 -0500 Subject: [PATCH 074/106] refactor and feat This includes several updates: We now load all scenes first and then we synchronize all NetworkObjects after all scenes have loaded (during the client synchronization process). This means users no longer have to register any form of dependency with NetworkObjects. This includes the additional modifications to support loading the same additive scene (within scene placed NetworkObjects) multiple times. This did require a bit of refactoring and some improvements in how we organize our InScenePlacedObjects. The SceneTransitioningAdditive manual tests (SceneTransitioningBase1 is root scene) include demonstration of loading/unloading the same additive scene repeatedly. --- .../Runtime/Core/NetworkObject.cs | 21 - .../SceneManagement/NetworkSceneManager.cs | 456 +++-- .../Runtime/SceneManagement/SceneEventData.cs | 212 ++- .../SceneManagement/SceneEventProgress.cs | 7 + .../Runtime/Spawning/NetworkSpawnManager.cs | 8 +- .../NetworkObjectSceneSerializationTests.cs | 7 +- .../Assets/Prefabs/RandomMoverObject.prefab | 282 +++ .../Prefabs/RandomMoverObject.prefab.meta | 7 + .../AdditiveSceneMultiInstance.unity | 260 +++ .../AdditiveSceneMultiInstance.unity.meta | 7 + .../AdditiveSceneToggleHandler.cs | 73 +- .../SceneEventNotificationQueue.cs | 2 +- .../SceneTransitioningBase1.unity | 1624 ++++++++++++++++- .../SwitchSceneHandlerAdditive.cs | 24 +- .../Tests/Manual/Scripts/IndependentMover.cs | 72 + .../Manual/Scripts/IndependentMover.cs.meta | 11 + .../Scripts/NetworkPrefabPoolAdditive.cs | 11 - 17 files changed, 2777 insertions(+), 307 deletions(-) create mode 100644 testproject/Assets/Prefabs/RandomMoverObject.prefab create mode 100644 testproject/Assets/Prefabs/RandomMoverObject.prefab.meta create mode 100644 testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity create mode 100644 testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity.meta create mode 100644 testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs create mode 100644 testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 952e4019bd..5a5de958a7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -144,27 +144,6 @@ internal set /// public bool DestroyWithScene { get; internal set; } - /// - /// Used for late-joining client synchronization purposes. - /// The scene that has dependencies to this NetworkObject - /// If not set then it is ignored. - /// for more information. - /// - public string DependentSceneName { get; internal set; } - - /// - /// For late-joining client synchronization and additive scene(s) purposes - /// This provides the ability to associate a with a scene that is not - /// currently spawned in but may have dependencies within the dependent scene - /// Example: pool generator with custom Network Prefab override handler - /// needs to initialize before this is spawned. - /// - /// scene that has dependencies to the NetworkObject - public void SetSceneAsDependency(string sceneName) - { - DependentSceneName = sceneName; - } - /// /// Delegate type for checking visibility /// diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 0072cdb669..96793e17d7 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -37,6 +37,11 @@ public class SceneEvent /// public string SceneName; + /// + /// When a scene is loaded, the Scene structure is returned. + /// + public Scene Scene; + /// /// Events that always set to the local client identifier /// and only triggered locally: @@ -104,13 +109,22 @@ public class NetworkSceneManager internal readonly Dictionary SceneNameToIndex = new Dictionary(); internal readonly Dictionary SceneIndexToString = new Dictionary(); internal readonly Dictionary SceneEventProgressTracking = new Dictionary(); - internal readonly Dictionary ScenePlacedObjects = new Dictionary(); + + + /// + /// ScenePlacedObjects (i.e. In-Scene Placed NetworkObjects) are now stored by GlobalObjectIdHash + /// + internal readonly Dictionary> ScenePlacedObjects = new Dictionary>(); + + internal Scene SceneBeingSynchronized; // Used for observed object synchronization private readonly List m_ObservedObjects = new List(); - // Used to track which scenes are currently loaded (outside of SceneManager) - private List m_ScenesLoaded = new List(); + // Used to track which scenes are currently loaded + private Dictionary> m_ScenesLoaded = new Dictionary>(); + + internal Dictionary ServerSceneHandleToClientSceneHandle = new Dictionary(); // 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 @@ -129,7 +143,7 @@ public class NetworkSceneManager private const MessageQueueContainer.MessageType k_MessageType = MessageQueueContainer.MessageType.SceneEvent; private const NetworkChannel k_ChannelType = NetworkChannel.Internal; - private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.PreUpdate; + private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.EarlyUpdate; /// /// Constructor @@ -142,6 +156,111 @@ internal NetworkSceneManager(NetworkManager networkManager) ClientSynchEventData = new SceneEventData(networkManager); } + /// + /// 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 + /// which scene with the same name has not yet been loaded. + /// In order to support loading the same additive scene within in-scene placed NetworkObjects, + /// we must do this to be able to soft synchronize the "right version" of the NetworkObject. + /// + /// + /// + internal Scene GetAndAddNewlyLoadedSceneByName(string sceneName) + { + + for (int i = 0; i < SceneManager.sceneCount; i++) + { + var sceneLoaded = SceneManager.GetSceneAt(i); + if (sceneLoaded.name == sceneName) + { + if (m_ScenesLoaded.ContainsKey(sceneName)) + { + if (!m_ScenesLoaded[sceneName].ContainsKey(sceneLoaded.handle)) + { + m_ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); + return sceneLoaded; + } + } + else + { + // On a new entry we add the entry and scene then we are done. + m_ScenesLoaded.Add(sceneName, new Dictionary()); + m_ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); + return sceneLoaded; + } + } + } + + throw new Exception("Failed to find scene that was loaded!"); + } + + /// + /// Client Side Only: + /// This takes a server scene handle that is written by the server before the scene relative + /// NetworkObject is serialized and converts the server scene handle to a local client handle + /// so it can set the appropriate SceneBeingSynchronized. + /// Note: This is now part of the soft synchronization process and is needed for the scenario + /// where a user loads the same scene additively that has an in-scene placed NetworkObject + /// which means each scene relative in-scene placed NetworkObject will have the identical GlobalObjectIdHash + /// value. Scene handles are used to distinguish between in-scene placed NetworkObjects under this situation. + /// + /// + internal void SetTheSceneBeingSynchronized(int serverSceneHandle) + { + var clientSceneHandle = serverSceneHandle; + if (m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(serverSceneHandle)) + { + clientSceneHandle = m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle[serverSceneHandle]; + // If we were already set, then ignore + if (SceneBeingSynchronized.IsValid() && SceneBeingSynchronized.isLoaded && SceneBeingSynchronized.handle == clientSceneHandle) + { + return; + } + + // Find and set the scene currently being synchronized + SceneBeingSynchronized = new Scene(); + foreach (var keyValuePairBySceneName in m_ScenesLoaded) + { + if (keyValuePairBySceneName.Value.ContainsKey(clientSceneHandle)) + { + SceneBeingSynchronized = keyValuePairBySceneName.Value[clientSceneHandle]; + } + } + + if (!SceneBeingSynchronized.IsValid() || !SceneBeingSynchronized.isLoaded) + { + throw new Exception($"[{nameof(NetworkSceneManager)}- {nameof(m_ScenesLoaded)}] Could not find the appropriate scene to set as being synchronized!"); + } + } + else + { + // This should never happen, but in the event it does... + throw new Exception($"[{nameof(SceneEventData)}- Scene Handle Mismatch] {nameof(serverSceneHandle)} could not be found in {nameof(ServerSceneHandleToClientSceneHandle)}!"); + } + } + + /// + /// During soft synchronization of in-scene placed NetworkObjects, this is now used by NetworkSpawnManager.CreateLocalNetworkObject + /// + /// + /// + internal NetworkObject GetSceneRelativeInSceneNetworkObject(uint globalObjectIdHash) + { + if (ScenePlacedObjects.ContainsKey(globalObjectIdHash)) + { + if (ScenePlacedObjects[globalObjectIdHash].ContainsKey(SceneBeingSynchronized.handle)) + { + var inScenePlacedNetworkObject = ScenePlacedObjects[globalObjectIdHash][SceneBeingSynchronized.handle]; + + // We can only have 1 duplicated globalObjectIdHash per scene instance, so remove it once it has been returned + ScenePlacedObjects[globalObjectIdHash].Remove(SceneBeingSynchronized.handle); + + return inScenePlacedNetworkObject; + } + } + return null; + } + /// /// Generic sending of scene event data /// @@ -325,13 +444,12 @@ private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress /// /// scene name to unload /// ( means it was successful) - public SceneEventProgressStatus UnloadScene(string sceneName) + public SceneEventProgressStatus UnloadScene(Scene scene) { - // Make sure the scene is actually loaded - var sceneToUnload = SceneManager.GetSceneByName(sceneName); - if (!sceneToUnload.isLoaded) + var sceneName = scene.name; + if (!scene.isLoaded) { - Debug.LogWarning($"{nameof(UnloadScene)} was called, but the scene {sceneName} is not currently loaded!"); + Debug.LogWarning($"{nameof(UnloadScene)} was called, but the scene {scene.name} is not currently loaded!"); return SceneEventProgressStatus.SceneNotLoaded; } @@ -341,22 +459,34 @@ public SceneEventProgressStatus UnloadScene(string sceneName) return sceneEventProgress.Status; } + if (!m_ScenesLoaded.ContainsKey(sceneName) || !m_ScenesLoaded[sceneName].ContainsKey(scene.handle)) + { + Debug.LogError($"{nameof(UnloadScene)} internal error! {sceneName} with handle {scene.handle} is not within the internal scenes loaded dictionary!"); + return SceneEventProgressStatus.InternalNetcodeError; + } + SceneEventData.SceneEventGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; + SceneEventData.SceneHandle = m_ScenesLoaded[sceneName][scene.handle].handle; + + // This will be the message we send to everyone when this scene event sceneEventProgress is complete sceneEventProgress.SceneEventType = SceneEventData.SceneEventTypes.S2C_UnLoadComplete; // Sends the unload scene notification SendSceneEventData(m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray()); - AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(sceneToUnload); - sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; - sceneEventProgress.SetSceneLoadOperation(sceneUnload); - if (m_ScenesLoaded.Contains(sceneName)) + m_ScenesLoaded[sceneName].Remove(scene.handle); + + if (m_ScenesLoaded[sceneName].Count == 0) { m_ScenesLoaded.Remove(sceneName); } + AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(scene); + sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; + sceneEventProgress.SetSceneLoadOperation(sceneUnload); + // Notify local server that a scene is going to be unloaded OnSceneEvent?.Invoke(new SceneEvent() { @@ -389,24 +519,50 @@ private void OnClientUnloadScene() } s_IsSceneEventActive = true; - var sceneUnload = SceneManager.UnloadSceneAsync(sceneName); - - if (m_ScenesLoaded.Contains(sceneName)) + if (m_ScenesLoaded.ContainsKey(sceneName)) { - m_ScenesLoaded.Remove(sceneName); - } + if(!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) + { + throw new Exception("No server to scene handle exist!"); + } + var sceneHandle = ServerSceneHandleToClientSceneHandle[SceneEventData.SceneHandle]; + if (m_ScenesLoaded[sceneName].ContainsKey(sceneHandle)) + { + var sceneUnload = SceneManager.UnloadSceneAsync(m_ScenesLoaded[sceneName][sceneHandle]); - sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); + sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); - // Notify the local client that a scene is going to be unloaded - OnSceneEvent?.Invoke(new SceneEvent() + m_ScenesLoaded[sceneName].Remove(sceneHandle); + + // Remove our server to scene handle lookup + ServerSceneHandleToClientSceneHandle.Remove(SceneEventData.SceneHandle); + if (m_ScenesLoaded[sceneName].Count == 0) + { + m_ScenesLoaded.Remove(sceneName); + } + + // Notify the local client that a scene is going to be unloaded + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = sceneUnload, + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId // Server sent this message to the client, but client is executing it + }); + } + else + { + // Error scene handle not found! + Debug.LogError("Server Scene Handle Not Found!"); + } + } + else { - AsyncOperation = sceneUnload, - SceneEventType = SceneEventData.SceneEventType, - LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = sceneName, - ClientId = m_NetworkManager.LocalClientId // Server sent this message to the client, but client is executing it - }); + + // Error scene not loaded! + Debug.LogError("Server Scene Handle Not Loaded!"); + } } /// @@ -441,6 +597,36 @@ private void OnSceneUnloaded() s_IsSceneEventActive = false; } + /// + /// Clears all scenes when loading in single mode + /// Since we assume a single mode loaded scene will be considered the "currently active scene", + /// we only unload any additively loaded scenes. + /// + internal void UnloadAdditivelyLoadedScenes() + { + // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). + var currentActiveScene = SceneManager.GetActiveScene(); + foreach (var keyRootSceneEntry in m_ScenesLoaded) + { + foreach (var keyHandleEntry in keyRootSceneEntry.Value) + { + if (currentActiveScene.name != keyHandleEntry.Value.name) + { + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = SceneManager.UnloadSceneAsync(keyHandleEntry.Value), + SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload, + LoadSceneMode = LoadSceneMode.Additive, + SceneName = keyHandleEntry.Value.name, + ClientId = m_NetworkManager.ServerClientId + }); + } + } + } + // clear out our scenes loaded list + m_ScenesLoaded.Clear(); + } + /// /// Server side: /// Loads the scene name in either additive or single loading mode. @@ -456,8 +642,11 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc return sceneEventProgress.Status; } + // This will be the message we send to everyone when this scene event sceneEventProgress is complete sceneEventProgress.SceneEventType = SceneEventData.SceneEventTypes.S2C_LoadComplete; sceneEventProgress.LoadSceneMode = loadSceneMode; + + // Now set up the current scene event SceneEventData.SceneEventGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Load; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; @@ -470,26 +659,9 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc // Preserve the objects that should not be destroyed during the scene event MoveObjectsToDontDestroyOnLoad(); - } - var currentActiveScene = SceneManager.GetActiveScene(); - // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). - if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) - { - foreach (var additiveSceneName in m_ScenesLoaded) - { - if (currentActiveScene.name != additiveSceneName) - { - OnSceneEvent?.Invoke(new SceneEvent() - { - SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload, - LoadSceneMode = LoadSceneMode.Additive, - SceneName = additiveSceneName, - ClientId = m_NetworkManager.ServerClientId - }); - } - } - m_ScenesLoaded.Clear(); + // Now Unload all currently additively loaded scenes + UnloadAdditivelyLoadedScenes(); } // Now start loading the scene @@ -530,28 +702,13 @@ private void OnClientSceneLoadingEvent(Stream objectStream) return; } - // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). - var currentActiveScene = SceneManager.GetActiveScene(); if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - foreach (var loadedSceneName in m_ScenesLoaded) - { - if (currentActiveScene.name != loadedSceneName) - { - OnSceneEvent?.Invoke(new SceneEvent() - { - AsyncOperation = SceneManager.UnloadSceneAsync(loadedSceneName), - SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload, - LoadSceneMode = LoadSceneMode.Additive, - SceneName = loadedSceneName, - ClientId = m_NetworkManager.LocalClientId - }); - } - } - m_ScenesLoaded.Clear(); - // Move ALL NetworkObjects to the temp scene MoveObjectsToDontDestroyOnLoad(); + + // Now Unload all currently additively loaded scenes + UnloadAdditivelyLoadedScenes(); } // The Condition: While a scene is asynchronously loaded in single loading scene mode, if any new NetworkObjects are spawned @@ -577,21 +734,22 @@ private void OnClientSceneLoadingEvent(Stream objectStream) }); } + /// /// Client and Server: /// Generic on scene loaded callback method to be called upon a scene loading /// private void OnSceneLoaded(string sceneName) { - var nextScene = SceneManager.GetSceneByName(sceneName); - if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) + var nextScene = GetAndAddNewlyLoadedSceneByName(sceneName); + if (!nextScene.isLoaded || !nextScene.IsValid()) { - SceneManager.SetActiveScene(nextScene); + throw new Exception($"Failed to find valid scene internal Unity.Netcode for {nameof(GameObject)}s error!"); } - if (!m_ScenesLoaded.Contains(sceneName)) + if (SceneEventData.LoadSceneMode == LoadSceneMode.Single) { - m_ScenesLoaded.Add(sceneName); + SceneManager.SetActiveScene(nextScene); } //Get all NetworkObjects loaded by the scene @@ -612,11 +770,22 @@ private void OnSceneLoaded(string sceneName) if (m_NetworkManager.IsServer) { - OnServerLoadedScene(); + OnServerLoadedScene(nextScene); } else { - OnClientLoadedScene(); + // For the client, we make a server scene handle to client scene handle look up table + if(!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) + { + ServerSceneHandleToClientSceneHandle.Add(SceneEventData.SceneHandle, nextScene.handle); + } + else + { + // If the exact same handle exists then there are problems with using handles + throw new Exception($"Server Scene Handle ({SceneEventData.SceneHandle}) already exist! Happened during scene load of {nextScene.name} with Client Handle ({nextScene.handle})"); + } + + OnClientLoadedScene(nextScene); } } @@ -624,17 +793,23 @@ private void OnSceneLoaded(string sceneName) /// Server side: /// On scene loaded callback method invoked by OnSceneLoading only /// - private void OnServerLoadedScene() + private void OnServerLoadedScene(Scene scene) { // Register in-scene placed NetworkObjects with the netcode - foreach (var keyValuePair in ScenePlacedObjects) + foreach (var keyValuePairByGlobalObjectIdHash in ScenePlacedObjects) { - if (!keyValuePair.Value.IsPlayerObject) + foreach (var keyValuePairBySceneHandle in keyValuePairByGlobalObjectIdHash.Value) { - m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePair.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, null, false, true); + if (!keyValuePairBySceneHandle.Value.IsPlayerObject) + { + m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, null, false, true); + } } } + // Set the server's scene's handle so the client can build a look up table + SceneEventData.SceneHandle = scene.handle; + // Send all clients the scene load event for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++) { @@ -644,11 +819,14 @@ private void OnServerLoadedScene() uint sceneObjectsToSpawn = 0; - foreach (var keyValuePair in ScenePlacedObjects) + foreach (var keyValuePairByGlobalObjectIdHash in ScenePlacedObjects) { - if (keyValuePair.Value.Observers.Contains(clientId)) + foreach (var keyValuePairBySceneHandle in keyValuePairByGlobalObjectIdHash.Value) { - sceneObjectsToSpawn++; + if (keyValuePairBySceneHandle.Value.Observers.Contains(clientId)) + { + sceneObjectsToSpawn++; + } } } @@ -660,11 +838,18 @@ private void OnServerLoadedScene() SceneEventData.OnWrite(nonNullContext.NetworkWriter); // Write number of scene objects to spawn nonNullContext.NetworkWriter.WriteUInt32Packed(sceneObjectsToSpawn); - foreach (var keyValuePair in ScenePlacedObjects) + + foreach (var keyValuePairByGlobalObjectIdHash in ScenePlacedObjects) { - if (keyValuePair.Value.Observers.Contains(clientId)) + foreach (var keyValuePairBySceneHandle in keyValuePairByGlobalObjectIdHash.Value) { - keyValuePair.Value.SerializeSceneObject(nonNullContext.NetworkWriter, clientId); + if (keyValuePairBySceneHandle.Value.Observers.Contains(clientId)) + { + // Write our server relative scene handle for the NetworkObject being serialized + nonNullContext.NetworkWriter.WriteInt32Packed(keyValuePairBySceneHandle.Key); + // Serialize the NetworkObject + keyValuePairBySceneHandle.Value.SerializeSceneObject(nonNullContext.NetworkWriter, clientId); + } } } } @@ -684,7 +869,8 @@ private void OnServerLoadedScene() SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = SceneEventData.LoadSceneMode, SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), - ClientId = m_NetworkManager.ServerClientId + ClientId = m_NetworkManager.ServerClientId, + Scene = scene, }); //Second, set the server as having loaded for the associated SceneEventProgress @@ -698,7 +884,7 @@ private void OnServerLoadedScene() /// Client side: /// On scene loaded callback method invoked by OnSceneLoading only /// - private void OnClientLoadedScene() + private void OnClientLoadedScene(Scene scene) { using (var reader = PooledNetworkReader.Get(SceneEventData.InternalBuffer)) { @@ -706,6 +892,10 @@ private void OnClientLoadedScene() for (int i = 0; i < newObjectsCount; i++) { + // Set our relative scene to the NetworkObject + SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); + + // Deserialize the NetworkObject NetworkObject.DeserializeSceneObject(SceneEventData.InternalBuffer as NetworkBuffer, reader, m_NetworkManager); } } @@ -720,7 +910,8 @@ private void OnClientLoadedScene() SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = SceneEventData.LoadSceneMode, SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), - ClientId = m_NetworkManager.LocalClientId + ClientId = m_NetworkManager.LocalClientId, + Scene = scene, }); } @@ -728,6 +919,8 @@ private void OnClientLoadedScene() /// Server Side: /// This is used for players that have just had their connection approved and will assure they are synchronized /// properly if they are late joining + /// Note: We write out all of the scenes to be loaded first and then all of the NetworkObjects that need to be + /// synchronized. /// /// newly joined client identifier internal void SynchronizeNetworkObjects(ulong ownerClientId) @@ -760,29 +953,19 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) { continue; } - // This would depend upon whether we are additive or note + // This would depend upon whether we are additive or not + // If we are the base scene, then we set the root scene index; if (activeScene == scene) { ClientSynchEventData.SceneIndex = sceneIndex; + ClientSynchEventData.SceneHandle = scene.handle; } - // Separate NetworkObjects by scene - foreach (var networkObject in m_ObservedObjects) - { - // If the current scene we are matching NetworkObjects to does not match and this NetworkObject has no dependent scene, then continue. - if (networkObject.gameObject.scene != scene && (networkObject.DependentSceneName == null || networkObject.DependentSceneName == string.Empty)) - { - continue; - } - else // If this NetworkObject has a dependent scene and the current scene is not the dependent scene, then continue - if (networkObject.DependentSceneName != null && networkObject.DependentSceneName != string.Empty && networkObject.DependentSceneName != scene.name) - { - continue; - } - ClientSynchEventData.AddNetworkObjectForSynch(sceneIndex, networkObject); - } + ClientSynchEventData.AddSceneToSynchronize(sceneIndex, scene.handle); } + ClientSynchEventData.AddSpawnedNetworkObjects(); + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, new ulong[] { ownerClientId }, k_NetworkUpdateStage); if (context != null) { @@ -805,9 +988,10 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) /// Note: This can recurse one additional time by the client if the current scene loaded by the client /// is already loaded. /// - /// Netcode sceneIndex to load - private void OnClientBeginSync(uint sceneIndex) + private void OnClientBeginSync() { + var sceneIndex = SceneEventData.GetNextSceneSynchronizationIndex(); + var sceneHandle = SceneEventData.GetNextSceneSynchronizationHandle(); if (!SceneIndexToString.TryGetValue(sceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) @@ -829,6 +1013,9 @@ private void OnClientBeginSync(uint sceneIndex) SceneEventType = SceneEventData.SceneEventTypes.S2C_Sync, ClientId = m_NetworkManager.LocalClientId, }); + + // Clear the in-scene placed NetworkObjects when we load the first scene in our synchronization process + ScenePlacedObjects.Clear(); } // Check to see if the client already has loaded the scene to be loaded @@ -847,12 +1034,12 @@ private void OnClientBeginSync(uint sceneIndex) ClientId = m_NetworkManager.LocalClientId, }); - sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex); + sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneIndex, sceneHandle); } else { // If so, then pass through - ClientLoadedSynchronization(sceneIndex); + ClientLoadedSynchronization(sceneIndex, sceneHandle); } } @@ -861,14 +1048,14 @@ private void OnClientBeginSync(uint sceneIndex) /// This handles all of the in-scene and dynamically spawned NetworkObject synchronization /// /// Netcode scene index that was loaded - private void ClientLoadedSynchronization(uint sceneIndex) + private void ClientLoadedSynchronization(uint sceneIndex, int sceneHandle) { var sceneName = GetSceneNameFromNetcodeSceneIndex(sceneIndex); - var nextScene = SceneManager.GetSceneByName(sceneName); - if (nextScene == null) + var nextScene = GetAndAddNewlyLoadedSceneByName(sceneName); + + if (!nextScene.isLoaded || !nextScene.IsValid()) { - Debug.LogError($"Client was trying to load {sceneIndex} which does not appear to be a valid registered scene index!"); - return; + throw new Exception($"Failed to find valid scene internal Unity.Netcode for {nameof(GameObject)}s error!"); } var loadSceneMode = (sceneIndex == SceneEventData.SceneIndex ? SceneEventData.LoadSceneMode : LoadSceneMode.Additive); @@ -879,16 +1066,18 @@ private void ClientLoadedSynchronization(uint sceneIndex) SceneManager.SetActiveScene(nextScene); } - if (!m_ScenesLoaded.Contains(sceneName)) + if (!ServerSceneHandleToClientSceneHandle.ContainsKey(sceneHandle)) { - m_ScenesLoaded.Add(sceneName); + ServerSceneHandleToClientSceneHandle.Add(sceneHandle, nextScene.handle); + } + else + { + // If the exact same handle exists then there are problems with using handles + throw new Exception($"Server Scene Handle ({SceneEventData.SceneHandle}) already exist! Happened during scene load of {nextScene.name} with Client Handle ({nextScene.handle})"); } - // Get all NetworkObjects loaded by the scene (in-scene NetworkObjects) - PopulateScenePlacedObjects(nextScene); - - // Synchronize the NetworkObjects for this scene - SceneEventData.SynchronizeSceneNetworkObjects(sceneIndex, m_NetworkManager); + // Apply all in-scene placed NetworkObjects loaded by the scene + PopulateScenePlacedObjects(nextScene, false); // Send notification back to server that we finished loading this scene ClientSynchEventData.LoadSceneMode = loadSceneMode; @@ -911,6 +1100,7 @@ private void ClientLoadedSynchronization(uint sceneIndex) SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete, LoadSceneMode = loadSceneMode, SceneName = sceneName, + Scene = nextScene, ClientId = m_NetworkManager.LocalClientId, }); @@ -941,10 +1131,13 @@ private void HandleClientSceneEvent(Stream stream) { if (!SceneEventData.IsDoneWithSynchronization()) { - OnClientBeginSync(SceneEventData.GetNextSceneSynchronizationIndex()); + OnClientBeginSync(); } else { + // Synchronize the NetworkObjects for this scene + SceneEventData.SynchronizeSceneNetworkObjects(m_NetworkManager); + SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_SyncComplete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); @@ -1141,6 +1334,10 @@ private void MoveObjectsToDontDestroyOnLoad() /// -- A new scene has been loaded /// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene. /// Added the ability to choose not to clear the scene placed objects for additive scene loading. + /// We organize our ScenePlacedObjects by: + /// [GlobalObjectIdHash][SceneHandle][NetworkObject] + /// Using the local scene relative Scene.handle as a sub-key to the root dictionary allows us to + /// distinguish between duplicate in-scene placed NetworkObjects /// private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true) { @@ -1152,16 +1349,29 @@ private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePl var networkObjects = UnityEngine.Object.FindObjectsOfType(); // Just add every NetworkObject found that isn't already in the list - // If any "non-in-scene placed NetworkObjects" are added to this list it shouldn't matter - // The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash + // With additive scenes, we can have multiple in-scene placed NetworkObjects with the same GlobalObjectIdHash value + // Client Side Synchronization: So, we add them on a FIFO basis and for each scene loaded foreach (var networkObjectInstance in networkObjects) { - if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) + // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) + if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy && + networkObjectInstance.gameObject.scene.handle == sceneToFilterBy.handle) { - // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) - if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy) + if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) + { + ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, new Dictionary()); + } + + if(!ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].ContainsKey(networkObjectInstance.gameObject.scene.handle)) + { + ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].Add(networkObjectInstance.gameObject.scene.handle, networkObjectInstance); + } + else { - ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, networkObjectInstance); + var exitingEntryName = ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle] != null ? + ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle].name : "Null Entry"; + throw new Exception($"{networkObjectInstance.name} tried to registered with {nameof(ScenePlacedObjects)} which already contains " + + $"the same {nameof(NetworkObject.GlobalObjectIdHash)} value {networkObjectInstance.GlobalObjectIdHash} for {exitingEntryName}!"); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 476b879bf2..9eb15f7e43 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -94,6 +94,11 @@ public enum SceneEventTypes internal Guid SceneEventGuid; internal uint SceneIndex; + internal int SceneHandle; + + /// Only used for S2C_Synch scene events, this assures permissions when writing + /// NetworkVariable information. If that process changes, then we need to update + /// this internal ulong TargetClientId; private Dictionary> m_SceneNetworkObjects; @@ -129,6 +134,30 @@ public enum SceneEventTypes internal List ClientsCompleted; internal List ClientsTimedOut; + internal Queue ScenesToSynchronize; + internal Queue SceneHandlesToSynchronize; + + + /// + /// Server Side: + /// Add a scene and its handle to the list of scenes the client should load before synchronizing + /// Since scene handles are not the same per instance, the client builds a server scene handle to + /// client scene handle lookup table. + /// Why include the scene handle? In order to support loading of the same additive scene more than once + /// we must distinguish which scene we are talking about when the server tells the client to unload a scene. + /// The server will always communicate its local relative scene's handle and the client will determine its + /// local relative handle from the table being built. + /// Look for usage to see where + /// entries are being added to or removed from the table + /// + /// + /// + internal void AddSceneToSynchronize(uint sceneIndex, int sceneHandle) + { + ScenesToSynchronize.Enqueue(sceneIndex); + SceneHandlesToSynchronize.Enqueue((uint)sceneHandle); + } + /// /// Client Side: /// Gets the next scene index to be loaded for approval and/or late joining @@ -136,11 +165,17 @@ public enum SceneEventTypes /// internal uint GetNextSceneSynchronizationIndex() { - if (m_SceneNetworkObjectDataOffsets.ContainsKey(SceneIndex)) - { - return SceneIndex; - } - return m_SceneNetworkObjectDataOffsets.First().Key; + return ScenesToSynchronize.Dequeue(); + } + + /// + /// Client Side: + /// Gets the next scene handle to be loaded for approval and/or late joining + /// + /// + internal int GetNextSceneSynchronizationHandle() + { + return (int)SceneHandlesToSynchronize.Dequeue(); } /// @@ -150,7 +185,16 @@ internal uint GetNextSceneSynchronizationIndex() /// true/false internal bool IsDoneWithSynchronization() { - return (m_SceneNetworkObjectDataOffsets.Count == 0); + if (ScenesToSynchronize.Count == 0 && SceneHandlesToSynchronize.Count == 0) + { + return true; + } + else if (ScenesToSynchronize.Count != SceneHandlesToSynchronize.Count ) + { + // This should never happen, but in the event it does... + throw new Exception($"[{nameof(SceneEventData)}-Internal Mismatch Error] {nameof(ScenesToSynchronize)} count != {nameof(SceneHandlesToSynchronize)} count!"); + } + return false; } /// @@ -167,6 +211,30 @@ internal void InitializeForSynch() { m_SceneNetworkObjects.Clear(); } + + if (ScenesToSynchronize == null) + { + ScenesToSynchronize = new Queue(); + } + else + { + ScenesToSynchronize.Clear(); + } + + if (SceneHandlesToSynchronize == null) + { + SceneHandlesToSynchronize = new Queue(); + } + else + { + SceneHandlesToSynchronize.Clear(); + } + } + + internal void AddSpawnedNetworkObjects() + { + m_NetworkObjectsSync = m_NetworkManager.SpawnManager.SpawnedObjectsList.ToList(); + m_NetworkObjectsSync.Sort(SortNetworkObjects); } /// @@ -209,8 +277,8 @@ internal bool IsSceneEventClientSide() /// /// Server Side: - /// Sorts the NetworkObjects to assure proper order of operations for custom Network Prefab handlers - /// that implement the INetworkPrefabInstanceHandler interface. + /// Sorts the NetworkObjects to assure proper instantiation order of operations for + /// registered INetworkPrefabInstanceHandler implementations /// /// /// @@ -240,53 +308,56 @@ private int SortNetworkObjects(NetworkObject first, NetworkObject second) /// to write the scene event data internal void OnWrite(NetworkWriter writer) { + // Write the scene event type writer.WriteByte((byte)SceneEventType); + // Write the scene loading mode writer.WriteByte((byte)LoadSceneMode); + // Write the scene event progress Guid if (SceneEventType != SceneEventTypes.S2C_Sync) { writer.WriteByteArray(SceneEventGuid.ToByteArray()); } + // Write the scene index and handle writer.WriteUInt32Packed(SceneIndex); + writer.WriteInt32Packed(SceneHandle); if (SceneEventType == SceneEventTypes.S2C_Sync) { - writer.WriteInt32Packed(m_SceneNetworkObjects.Count()); + // Write the scenes we want to load, in the order we want to load them + writer.WriteUIntArrayPacked(ScenesToSynchronize.ToArray()); + writer.WriteUIntArrayPacked(SceneHandlesToSynchronize.ToArray()); - if (m_SceneNetworkObjects.Count() > 0) - { - foreach (var keypair in m_SceneNetworkObjects) - { - writer.WriteUInt32Packed(keypair.Key); - writer.WriteInt32Packed(keypair.Value.Count); - var positionStart = writer.GetStream().Position; - // Size Place Holder (For offset purposes, needs to not be packed) - writer.WriteUInt32(0); - var totalBytes = 0; - - // Sort NetworkObjects so any NetworkObjects with a PrefabHandler are sorted to be after all other NetworkObjects - // This will assure any INetworkPrefabInstanceHandler instance is registered before we try to spawn the NetworkObjects - // on the client side. - keypair.Value.Sort(SortNetworkObjects); - - foreach (var networkObject in keypair.Value) - { - var noStart = writer.GetStream().Position; + // Store our current position in the stream to come back and say how much data we have written + var positionStart = writer.GetStream().Position; - networkObject.SerializeSceneObject(writer, TargetClientId); - var noStop = writer.GetStream().Position; - totalBytes += (int)(noStop - noStart); - } - var positionEnd = writer.GetStream().Position; - var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); - writer.GetStream().Position = positionStart; - // Write the total size written to the stream by NetworkObjects being serialized - writer.WriteUInt32(bytesWritten); - writer.GetStream().Position = positionEnd; - } + // Size Place Holder -- Start + // !!NOTE!!: Since this is a placeholder to be set after we know how much we have written, + // for stream offset purposes this MUST not be a packed value! + writer.WriteUInt32(0); + var totalBytes = 0; + + // Write the number of NetworkObjects we are serializing + writer.WriteInt32Packed(m_NetworkObjectsSync.Count()); + + foreach (var networkObject in m_NetworkObjectsSync) + { + var noStart = writer.GetStream().Position; + writer.WriteInt32Packed(networkObject.gameObject.scene.handle); + networkObject.SerializeSceneObject(writer, TargetClientId); + var noStop = writer.GetStream().Position; + totalBytes += (int)(noStop - noStart); } + + // Size Place Holder -- End + var positionEnd = writer.GetStream().Position; + var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); + writer.GetStream().Position = positionStart; + // Write the total size written to the stream by NetworkObjects being serialized + writer.WriteUInt32(bytesWritten); + writer.GetStream().Position = positionEnd; } if (SceneEventType == SceneEventTypes.C2S_SyncComplete) @@ -340,38 +411,23 @@ internal void OnRead(NetworkReader reader) } SceneIndex = reader.ReadUInt32Packed(); + SceneHandle = reader.ReadInt32Packed(); if (SceneEventType == SceneEventTypes.S2C_Sync) { m_NetworkObjectsSync.Clear(); - var keyPairCount = reader.ReadInt32Packed(); - - if (m_SceneNetworkObjectDataOffsets == null) - { - m_SceneNetworkObjectDataOffsets = new Dictionary(); - } - if (keyPairCount > 0) - { - m_SceneNetworkObjectDataOffsets.Clear(); + ScenesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); + SceneHandlesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); - InternalBuffer.Position = 0; + var sizeToCopy = reader.ReadUInt32(); - using (var writer = PooledNetworkWriter.Get(InternalBuffer)) - { - for (int i = 0; i < keyPairCount; i++) - { - var key = reader.ReadUInt32Packed(); - var count = reader.ReadInt32Packed(); - // how many bytes to read for this scene set - var bytesToRead = (ulong)reader.ReadUInt32(); - // We store off the current position of the stream as it pertains to the scene relative NetworkObjects - m_SceneNetworkObjectDataOffsets.Add(key, InternalBuffer.Position); - writer.WriteInt32Packed(count); - writer.ReadAndWrite(reader, (long)bytesToRead); - } - } + using (var writer = PooledNetworkWriter.Get(InternalBuffer)) + { + writer.ReadAndWrite(reader, (long)sizeToCopy); } + + InternalBuffer.Position = 0; } if (SceneEventType == SceneEventTypes.C2S_SyncComplete) @@ -386,7 +442,7 @@ internal void OnRead(NetworkReader reader) if (SceneEventType == SceneEventTypes.S2C_LoadComplete || SceneEventType == SceneEventTypes.S2C_UnLoadComplete) { - ReadSceneEventProgressDone(reader); + ReadSceneEventProgressDone(reader); } } @@ -515,30 +571,26 @@ internal void WriteClientSynchronizationResults(NetworkWriter writer) /// /// /// - internal void SynchronizeSceneNetworkObjects(uint sceneId, NetworkManager networkManager) + internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { - if (m_SceneNetworkObjectDataOffsets.ContainsKey(sceneId)) + using (var reader = PooledNetworkReader.Get(InternalBuffer)) { - // Point to the appropriate offset - InternalBuffer.Position = m_SceneNetworkObjectDataOffsets[sceneId]; + // Process all NetworkObjects for this scene + var newObjectsCount = reader.ReadInt32Packed(); - using (var reader = PooledNetworkReader.Get(InternalBuffer)) + for (int i = 0; i < newObjectsCount; i++) { - // Process all NetworkObjects for this scene - var newObjectsCount = reader.ReadInt32Packed(); + /// We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is + /// currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject + /// from the list of populated + m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); - for (int i = 0; i < newObjectsCount; i++) + var spawnedNetworkObject = NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); + if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) { - var spawnedNetworkObject = NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); - if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) - { - m_NetworkObjectsSync.Add(spawnedNetworkObject); - } + m_NetworkObjectsSync.Add(spawnedNetworkObject); } } - - // Remove each entry after it is processed so we know when we are done - m_SceneNetworkObjectDataOffsets.Remove(sceneId); } } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs index 0f817437f2..d8db7d3476 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs @@ -37,6 +37,13 @@ public enum SceneEventProgressStatus /// or is invalid /// InvalidSceneName, + + /// + /// This is used for internal error notifications. + /// If you receive this event then it is most likely due to a bug. + /// If you receive this event repeatedly, then please open a GitHub issue with steps to replicate + /// + InternalNetcodeError, } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 934bc1faf2..0795257fb6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -267,7 +267,9 @@ internal NetworkObject CreateLocalNetworkObject(bool isSceneObject, uint globalO } else { - if (!NetworkManager.SceneManager.ScenePlacedObjects.TryGetValue(globalObjectIdHash, out NetworkObject networkObject)) + var networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(globalObjectIdHash); + + if(networkObject == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { @@ -276,10 +278,6 @@ internal NetworkObject CreateLocalNetworkObject(bool isSceneObject, uint globalO return null; } - else - { - NetworkManager.SceneManager.ScenePlacedObjects.Remove(globalObjectIdHash); - } if (parentNetworkObject != null) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index d584379747..159b8dba8d 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using UnityEngine; +using UnityEngine.SceneManagement; using NUnit.Framework; namespace Unity.Netcode.RuntimeTests @@ -81,8 +82,12 @@ public void NetworkObjectSceneSerializationFailure() // Serialize the valid NetworkObject networkObject.SerializeSceneObject(writer, 0); + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.ContainsKey(networkObject.GlobalObjectIdHash)) + { + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(networkObject.GlobalObjectIdHash, new Dictionary()); + } // Add this valid NetworkObject into the ScenePlacedObjects list - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(networkObject.GlobalObjectIdHash, networkObject); + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects[networkObject.GlobalObjectIdHash].Add(SceneManager.GetActiveScene().handle,networkObject); } } diff --git a/testproject/Assets/Prefabs/RandomMoverObject.prefab b/testproject/Assets/Prefabs/RandomMoverObject.prefab new file mode 100644 index 0000000000..852d8d0059 --- /dev/null +++ b/testproject/Assets/Prefabs/RandomMoverObject.prefab @@ -0,0 +1,282 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &771575417923360811 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 771575417923360822} + - component: {fileID: -5591000292386890817} + - component: {fileID: 771575417923360817} + - component: {fileID: 771575417923360819} + - component: {fileID: 5277211521825898157} + - component: {fileID: 771575417923360831} + - component: {fileID: 4600632750638426092} + - component: {fileID: 1932194183178713890} + m_Layer: 9 + m_Name: RandomMoverObject + m_TagString: GenericObject + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &771575417923360822 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2591044912546013445} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &-5591000292386890817 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 951099334 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 0 +--- !u!33 &771575417923360817 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &771575417923360819 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 5d9678afcf333564dab383a5db660ae3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!136 &5277211521825898157 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 1 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!54 &771575417923360831 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + serializedVersion: 2 + m_Mass: 7 + m_Drag: 7 + m_AngularDrag: 0.05 + m_UseGravity: 0 + m_IsKinematic: 0 + m_Interpolate: 1 + m_Constraints: 112 + m_CollisionDetection: 0 +--- !u!114 &4600632750638426092 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} + m_Name: + m_EditorClassIdentifier: + Authority: 2 + Channel: 0 + InLocalSpace: 0 + SyncPositionX: 1 + SyncPositionY: 1 + SyncPositionZ: 1 + SyncRotAngleX: 1 + SyncRotAngleY: 1 + SyncRotAngleZ: 1 + SyncScaleX: 1 + SyncScaleY: 1 + SyncScaleZ: 1 + FixedSendsPerSecond: 5 +--- !u!114 &1932194183178713890 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 771575417923360811} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c552ef0ccf6ff8148a9c9606e7d3fc15, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &2058396567867412356 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2591044912546013445} + - component: {fileID: 2507955376722285842} + - component: {fileID: 1442227766690505295} + - component: {fileID: 7257348192915777211} + m_Layer: 9 + m_Name: ObjectLabel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2591044912546013445 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058396567867412356} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 1.2525, z: 0} + m_LocalScale: {x: 0.5, y: 0.75, z: 0.5} + m_Children: [] + m_Father: {fileID: 771575417923360822} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &2507955376722285842 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058396567867412356} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!102 &1442227766690505295 +TextMesh: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058396567867412356} + m_Text: No State + m_OffsetZ: 0 + m_CharacterSize: 1 + m_LineSpacing: 1 + m_Anchor: 7 + m_Alignment: 0 + m_TabSize: 4 + m_FontSize: 24 + m_FontStyle: 0 + m_RichText: 1 + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_Color: + serializedVersion: 2 + rgba: 4294967295 +--- !u!114 &7257348192915777211 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2058396567867412356} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7b85e388ccb24094d97ef2c46a7ef0d2, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/testproject/Assets/Prefabs/RandomMoverObject.prefab.meta b/testproject/Assets/Prefabs/RandomMoverObject.prefab.meta new file mode 100644 index 0000000000..317210dbbf --- /dev/null +++ b/testproject/Assets/Prefabs/RandomMoverObject.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ea906834639fa3f4ba65c95db6181d6b +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity new file mode 100644 index 0000000000..5e2a4dcbf1 --- /dev/null +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity @@ -0,0 +1,260 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 903034822} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!850595691 &903034822 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1001 &1247056486 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -5591000292386890817, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: GlobalObjectIdHash + value: 1456498493 + objectReference: {fileID: 0} + - target: {fileID: 771575417923360811, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_Name + value: RandomMoverObject + objectReference: {fileID: 0} + - target: {fileID: 771575417923360822, guid: ea906834639fa3f4ba65c95db6181d6b, + type: 3} + propertyPath: m_RootOrder + value: 0 + 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: 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} diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity.meta b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity.meta new file mode 100644 index 0000000000..414a6202d7 --- /dev/null +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0ae94f636016d3b40bfbecad57d99553 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index 89421f6489..b4f0cd04cd 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -1,5 +1,6 @@ using System.Collections; using UnityEngine; +using UnityEngine.SceneManagement; using UnityEngine.UI; #if UNITY_EDITOR using UnityEditor; @@ -20,7 +21,7 @@ public class AdditiveSceneToggleHandler : NetworkBehaviour [SerializeField] private string m_SceneToLoad; - + private Scene m_SceneLoaded; #if UNITY_EDITOR [SerializeField] @@ -84,6 +85,29 @@ private IEnumerator CheckForVisibility() yield return null; } + private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) + { + if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) + { + if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.C2S_LoadComplete) + { + if (sceneEvent.ClientId == NetworkManager.Singleton.ServerClientId && !m_SceneLoaded.IsValid() && sceneEvent.Scene.IsValid()) + { + m_SceneLoaded = sceneEvent.Scene; + m_WaitForSceneLoadOrUnload = false; + } + } + else if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.C2S_UnloadComplete) + { + if (sceneEvent.ClientId == NetworkManager.Singleton.ServerClientId && !m_SceneLoaded.isLoaded) + { + m_SceneLoaded = new Scene(); + m_WaitForSceneLoadOrUnload = false; + } + } + } + } + private IEnumerator DelayedActivate() { yield return new WaitForSeconds(0.5f); @@ -96,7 +120,7 @@ private IEnumerator DelayedActivate() public void OnToggle() { - if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening && NetworkManager.Singleton.IsServer) { if (m_ToggleObject) { @@ -106,25 +130,60 @@ public void OnToggle() } } + private bool m_WaitForSceneLoadOrUnload; + private IEnumerator SceneEventCoroutine(bool isLoading) { var sceneEventProgressStatus = SceneEventProgressStatus.None; - - while (sceneEventProgressStatus != SceneEventProgressStatus.Started) + var continueCheck = true; + NetworkManager.Singleton.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; + while (continueCheck && sceneEventProgressStatus != SceneEventProgressStatus.Started) { if (isLoading) { - sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad,UnityEngine.SceneManagement.LoadSceneMode.Additive); + sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.LoadScene(m_SceneToLoad, LoadSceneMode.Additive); } else { - sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToLoad); + sceneEventProgressStatus = NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneLoaded); } - if (sceneEventProgressStatus == SceneEventProgressStatus.SceneEventInProgress) + if (sceneEventProgressStatus == SceneEventProgressStatus.SceneEventInProgress) { yield return new WaitForSeconds(0.25f); } + else + { + switch (sceneEventProgressStatus) + { + case SceneEventProgressStatus.Started: + { + continueCheck = false; + break; + } + case SceneEventProgressStatus.InternalNetcodeError: + case SceneEventProgressStatus.InvalidSceneName: + case SceneEventProgressStatus.SceneNotLoaded: + { + continueCheck = false; + break; + } + } + } } + m_WaitForSceneLoadOrUnload = true; + var timeOutAfter = Time.realtimeSinceStartup + 10.0f; + while (m_WaitForSceneLoadOrUnload) + { + if(timeOutAfter < Time.realtimeSinceStartup) + { + Debug.LogWarning("Timed out waiting for scene to load or unload!"); + m_WaitForSceneLoadOrUnload = false; + } + yield return new WaitForSeconds(0.5f); + } + + + NetworkManager.Singleton.SceneManager.OnSceneEvent -= SceneManager_OnSceneEvent; m_ToggleObject.isOn = isLoading; m_ToggleObject.enabled = true; yield return null; diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs index 93ca815768..b867688e85 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs @@ -44,7 +44,7 @@ private void Start() /// private void OnSceneEvent(SceneEvent sceneEvent) { - var sceneEventMsg = $"[{sceneEvent.ClientId} | {sceneEvent.SceneEventType} | {sceneEvent.SceneName}"; + var sceneEventMsg = $"({NetworkManager.Singleton.LocalClientId})-[{sceneEvent.ClientId} | {sceneEvent.SceneEventType} | {sceneEvent.SceneName}"; if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.S2C_Load || sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.C2S_LoadComplete) { sceneEventMsg += $" | { sceneEvent.LoadSceneMode}"; diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity index 6d5bc925b7..54c12bae18 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -123,6 +123,58 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &19371289 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 19371290} + - component: {fileID: 19371291} + m_Layer: 5 + m_Name: ToggleAddIMScene (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &19371290 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19371289} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1351730454} + m_Father: {fileID: 290861172} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -104} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &19371291 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19371289} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ActivateOnLoad: 0 + m_SceneToLoad: AdditiveSceneMultiInstance + m_SceneAsset: {fileID: 102900000, guid: 0ae94f636016d3b40bfbecad57d99553, type: 3} --- !u!1 &34066664 GameObject: m_ObjectHideFlags: 0 @@ -526,6 +578,213 @@ Transform: m_Father: {fileID: 1332123092} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &59926368 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 59926369} + - component: {fileID: 59926370} + m_Layer: 5 + m_Name: ToggleAddIMScene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &59926369 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 59926368} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1651938367} + m_Father: {fileID: 290861172} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -27} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &59926370 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 59926368} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ActivateOnLoad: 0 + m_SceneToLoad: AdditiveSceneMultiInstance + m_SceneAsset: {fileID: 102900000, guid: 0ae94f636016d3b40bfbecad57d99553, type: 3} +--- !u!1 &125866602 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 125866603} + - component: {fileID: 125866605} + - component: {fileID: 125866604} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &125866603 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 125866602} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 615497064} + m_Father: {fileID: 1351730454} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &125866604 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 125866602} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &125866605 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 125866602} + m_CullTransparentMesh: 1 +--- !u!1 &163541781 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 163541782} + - component: {fileID: 163541784} + - component: {fileID: 163541783} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &163541782 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 163541781} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1640896166} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &163541783 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 163541781} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: AddScene Dup - 2 +--- !u!222 &163541784 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 163541781} + m_CullTransparentMesh: 1 --- !u!1 &167044830 GameObject: m_ObjectHideFlags: 0 @@ -725,6 +984,10 @@ RectTransform: - {fileID: 1383741138} - {fileID: 884557066} - {fileID: 291820796} + - {fileID: 59926369} + - {fileID: 1008611499} + - {fileID: 1187680250} + - {fileID: 19371290} m_Father: {fileID: 0} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -1079,7 +1342,7 @@ MonoBehaviour: m_BoolArgument: 0 m_CallState: 2 m_IsOn: 0 ---- !u!1 &562991978 +--- !u!1 &418148060 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1087,75 +1350,226 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 562991979} - - component: {fileID: 562991981} - - component: {fileID: 562991980} + - component: {fileID: 418148061} + - component: {fileID: 418148063} + - component: {fileID: 418148062} m_Layer: 5 - m_Name: BoxGeneratorCount + m_Name: Background m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &562991979 +--- !u!224 &418148061 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 562991978} + m_GameObject: {fileID: 418148060} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 2058276876} - m_RootOrder: 3 + m_Children: + - {fileID: 432733929} + m_Father: {fileID: 1651938367} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 3, y: 20} - m_SizeDelta: {x: 160, y: 30} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &562991980 +--- !u!114 &418148062 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 562991978} + m_GameObject: {fileID: 418148060} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.9811321, g: 0.9811321, b: 0.9811321, a: 1} + m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_FontData: - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 18 - m_FontStyle: 1 - m_BestFit: 0 - m_MinSize: 1 - m_MaxSize: 40 - m_Alignment: 4 - m_AlignByGeometry: 0 - m_RichText: 1 - m_HorizontalOverflow: 0 - m_VerticalOverflow: 0 - m_LineSpacing: 1 - m_Text: 0 ---- !u!222 &562991981 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &418148063 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 418148060} + m_CullTransparentMesh: 1 +--- !u!1 &432733928 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 432733929} + - component: {fileID: 432733931} + - component: {fileID: 432733930} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &432733929 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 432733928} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 418148061} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &432733930 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 432733928} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &432733931 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 432733928} + m_CullTransparentMesh: 1 +--- !u!1 &562991978 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 562991979} + - component: {fileID: 562991981} + - component: {fileID: 562991980} + m_Layer: 5 + m_Name: BoxGeneratorCount + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &562991979 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2058276876} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 3, y: 20} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &562991980 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562991978} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.9811321, g: 0.9811321, b: 0.9811321, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 0 +--- !u!222 &562991981 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 562991978} m_CullTransparentMesh: 1 --- !u!1 &575203307 @@ -1318,7 +1732,232 @@ CanvasRenderer: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 599972120} + m_GameObject: {fileID: 599972120} + m_CullTransparentMesh: 1 +--- !u!1 &615497063 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 615497064} + - component: {fileID: 615497066} + - component: {fileID: 615497065} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &615497064 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 615497063} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 125866603} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &615497065 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 615497063} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &615497066 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 615497063} + m_CullTransparentMesh: 1 +--- !u!1 &833301794 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 833301795} + - component: {fileID: 833301797} + - component: {fileID: 833301796} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &833301795 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 833301794} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1290928583} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &833301796 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 833301794} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &833301797 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 833301794} + m_CullTransparentMesh: 1 +--- !u!1 &865202801 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 865202802} + - component: {fileID: 865202804} + - component: {fileID: 865202803} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &865202802 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 865202801} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1398648428} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &865202803 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 865202801} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &865202804 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 865202801} m_CullTransparentMesh: 1 --- !u!1 &884557065 GameObject: @@ -1433,6 +2072,137 @@ LightingSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &906714043 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 906714044} + - component: {fileID: 906714046} + - component: {fileID: 906714045} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &906714044 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 906714043} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2019086800} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &906714045 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 906714043} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: AddScene Dup - 3 +--- !u!222 &906714046 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 906714043} + m_CullTransparentMesh: 1 +--- !u!1 &1008611498 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1008611499} + - component: {fileID: 1008611500} + m_Layer: 5 + m_Name: ToggleAddIMScene (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1008611499 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1008611498} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1640896166} + m_Father: {fileID: 290861172} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -54} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1008611500 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1008611498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ActivateOnLoad: 0 + m_SceneToLoad: AdditiveSceneMultiInstance + m_SceneAsset: {fileID: 102900000, guid: 0ae94f636016d3b40bfbecad57d99553, type: 3} --- !u!1 &1024114717 GameObject: m_ObjectHideFlags: 0 @@ -1483,6 +2253,7 @@ MonoBehaviour: - SceneTransitioningBase2 - AdditiveScene3 - AdditiveScene4 + - AdditiveSceneMultiInstance RegisteredSceneAssets: - {fileID: 102900000, guid: 780f96a61e8ac8e41b638ae8ec3a3236, type: 3} - {fileID: 102900000, guid: 9e437cc704801bc47add735d743985f5, type: 3} @@ -1490,6 +2261,7 @@ MonoBehaviour: - {fileID: 102900000, guid: c6a3d883c8253ee43bca4f2b03797d7b, type: 3} - {fileID: 102900000, guid: 7da3dd618f5b5a34db1f6d3c9511e221, type: 3} - {fileID: 102900000, guid: dc7e17c86f5ca81478043be306027c13, type: 3} + - {fileID: 102900000, guid: 0ae94f636016d3b40bfbecad57d99553, type: 3} AllowRuntimeSceneChanges: 0 PlayerPrefab: {fileID: 4079352819444256614, guid: c16f03336b6104576a565ef79ad643c0, type: 3} @@ -1560,6 +2332,8 @@ MonoBehaviour: LoadSceneTimeOut: 120 MessageBufferTimeout: 20 EnableNetworkLogs: 1 + UseSnapshotDelta: 0 + UseSnapshotSpawn: 0 --- !u!114 &1024114719 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1682,22 +2456,229 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &1113539281 +--- !u!114 &1113539281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1113539278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 1983031731 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!1 &1187680249 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1187680250} + - component: {fileID: 1187680251} + m_Layer: 5 + m_Name: ToggleAddIMScene (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1187680250 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1187680249} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2019086800} + m_Father: {fileID: 290861172} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -79} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1187680251 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1187680249} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 475de064003ff104fb88b1fbccd0f417, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ActivateOnLoad: 0 + m_SceneToLoad: AdditiveSceneMultiInstance + m_SceneAsset: {fileID: 102900000, guid: 0ae94f636016d3b40bfbecad57d99553, type: 3} +--- !u!1 &1210784441 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1210784442} + - component: {fileID: 1210784444} + - component: {fileID: 1210784443} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1210784442 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1210784441} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1651938367} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1210784443 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1210784441} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: AddScene Dup - 1 +--- !u!222 &1210784444 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1210784441} + m_CullTransparentMesh: 1 +--- !u!1 &1290928582 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1290928583} + - component: {fileID: 1290928585} + - component: {fileID: 1290928584} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1290928583 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290928582} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 833301795} + m_Father: {fileID: 1640896166} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1290928584 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1113539278} + m_GameObject: {fileID: 1290928582} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: - GlobalObjectIdHash: 1983031731 - AlwaysReplicateAsRoot: 0 - DontDestroyWithOwner: 0 - AutoObjectParentSync: 1 + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1290928585 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290928582} + m_CullTransparentMesh: 1 --- !u!1 &1332123091 GameObject: m_ObjectHideFlags: 0 @@ -1829,6 +2810,85 @@ Transform: m_Father: {fileID: 1332123092} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1336892118 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1336892119} + - component: {fileID: 1336892121} + - component: {fileID: 1336892120} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1336892119 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336892118} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1351730454} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1336892120 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336892118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 1 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: AddScene Dup - 4 +--- !u!222 &1336892121 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1336892118} + m_CullTransparentMesh: 1 --- !u!1 &1347823141 GameObject: m_ObjectHideFlags: 0 @@ -1962,6 +3022,105 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1347823141} m_CullTransparentMesh: 1 +--- !u!1 &1351730453 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1351730454} + - component: {fileID: 1351730455} + m_Layer: 5 + m_Name: AdditiveSceneMultiInstance + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1351730454 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1351730453} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 125866603} + - {fileID: 1336892119} + m_Father: {fileID: 19371290} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 210} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1351730455 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1351730453} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 125866604} + toggleTransition: 1 + graphic: {fileID: 615497065} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 19371291} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 --- !u!1 &1383741137 GameObject: m_ObjectHideFlags: 0 @@ -2077,6 +3236,82 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: -20, y: 0} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1398648427 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1398648428} + - component: {fileID: 1398648430} + - component: {fileID: 1398648429} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1398648428 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1398648427} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 865202802} + m_Father: {fileID: 2019086800} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1398648429 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1398648427} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1398648430 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1398648427} + m_CullTransparentMesh: 1 --- !u!1 &1429502878 GameObject: m_ObjectHideFlags: 0 @@ -2514,6 +3749,204 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1638885887} m_CullTransparentMesh: 1 +--- !u!1 &1640896165 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1640896166} + - component: {fileID: 1640896167} + m_Layer: 5 + m_Name: AdditiveSceneMultiInstance + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1640896166 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1640896165} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1290928583} + - {fileID: 163541782} + m_Father: {fileID: 1008611499} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 210} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1640896167 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1640896165} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1290928584} + toggleTransition: 1 + graphic: {fileID: 833301796} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1008611500} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 +--- !u!1 &1651938366 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1651938367} + - component: {fileID: 1651938368} + m_Layer: 5 + m_Name: AdditiveSceneMultiInstance + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1651938367 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1651938366} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 418148061} + - {fileID: 1210784442} + m_Father: {fileID: 59926369} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 210} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1651938368 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1651938366} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 418148062} + toggleTransition: 1 + graphic: {fileID: 432733930} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 59926370} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 --- !u!1 &1689223597 GameObject: m_ObjectHideFlags: 0 @@ -3067,6 +4500,105 @@ RectTransform: m_AnchoredPosition: {x: -5, y: 0} m_SizeDelta: {x: -20, y: 0} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &2019086799 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2019086800} + - component: {fileID: 2019086801} + m_Layer: 5 + m_Name: AdditiveSceneMultiInstance + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2019086800 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2019086799} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1398648428} + - {fileID: 906714044} + m_Father: {fileID: 1187680250} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 448, y: 210} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2019086801 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2019086799} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1398648429} + toggleTransition: 1 + graphic: {fileID: 865202803} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1187680251} + m_TargetAssemblyTypeName: TestProject.ManualTests.AdditiveSceneToggleHandler, + Assembly-CSharp + m_MethodName: OnToggle + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_IsOn: 0 --- !u!1 &2021718438 GameObject: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index c0c4b7c183..9c4812c37f 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -115,18 +115,18 @@ public void OnSwitchScene() } } } - else - { - if (NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]) == SceneEventProgressStatus.Started) - { - m_CurrentSceneIndex--; - if (m_CurrentSceneIndex < 0) - { - m_IsReversing = false; - m_CurrentSceneIndex = 0; - } - } - } + //else + //{ + // if (NetworkManager.Singleton.SceneManager.UnloadScene(m_SceneToSwitchTo[m_CurrentSceneIndex]) == SceneEventProgressStatus.Started) + // { + // m_CurrentSceneIndex--; + // if (m_CurrentSceneIndex < 0) + // { + // m_IsReversing = false; + // m_CurrentSceneIndex = 0; + // } + // } + //} } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs new file mode 100644 index 0000000000..fcafde9cb3 --- /dev/null +++ b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs @@ -0,0 +1,72 @@ +using UnityEngine; +using Unity.Netcode; + +namespace TestProject.ManualTests +{ + /// + /// Used with GenericObjects to randomly move them around + /// + public class IndependentMover : NetworkBehaviour + { + private Vector3 m_Direction; + private Rigidbody m_Rigidbody; + + + public override void OnNetworkSpawn() + { + Debug.Log($"{nameof(IndependentMover)} NID: {NetworkObjectId}"); + m_Rigidbody = GetComponent(); + if (NetworkObject != null && m_Rigidbody != null) + { + m_Rigidbody.isKinematic = !NetworkObject.IsOwner; + if (!m_Rigidbody.isKinematic) + { + ChangeDirection(true, true); + } + } + } + + private void FixedUpdate() + { + if (IsServer && IsOwner) + { + Move(4); + } + } + + public void Move(int speed) + { + if(m_Rigidbody == null) + { + m_Rigidbody = GetComponent(); + } + if (m_Rigidbody != null) + { + m_Rigidbody.MovePosition(transform.position + m_Direction * (speed * Time.fixedDeltaTime)); + } + } + + private void OnCollisionStay(Collision collision) + { + if (collision.gameObject.CompareTag("Floor") || collision.gameObject.CompareTag("GenericObject")) + { + return; + } + Vector3 collisionPoint = collision.collider.ClosestPoint(transform.position); + bool moveRight = collisionPoint.x < transform.position.x; + bool moveDown = collisionPoint.z > transform.position.z; + ChangeDirection(moveRight, moveDown); + } + + private void ChangeDirection(bool moveRight, bool moveDown) + { + float ang = Random.Range(0, 2 * Mathf.PI); + + m_Direction.x = Mathf.Cos(ang); + m_Direction.y = 0.0f; + ang = Random.Range(0, 2 * Mathf.PI); + m_Direction.z = Mathf.Sin(ang); + m_Direction.Normalize(); + } + } +} diff --git a/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta new file mode 100644 index 0000000000..37294f1a0e --- /dev/null +++ b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c552ef0ccf6ff8148a9c9606e7d3fc15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index e375975020..593786f839 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -330,17 +330,6 @@ private GameObject AddNewInstance() // about setting the NetworkObject's scene dependency. SceneManager.MoveGameObjectToScene(obj, gameObject.scene); } - else // Otherwise, instantiate in the currently active scene - { - // If your spawn generator is not in the target active scene, then to properly synchronize your NetworkObjects - // for late joining players you **must** set the scene that the NetworkObject depends on - // (i.e. NetworkObjet pool with custom Network Prefab Handler) - if (gameObject.scene != SceneManager.GetActiveScene()) - { - var networkObject = obj.GetComponent(); - networkObject.SetSceneAsDependency(gameObject.scene.name); - } - } obj.SetActive(false); From 7fd53e62462b86a5badb7f524d1f11835a6cbe8e Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 15:25:15 -0500 Subject: [PATCH 075/106] Fix Lots of fixes with the new requirements. We will need to come up with a better way to instantiate NetworkObjects for unit tests, but this includes all of the fixes required to get all current unit tests working again. --- .../Runtime/Core/NetworkObject.cs | 1 + .../Runtime/SceneManagement/NetworkSceneManager.cs | 13 +++++++++++++ .../Tests/Runtime/BaseMultiInstanceTest.cs | 5 +++-- .../Tests/Runtime/MultiInstanceHelpers.cs | 1 + .../NetworkObjectSceneSerializationTests.cs | 9 +++++++++ .../Tests/Runtime/NetworkPrefabHandlerTests.cs | 12 +++++++++++- .../Tests/Runtime/NetworkShowHideTests.cs | 2 +- .../NetworkTransform/NetworkTransformStateTests.cs | 3 ++- testproject/Assets/Resources.meta | 8 ++++++++ 9 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 testproject/Assets/Resources.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 5a5de958a7..8d57d17793 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -18,6 +18,7 @@ public sealed class NetworkObject : MonoBehaviour [SerializeField] internal uint GlobalObjectIdHash; + #if UNITY_EDITOR // HEAD: DO NOT USE! TEST ONLY TEMP IMPL, WILL BE REMOVED internal uint TempGlobalObjectIdHashOverride = 0; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 96793e17d7..b09af6f060 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -92,6 +92,8 @@ public class NetworkSceneManager // Used to detect if we are in the middle of a single mode scene transition private static bool s_IsSceneEventActive = false; + internal static bool IsTesting; + /// /// The delegate callback definition for scene event notifications /// For more details review over and @@ -207,6 +209,17 @@ internal Scene GetAndAddNewlyLoadedSceneByName(string sceneName) /// internal void SetTheSceneBeingSynchronized(int serverSceneHandle) { + if (IsTesting) + { + // If we were already set, then ignore + if (SceneBeingSynchronized.IsValid() && SceneBeingSynchronized.isLoaded) + { + return; + } + SceneBeingSynchronized = SceneManager.GetActiveScene(); + return; + } + var clientSceneHandle = serverSceneHandle; if (m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(serverSceneHandle)) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs index de17e9e94e..fb8c1e3f5d 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs @@ -13,6 +13,8 @@ public abstract class BaseMultiInstanceTest protected NetworkManager m_ServerNetworkManager; protected NetworkManager[] m_ClientNetworkManagers; + internal static uint DefaultPayerGlobalObjectIdHashValue = 7777777; + protected abstract int NbClients { get; } [UnitySetUp] @@ -66,7 +68,6 @@ public IEnumerator StartSomeClientsAndServerWithPlayers(bool useHost, int nbClie // Create playerPrefab m_PlayerPrefab = new GameObject("Player"); NetworkObject networkObject = m_PlayerPrefab.AddComponent(); - /* * Normally we would only allow player prefabs to be set to a prefab. Not runtime created objects. * In order to prevent having a Resource folder full of a TON of prefabs that we have to maintain, @@ -75,7 +76,7 @@ public IEnumerator StartSomeClientsAndServerWithPlayers(bool useHost, int nbClie * at runtime without it being treated as a SceneObject or causing other conflicts with the Netcode. */ // Make it a prefab - MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject); + MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject, DefaultPayerGlobalObjectIdHashValue); updatePlayerPrefab(m_PlayerPrefab); // update player prefab with whatever is needed before players are spawned diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs index 7b576726a7..e765a184ff 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/MultiInstanceHelpers.cs @@ -204,6 +204,7 @@ public class CoroutineResultWrapper public T Result; } + /// /// Normally we would only allow player prefabs to be set to a prefab. Not runtime created objects. /// In order to prevent having a Resource folder full of a TON of prefabs that we have to maintain, diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index 159b8dba8d..149da0d91c 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -7,6 +7,7 @@ namespace Unity.Netcode.RuntimeTests { public class NetworkObjectSceneSerializationTests { + /// /// The purpose behind this test is to assure that in-scene NetworkObjects /// that are serialized into a single stream (approval or switch scene this happens) @@ -52,6 +53,7 @@ public void NetworkObjectSceneSerializationFailure() invalidNetworkObjects.Add(gameObject); + writer.WriteInt32Packed(networkObject.gameObject.scene.handle); // Serialize the invalid NetworkObject networkObject.SerializeSceneObject(writer, 0); @@ -79,6 +81,7 @@ public void NetworkObjectSceneSerializationFailure() networkObjectsToTest.Add(gameObject); + writer.WriteInt32Packed(networkObject.gameObject.scene.handle); // Serialize the valid NetworkObject networkObject.SerializeSceneObject(writer, 0); @@ -113,6 +116,9 @@ public void NetworkObjectSceneSerializationFailure() invalidNetworkObjectCount++; } + + NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); + var deserializedNetworkObject = NetworkObject.DeserializeSceneObject(pooledBuffer, reader, NetworkManagerHelper.NetworkManagerObject); if (deserializedNetworkObject != null) { @@ -150,15 +156,18 @@ public void NetworkObjectSceneSerializationFailure() [SetUp] public void Setup() { + NetworkSceneManager.IsTesting = true; // Create, instantiate, and host NetworkManagerHelper.StartNetworkManager(out NetworkManager networkManager, NetworkManagerHelper.NetworkManagerOperatingMode.None); networkManager.NetworkConfig.EnableSceneManagement = true; networkManager.StartHost(); + } [TearDown] public void TearDown() { + NetworkSceneManager.IsTesting = false; // Stop, shutdown, and destroy NetworkManagerHelper.ShutdownNetworkManager(); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs index b1e3da891b..1b2751b629 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Collections; using System.Collections.Generic; using UnityEngine; using NUnit.Framework; @@ -48,12 +50,13 @@ public void NetworkConfigInvalidNetworkPrefabTest() Assert.False(exceptionOccurred); } + private const string k_PrefabObjectName = "NetworkPrefabHandlerTestObject"; [Test] public void NetworkPrefabHandlerClass() { Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _)); - var testPrefabObjectName = "NetworkPrefabHandlerTestObject"; + var testPrefabObjectName = k_PrefabObjectName; Guid baseObjectID = NetworkManagerHelper.AddGameNetworkObject(testPrefabObjectName); NetworkObject baseObject = NetworkManagerHelper.InstantiatedNetworkObjects[baseObjectID]; @@ -150,6 +153,13 @@ public void TearDown() { //Stop, shutdown, and destroy NetworkManagerHelper.ShutdownNetworkManager(); + + var networkObjects = UnityEngine.Object.FindObjectsOfType().ToList(); + var networkObjectsList = networkObjects.Where(c => c.name.Contains(k_PrefabObjectName)); + foreach (var networkObject in networkObjectsList) + { + UnityEngine.Object.DestroyImmediate(networkObject); + } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs index 729ea542ba..0718224b1f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs @@ -44,7 +44,7 @@ public GameObject PreparePrefab(Type type) var prefabToSpawn = new GameObject(); prefabToSpawn.AddComponent(type); var networkObjectPrefab = prefabToSpawn.AddComponent(); - MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObjectPrefab); + MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObjectPrefab, DefaultPayerGlobalObjectIdHashValue); m_ServerNetworkManager.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab() { Prefab = prefabToSpawn }); foreach (var clientNetworkManager in m_ClientNetworkManagers) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs index 8a570c66b5..ecf8712434 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs @@ -15,6 +15,7 @@ public void TestSyncAxes( { var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}"); var networkObject = gameObject.AddComponent(); + networkObject.GlobalObjectIdHash = (uint)Time.realtimeSinceStartup; var networkTransform = gameObject.AddComponent(); networkTransform.enabled = false; // do not tick `FixedUpdate()` or `Update()` @@ -177,7 +178,7 @@ public void TestSyncAxes( } } - Object.DestroyImmediate(networkTransform); + Object.DestroyImmediate(gameObject); } } } diff --git a/testproject/Assets/Resources.meta b/testproject/Assets/Resources.meta new file mode 100644 index 0000000000..d4b26f12f1 --- /dev/null +++ b/testproject/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e5a90d716db681540a987bde0c8b44e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 20874b406ba000e052c09ec5065482ef71a002a8 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 16:16:11 -0500 Subject: [PATCH 076/106] fix Fixing minor issue with the standard NetworkPrefabPool (really need to merge these two classes) where it wasn't removing the registration from NetworkPrefabHandler when a scene was unloaded (only in the traditional SceneTransitioningTest without additive scene loading). --- testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 8cdcd121e7..82b0fdc3cc 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -82,6 +82,11 @@ private void RegisterCustomPrefabHandler() } } + private void OnDisable() + { + OnUnloadScene(); + } + private void DeregisterCustomPrefabHandler() { // Register the custom spawn handler? From 2485c89ca0d7a5b521c6015dad8ee1149aa99ae7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 16:19:10 -0500 Subject: [PATCH 077/106] fix and style removed the unused namespace from NetworkPrefabHandlerTests --- .../Tests/Runtime/NetworkPrefabHandlerTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs index 1b2751b629..2e30021315 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Collections; using System.Collections.Generic; using UnityEngine; using NUnit.Framework; From 2e1ce677f60b79ba018f29a654a2d372f340a4f7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 16:54:29 -0500 Subject: [PATCH 078/106] fix Fixing some tools unit test related issues. --- .../Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs | 6 ++++++ .../Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs index 06c072d7ac..7b2768e854 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs @@ -154,6 +154,9 @@ public IEnumerator TrackMultipleNetworkObjectSpawnSentMetric() Assert.AreEqual(1, objectSpawnedSentMetricValues.Select(x => x.BytesCount).Distinct().Count()); Assert.That(objectSpawnedSentMetricValues.Select(x => x.BytesCount), Has.All.Not.EqualTo(0)); + + // Always destroy anything you create + UnityEngine.Object.DestroyImmediate(gameObject); } [UnityTest] @@ -192,6 +195,9 @@ public IEnumerator TrackMultipleNetworkObjectDestroySentMetric() Assert.AreEqual(1, objectDestroyedSentMetricValues.Select(x => x.BytesCount).Distinct().Count()); Assert.That(objectDestroyedSentMetricValues.Select(x => x.BytesCount), Has.All.Not.EqualTo(0)); + + // Always destroy anything you create + UnityEngine.Object.DestroyImmediate(gameObject); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs index b14abdfd6c..409f93fc05 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs @@ -1,4 +1,4 @@ -#if MULTIPLAYER_TOOLS +#if MULTIPLAYER_TOOLS using System; using System.Collections; using System.Linq; @@ -34,6 +34,10 @@ public IEnumerator TrackOwnershipChangeSentMetric() Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeSent.NetworkId.NetworkId); Assert.AreEqual(Server.LocalClientId, ownershipChangeSent.Connection.Id); Assert.AreEqual(2, ownershipChangeSent.BytesCount); + + // Always destroy anything you create + UnityEngine.Object.DestroyImmediate(gameObject); + } [UnityTest] @@ -58,6 +62,9 @@ public IEnumerator TrackOwnershipChangeReceivedMetric() var ownershipChangeReceived = metricValues.First(); Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeReceived.NetworkId.NetworkId); Assert.AreEqual(2, ownershipChangeReceived.BytesCount); + + // Always destroy anything you create + UnityEngine.Object.DestroyImmediate(gameObject); } } } From 84c513a7732ed11e3a1219940362a4e89488e163 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 17:19:33 -0500 Subject: [PATCH 079/106] fix Removing my tools fix as that was the wrong place to fix that issues. The error causing issue actually came before those two tests where something was being instantiated but never destroyed. If this fails then I might need to do the same thing with the Setup side of things as well. --- .../Tests/Runtime/BaseMultiInstanceTest.cs | 10 ++++++++++ .../Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs | 6 ------ .../Runtime/Metrics/OwnershipChangeMetricsTests.cs | 7 ------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs index fb8c1e3f5d..228008e8ef 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Linq; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; @@ -41,6 +42,15 @@ 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) + { + Object.DestroyImmediate(networkObject); + } + // wait for next frame so everything is destroyed, so following tests can execute from clean environment int nextFrameNumber = Time.frameCount + 1; yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs index 7b2768e854..06c072d7ac 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/NetworkObjectMetricsTests.cs @@ -154,9 +154,6 @@ public IEnumerator TrackMultipleNetworkObjectSpawnSentMetric() Assert.AreEqual(1, objectSpawnedSentMetricValues.Select(x => x.BytesCount).Distinct().Count()); Assert.That(objectSpawnedSentMetricValues.Select(x => x.BytesCount), Has.All.Not.EqualTo(0)); - - // Always destroy anything you create - UnityEngine.Object.DestroyImmediate(gameObject); } [UnityTest] @@ -195,9 +192,6 @@ public IEnumerator TrackMultipleNetworkObjectDestroySentMetric() Assert.AreEqual(1, objectDestroyedSentMetricValues.Select(x => x.BytesCount).Distinct().Count()); Assert.That(objectDestroyedSentMetricValues.Select(x => x.BytesCount), Has.All.Not.EqualTo(0)); - - // Always destroy anything you create - UnityEngine.Object.DestroyImmediate(gameObject); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs index 409f93fc05..9cfb2dfbde 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs @@ -34,10 +34,6 @@ public IEnumerator TrackOwnershipChangeSentMetric() Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeSent.NetworkId.NetworkId); Assert.AreEqual(Server.LocalClientId, ownershipChangeSent.Connection.Id); Assert.AreEqual(2, ownershipChangeSent.BytesCount); - - // Always destroy anything you create - UnityEngine.Object.DestroyImmediate(gameObject); - } [UnityTest] @@ -62,9 +58,6 @@ public IEnumerator TrackOwnershipChangeReceivedMetric() var ownershipChangeReceived = metricValues.First(); Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeReceived.NetworkId.NetworkId); Assert.AreEqual(2, ownershipChangeReceived.BytesCount); - - // Always destroy anything you create - UnityEngine.Object.DestroyImmediate(gameObject); } } } From 1618a13afaca2c6e00b3337f697855c03fa7fdae Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 18:31:13 -0500 Subject: [PATCH 080/106] fix test Last try of the day... just delete every NetworkObject that exists when we start a mutli-instance test. --- .../Tests/Runtime/BaseMultiInstanceTest.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs index 228008e8ef..df3d8d30ee 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs @@ -65,6 +65,16 @@ public virtual IEnumerator Teardown() /// public IEnumerator StartSomeClientsAndServerWithPlayers(bool useHost, int nbClients, Action updatePlayerPrefab, int targetFrameRate = 60) { + // 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 netObject in networkObjects) + { + Object.DestroyImmediate(netObject); + } + + // Create multiple NetworkManager instances if (!MultiInstanceHelpers.Create(nbClients, out NetworkManager server, out NetworkManager[] clients, targetFrameRate)) { From 1130fe1e804cc1cd3a18ea6c7c1430ca19d5f576 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 19:14:34 -0500 Subject: [PATCH 081/106] Test For real, this is my last fix test and then I will ask Tools team about the issues involved in trying to debug their tests locally. --- .../Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs index 9cfb2dfbde..87c781e189 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs @@ -17,6 +17,7 @@ public IEnumerator TrackOwnershipChangeSentMetric() { var gameObject = new GameObject(Guid.NewGuid().ToString()); var networkObject = gameObject.AddComponent(); + MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject, DefaultPayerGlobalObjectIdHashValue); networkObject.NetworkManagerOwner = Server; networkObject.Spawn(); @@ -41,6 +42,7 @@ public IEnumerator TrackOwnershipChangeReceivedMetric() { var gameObject = new GameObject(Guid.NewGuid().ToString()); var networkObject = gameObject.AddComponent(); + MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject, DefaultPayerGlobalObjectIdHashValue); networkObject.NetworkManagerOwner = Server; networkObject.Spawn(); From 51198d547f980caaac12cf3692f5ee6c1d943cf4 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sun, 15 Aug 2021 20:41:30 -0500 Subject: [PATCH 082/106] style updated comments and added some xml doc see refs. --- .../SceneManagement/NetworkSceneManager.cs | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index b09af6f060..0a2819e1d8 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -89,9 +89,15 @@ public class NetworkSceneManager // Used to be able to turn re-synchronization off for future snapshot development purposes. internal static bool DisableReSynchronization; - // Used to detect if we are in the middle of a single mode scene transition + /// + /// Used to detect if a scene event is underway + /// Only 1 scene event can occur on the server at a time for now. + /// private static bool s_IsSceneEventActive = false; + /// + /// For multi-instance unit tests, set this to true if you are use the + /// internal static bool IsTesting; /// @@ -112,12 +118,19 @@ public class NetworkSceneManager internal readonly Dictionary SceneIndexToString = new Dictionary(); internal readonly Dictionary SceneEventProgressTracking = new Dictionary(); - /// - /// ScenePlacedObjects (i.e. In-Scene Placed NetworkObjects) are now stored by GlobalObjectIdHash + /// Used to track in-scene placed NetworkObjects + /// We store them by: + /// [GlobalObjectIdHash][Scene.Handle][NetworkObject] + /// The Scene.Handle aspect allows us to distinguish duplicated in-scene placed NetworkObjects created by the loading + /// of the same additive scene multiple times. /// internal readonly Dictionary> ScenePlacedObjects = new Dictionary>(); + /// + /// This is used for the deserialization of in-scene placed NetworkObjects in order to distinguish duplicated in-scene + /// placed NetworkObjects created by the loading of the same additive scene multiple times. + /// internal Scene SceneBeingSynchronized; // Used for observed object synchronization @@ -126,19 +139,30 @@ public class NetworkSceneManager // Used to track which scenes are currently loaded private Dictionary> m_ScenesLoaded = new Dictionary>(); + /// + /// Since Scene.handle is unique per client, we create a look-up table between the client and server + /// internal Dictionary ServerSceneHandleToClientSceneHandle = new Dictionary(); - // 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 - // When it is set: Just before starting the asynchronous loading call - // When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do - // not destroy temporary scene are moved into the active scene + /// + /// 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 + /// When it is set: Just before starting the asynchronous loading call + /// When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do + /// not destroy temporary scene are moved into the active scene + /// internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad = false; - //Client and Server: used for all scene event processing except for ClientSynchEventData specific events + /// + /// Client and Server: + /// Used for all scene event processing except for ClientSynchEventData specific events + /// internal SceneEventData SceneEventData; - //Server Side: Used specifically for scene synchronization and scene event progress related events. + /// + /// Server Side: + /// Used specifically for scene synchronization and scene event progress related events. + /// internal SceneEventData ClientSynchEventData; private NetworkManager m_NetworkManager { get; } @@ -1363,7 +1387,8 @@ private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePl // Just add every NetworkObject found that isn't already in the list // With additive scenes, we can have multiple in-scene placed NetworkObjects with the same GlobalObjectIdHash value - // Client Side Synchronization: So, we add them on a FIFO basis and for each scene loaded + // During Client Side Synchronization: We add them on a FIFO basis, for each scene loaded without clearing, and then + // at the end of scene loading we use this list to soft synchronize all in-scene placed NetworkObjects foreach (var networkObjectInstance in networkObjects) { // We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (additive scenes) From d1902c8c46fd2f6cdef0ef697af30b97a6c74fb0 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Mon, 16 Aug 2021 09:21:00 -0500 Subject: [PATCH 083/106] test updating test projects build settings scenes in build list with new scene to work with additive scene manual testing levels --- testproject/ProjectSettings/EditorBuildSettings.asset | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index 1ed2ce2d57..cd3a7b3f89 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -71,4 +71,7 @@ EditorBuildSettings: - enabled: 1 path: Assets/Samples/PrefabPool/PrefabPoolOverrideExample.unity guid: 8c9bee1332e0526429d8a2c929945d60 + - enabled: 1 + path: Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneMultiInstance.unity + guid: 0ae94f636016d3b40bfbecad57d99553 m_configObjects: {} From 581adfa0401b87c80969a62eb49fe6c21436a8cc Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 17 Aug 2021 08:41:47 -0500 Subject: [PATCH 084/106] Update com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs Co-authored-by: Luke Stampfli <43687322+LukeStampfli@users.noreply.github.com> --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 8d57d17793..7f810c3835 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -480,7 +480,7 @@ public void SpawnAsPlayerObject(ulong clientId, bool destroyWithScene = false) } /// - /// Despawns the parent of this and sends a destroy message for it to all connected clients. + /// Despawns the of this and sends a destroy message for it to all connected clients. /// /// (true) the will be destroyed (false) the will persist after being despawned public void Despawn(bool destroy = false) From 1b42100741c8a1aba6ef5d45b51a0359b50ccd71 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:14:52 -0500 Subject: [PATCH 085/106] fix Minor stats display issue with another sneaky resources meta file that somehow made it into a previous commit. --- testproject/Assets/Resources.meta | 8 -------- .../Tests/Manual/Scripts/StatsDisplay.cs | 19 +------------------ 2 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 testproject/Assets/Resources.meta diff --git a/testproject/Assets/Resources.meta b/testproject/Assets/Resources.meta deleted file mode 100644 index d4b26f12f1..0000000000 --- a/testproject/Assets/Resources.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: e5a90d716db681540a987bde0c8b44e4 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs index 4528c88b4b..64a31001b5 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsDisplay.cs @@ -179,24 +179,7 @@ private IEnumerator UpdateTextStatus() { m_LastStatsDump = m_IsServer ? "Server Stats" : "Client Stats"; m_LastStatsDump += "\ndeltaTime: [" + Time.deltaTime.ToString() + "]"; - var profileData = NetworkObject.NetworkManager.Transport.GetTransportProfilerData(); - - if (profileData.Count > 0) - { - foreach (var entry in profileData) - { - if (m_LastStatsDump != string.Empty) - { - m_LastStatsDump += "\n"; - } - m_LastStatsDump += entry.Key + ": " + entry.Value.ToString("0.0"); - } - } - else - { - m_LastStatsDump += "\n"; - } - + m_LastStatsDump += "\n"; m_LastStatsDump += $"Active Scene: {SceneManager.GetActiveScene().name}\n"; if (m_SceneEventNotificationQueue != null) { From 7f559f5afa6cd0868fb602cd6e1f21261c61dc7e Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 17 Aug 2021 14:29:52 -0500 Subject: [PATCH 086/106] stle Renamed NotifyPlayerConnected to ApprovedPlayerSpawn --- .../Runtime/Core/NetworkManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 6aa45dcea6..647285987c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1407,7 +1407,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? } // Separating this into a contained function call for potential further future separation of when this notification is sent. - NotifyPlayerConnected(ownerClientId, playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); + ApprovedPlayerSpawn(ownerClientId, playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); } else { @@ -1417,11 +1417,11 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? } /// - /// Notifies all existing clients that a new player has joined + /// Spawns the newly approved player /// /// new player client identifier /// the prefab GlobalObjectIdHash value for this player - internal void NotifyPlayerConnected(ulong clientId, uint playerPrefabHash) + internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash) { foreach (KeyValuePair clientPair in ConnectedClients) { From 747f1f73711d2cd07bf172f6da2b7fd80cbe3180 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 17 Aug 2021 17:49:43 -0500 Subject: [PATCH 087/106] refactor removing unused m_ObservedObjects. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 0a2819e1d8..557e00c023 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -133,9 +133,6 @@ public class NetworkSceneManager /// internal Scene SceneBeingSynchronized; - // Used for observed object synchronization - private readonly List m_ObservedObjects = new List(); - // Used to track which scenes are currently loaded private Dictionary> m_ScenesLoaded = new Dictionary>(); @@ -962,13 +959,10 @@ private void OnClientLoadedScene(Scene scene) /// newly joined client identifier internal void SynchronizeNetworkObjects(ulong ownerClientId) { - m_ObservedObjects.Clear(); - foreach (var sobj in m_NetworkManager.SpawnManager.SpawnedObjectsList) { if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(ownerClientId)) { - m_ObservedObjects.Add(sobj); sobj.Observers.Add(ownerClientId); } } From 831c3a3b2480a3391b4acff76b39d5805c46cc6a Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 17 Aug 2021 17:52:47 -0500 Subject: [PATCH 088/106] style Adding additional comment to further clarify usage of a multidimensional dictionary --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 557e00c023..b25276ac85 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -134,6 +134,7 @@ public class NetworkSceneManager internal Scene SceneBeingSynchronized; // Used to track which scenes are currently loaded + // We store scenes as follows: [SceneName][SceneHandle][Scene] private Dictionary> m_ScenesLoaded = new Dictionary>(); /// From 27d8f50699718261856f87b874762bc217fdc171 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Wed, 18 Aug 2021 14:00:47 +0100 Subject: [PATCH 089/106] minor editor UI polish --- .../Editor/NetworkManagerEditor.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs index 06acadf355..563a2eeabd 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs @@ -145,7 +145,7 @@ private void OnEnable() var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override)); var networkOverrideInt = networkOverrideProp.enumValueIndex; - return 10 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5); + return 8 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5); }; m_NetworkPrefabsList.drawElementCallback = (rect, index, isActive, isFocused) => { @@ -202,17 +202,23 @@ 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 = 38; + int firstLabelWidth = 24; int padding = 2; - EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), $"{index}"); + 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, "Registered Scene Entries"); + m_RegisteredSceneAssetsList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "NetworkScenes"); m_RegisteredSceneAssetsList.onAddCallback = (registeredList) => { From 39005dd31bc0f7ad98d423655e75509ea11096d8 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Wed, 18 Aug 2021 14:06:54 +0100 Subject: [PATCH 090/106] `./standards.py --fix` --- .../Runtime/Core/NetworkManager.cs | 10 ++++---- .../Runtime/Core/NetworkObject.cs | 2 +- .../Runtime/Logging/NetworkLog.cs | 2 +- .../Runtime/Metrics/StreamExtensions.cs | 2 +- .../SceneManagement/NetworkSceneManager.cs | 6 ++--- .../Runtime/SceneManagement/SceneEventData.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 4 ++-- .../Runtime/Metrics/Utility/MetricTestBase.cs | 2 +- .../Utility/NetworkVariableComponent.cs | 2 +- .../Metrics/Utility/RpcTestComponent.cs | 2 +- .../NetworkObjectSceneSerializationTests.cs | 2 +- .../Tests/Runtime/NetworkSceneManagerTests.cs | 2 +- .../AdditiveSceneToggleHandler.cs | 6 ++--- .../SceneEventNotificationQueue.cs | 10 ++++---- .../SwitchSceneHandlerAdditive.cs | 2 +- .../Tests/Manual/Scripts/IndependentMover.cs | 2 +- .../Scripts/NetworkPrefabPoolAdditive.cs | 24 +++++++++---------- .../Assets/Tests/Runtime/MessageOrdering.cs | 2 +- 18 files changed, 42 insertions(+), 42 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 647285987c..015f599516 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -264,9 +264,9 @@ private void OnValidate() if (NetworkConfig.EnableSceneManagement) { - foreach(var sceneAsset in NetworkConfig.RegisteredSceneAssets) + foreach (var sceneAsset in NetworkConfig.RegisteredSceneAssets) { - if(!NetworkConfig.RegisteredScenes.Contains(sceneAsset.name)) + if (!NetworkConfig.RegisteredScenes.Contains(sceneAsset.name)) { NetworkConfig.RegisteredScenes.Add(sceneAsset.name); } @@ -1380,7 +1380,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? // Don't send the CONNECTION_APPROVED message if this is the host that connected locally if (ownerClientId != ServerClientId) { - var context = MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.ConnectionApproved, NetworkChannel.Internal, new ulong[]{ownerClientId}, + var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ConnectionApproved, NetworkChannel.Internal, new ulong[] { ownerClientId }, NetworkUpdateStage.EarlyUpdate); if (context != null) @@ -1433,8 +1433,8 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash) continue; //The new client. } - var context = MessageQueueContainer.EnterInternalCommandContext( MessageQueueContainer.MessageType.CreateObject, NetworkChannel.Internal, - new[] {clientPair.Key}, NetworkUpdateLoop.UpdateStage); + var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.CreateObject, NetworkChannel.Internal, + new[] { clientPair.Key }, NetworkUpdateLoop.UpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext)context) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 7f810c3835..b66c8165b2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -384,7 +384,7 @@ public static void NetworkHide(List networkObjects, ulong clientI private void OnDestroy() { if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned - && (IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true) )) + && (IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true))) { throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead."); } diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs index e969eb5da2..3d4cb9b4cd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -65,7 +65,7 @@ private static void LogServer(string message, LogType logType) { var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); bufferSizeCapture.StartMeasureSegment(); - nonNullContext.NetworkWriter.WriteByte((byte) logType); + nonNullContext.NetworkWriter.WriteByte((byte)logType); nonNullContext.NetworkWriter.WriteStringPacked(message); var size = bufferSizeCapture.StopMeasureSegment(); diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/StreamExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/StreamExtensions.cs index 95474529e4..615efb5994 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/StreamExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/StreamExtensions.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Unity.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index b25276ac85..f9060b917f 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -556,7 +556,7 @@ private void OnClientUnloadScene() if (m_ScenesLoaded.ContainsKey(sceneName)) { - if(!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) + if (!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) { throw new Exception("No server to scene handle exist!"); } @@ -810,7 +810,7 @@ private void OnSceneLoaded(string sceneName) else { // For the client, we make a server scene handle to client scene handle look up table - if(!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) + if (!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) { ServerSceneHandleToClientSceneHandle.Add(SceneEventData.SceneHandle, nextScene.handle); } @@ -1395,7 +1395,7 @@ private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePl ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, new Dictionary()); } - if(!ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].ContainsKey(networkObjectInstance.gameObject.scene.handle)) + if (!ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].ContainsKey(networkObjectInstance.gameObject.scene.handle)) { ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].Add(networkObjectInstance.gameObject.scene.handle, networkObjectInstance); } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 9eb15f7e43..7cd6786ac4 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -189,7 +189,7 @@ internal bool IsDoneWithSynchronization() { return true; } - else if (ScenesToSynchronize.Count != SceneHandlesToSynchronize.Count ) + else if (ScenesToSynchronize.Count != SceneHandlesToSynchronize.Count) { // This should never happen, but in the event it does... throw new Exception($"[{nameof(SceneEventData)}-Internal Mismatch Error] {nameof(ScenesToSynchronize)} count != {nameof(SceneHandlesToSynchronize)} count!"); diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 0795257fb6..a01dbf8464 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -269,7 +269,7 @@ internal NetworkObject CreateLocalNetworkObject(bool isSceneObject, uint globalO { var networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(globalObjectIdHash); - if(networkObject == null) + if (networkObject == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { @@ -677,7 +677,7 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec // If the custom prefab handler returns true then we destroy the parent GameObject // otherwise the custom prefab handler handles how they want to deal with despawning // the NetworkObject - if(NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObject)) + if (NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObject)) { if (SpawnedObjects.Remove(networkObject.NetworkObjectId)) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/MetricTestBase.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/MetricTestBase.cs index a4f2c4ce45..c9531be61d 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/MetricTestBase.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/MetricTestBase.cs @@ -1,4 +1,4 @@ -#if MULTIPLAYER_TOOLS +#if MULTIPLAYER_TOOLS using System; using System.Collections; using UnityEngine; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/NetworkVariableComponent.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/NetworkVariableComponent.cs index 461fdc6736..f4eb14db88 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/NetworkVariableComponent.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/NetworkVariableComponent.cs @@ -1,4 +1,4 @@ -#if MULTIPLAYER_TOOLS +#if MULTIPLAYER_TOOLS using System; namespace Unity.Netcode.RuntimeTests.Metrics.Utlity diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/RpcTestComponent.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/RpcTestComponent.cs index 7ba041039d..d9792e1570 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/RpcTestComponent.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/Utility/RpcTestComponent.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Unity.Netcode.RuntimeTests.Metrics.Utlity { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index 149da0d91c..dda03375b2 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -90,7 +90,7 @@ public void NetworkObjectSceneSerializationFailure() NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(networkObject.GlobalObjectIdHash, new Dictionary()); } // Add this valid NetworkObject into the ScenePlacedObjects list - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects[networkObject.GlobalObjectIdHash].Add(SceneManager.GetActiveScene().handle,networkObject); + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects[networkObject.GlobalObjectIdHash].Add(SceneManager.GetActiveScene().handle, networkObject); } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs index 02551b6aa3..6cdb42cd19 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs @@ -13,7 +13,7 @@ public void SwitchSceneWithoutSceneManagement() var threwException = false; try { - networkManager.SceneManager.LoadScene("SomeSceneNane",UnityEngine.SceneManagement.LoadSceneMode.Single); + networkManager.SceneManager.LoadScene("SomeSceneNane", UnityEngine.SceneManagement.LoadSceneMode.Single); } catch (Exception ex) { diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index b4f0cd04cd..a588d2d93b 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -52,11 +52,11 @@ private IEnumerator CheckForVisibility() { while (!m_ExitingScene) { - if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening ) + if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) { if (m_ToggleObject) { - if(NetworkManager.Singleton.IsServer) + if (NetworkManager.Singleton.IsServer) { m_ToggleObject.gameObject.SetActive(true); if (m_ActivateOnLoad) @@ -174,7 +174,7 @@ private IEnumerator SceneEventCoroutine(bool isLoading) var timeOutAfter = Time.realtimeSinceStartup + 10.0f; while (m_WaitForSceneLoadOrUnload) { - if(timeOutAfter < Time.realtimeSinceStartup) + if (timeOutAfter < Time.realtimeSinceStartup) { Debug.LogWarning("Timed out waiting for scene to load or unload!"); m_WaitForSceneLoadOrUnload = false; diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs index b867688e85..6e36aa912f 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneEventNotificationQueue.cs @@ -14,11 +14,11 @@ namespace TestProject.ManualTests /// Note: this should be added to the parent GameObject of your NetworkManager component /// and will only work if NetworkManager's "Don't Destroy" is true. /// - public class SceneEventNotificationQueue: MonoBehaviour + public class SceneEventNotificationQueue : MonoBehaviour { public bool LogToConsole; - [Range(1,30)] + [Range(1, 30)] public float TimeToLive = 10.0f; private bool m_IsInitialized; @@ -53,7 +53,7 @@ private void OnSceneEvent(SceneEvent sceneEvent) if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.S2C_UnLoadComplete || sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.S2C_LoadComplete) { sceneEventMsg += $" | Loaded ({sceneEvent.ClientsThatCompleted.Count}) : ("; - foreach(var clientId in sceneEvent.ClientsThatCompleted) + foreach (var clientId in sceneEvent.ClientsThatCompleted) { sceneEventMsg += $"{clientId}, "; } @@ -84,9 +84,9 @@ public List GetCurrentNotifications() private void Update() { - if(m_NetworkManager != null && m_NetworkManager.IsListening) + if (m_NetworkManager != null && m_NetworkManager.IsListening) { - if(!m_IsInitialized) + if (!m_IsInitialized) { m_NetworkManager.SceneManager.OnSceneEvent += OnSceneEvent; m_IsInitialized = true; diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs index 9c4812c37f..c83cadd4e6 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SwitchSceneHandlerAdditive.cs @@ -26,7 +26,7 @@ public class SwitchSceneHandlerAdditive : NetworkBehaviour private void OnValidate() { m_SceneToSwitchTo = new List(); - foreach(var sceneAsset in m_SceneAssets) + foreach (var sceneAsset in m_SceneAssets) { m_SceneToSwitchTo.Add(sceneAsset.name); } diff --git a/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs index fcafde9cb3..15eebb6dc2 100644 --- a/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs +++ b/testproject/Assets/Tests/Manual/Scripts/IndependentMover.cs @@ -36,7 +36,7 @@ private void FixedUpdate() public void Move(int speed) { - if(m_Rigidbody == null) + if (m_Rigidbody == null) { m_Rigidbody = GetComponent(); } diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index 593786f839..e96e9f38c0 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -120,21 +120,21 @@ private void OnSceneEvent(SceneEvent sceneEvent) switch (sceneEvent.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Unload: - { - if (sceneEvent.LoadSceneMode == LoadSceneMode.Additive && (gameObject.scene.name == sceneEvent.SceneName)) { - OnUnloadScene(); + if (sceneEvent.LoadSceneMode == LoadSceneMode.Additive && (gameObject.scene.name == sceneEvent.SceneName)) + { + OnUnloadScene(); + } + break; } - break; - } case SceneEventData.SceneEventTypes.S2C_Load: - { - if (sceneEvent.LoadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneEvent.SceneName) || !SpawnInSourceScene)) { - OnUnloadScene(); + if (sceneEvent.LoadSceneMode == LoadSceneMode.Single && ((gameObject.scene.name == sceneEvent.SceneName) || !SpawnInSourceScene)) + { + OnUnloadScene(); + } + break; } - break; - } } } @@ -144,7 +144,7 @@ private void CleanNetworkObjects() { foreach (var obj in m_ObjectPool) { - if(obj == null) + if (obj == null) { continue; } @@ -291,7 +291,7 @@ public GameObject GetObject() { foreach (var obj in m_ObjectPool) { - if(obj == null) + if (obj == null) { continue; } diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 309e2a9e81..70506cdd08 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using Unity.Netcode; using Unity.Netcode.RuntimeTests; using NUnit.Framework; From 75817ed5c1168596b4989436e7a338b605eceed8 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 18 Aug 2021 12:55:48 -0500 Subject: [PATCH 091/106] refactor and style Removing a check that is no longer needed. Improved an exception message. Improved some comments. Removed a CR --- .../Runtime/Core/NetworkManager.cs | 21 +++++++------------ .../Runtime/Core/NetworkObject.cs | 1 - .../SceneManagement/NetworkSceneManager.cs | 6 ++++-- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 015f599516..ae3a22e39f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -870,14 +870,7 @@ public void Shutdown() NetworkTickSystem.Tick -= OnNetworkManagerTick; NetworkTickSystem = null; } - // This is required for handling the potential scenario where multiple NetworkManager instances are created. - // See MTT-860 for more information -#if !UNITY_2020_2_OR_NEWER - if (IsListening) - { - NetworkProfiler.Stop(); - } -#endif + IsServer = false; IsClient = false; NetworkConfig.NetworkTransport.OnTransportEvent -= HandleRawTransportPoll; @@ -1352,12 +1345,12 @@ private void SyncTime() /// /// Server Side: Handles the approval of a client /// - /// - /// - /// - /// - /// - /// + /// client being approved + /// whether we want to create a player or not + /// the GlobalObjectIdHash value for the Network Prefab to create as the player + /// Is the player approved or not? + /// Used when createPlayerObject is true, position of the player when spawned + /// Used when createPlayerObject is true, rotation of the player when spawned internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? playerPrefabHash, bool approved, Vector3? position, Quaternion? rotation) { if (approved) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index b66c8165b2..8d68d18b53 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -18,7 +18,6 @@ public sealed class NetworkObject : MonoBehaviour [SerializeField] internal uint GlobalObjectIdHash; - #if UNITY_EDITOR // HEAD: DO NOT USE! TEST ONLY TEMP IMPL, WILL BE REMOVED internal uint TempGlobalObjectIdHashOverride = 0; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index f9060b917f..2281e6bcfc 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -397,7 +397,9 @@ private SceneEventProgress ValidateServerSceneEvent(string sceneName, bool isUnl 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 this method."); + 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)}."); } // Return scene event already in progress if one is already in progress... :) @@ -538,7 +540,7 @@ public SceneEventProgressStatus UnloadScene(Scene scene) /// /// Client Side: - /// SceneManager.UnloadSceneAsync handler for clients + /// Handles scene events. /// private void OnClientUnloadScene() { From db097ee10db1029ffd17dadac0bfa28f4de060d6 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 18 Aug 2021 13:57:35 -0500 Subject: [PATCH 092/106] Update Applying Fatih's super-nit. Co-authored-by: M. Fatih MAR --- .../Runtime/Spawning/NetworkSpawnManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index a01dbf8464..e0972303df 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -500,7 +500,7 @@ internal void ServerDestroySpawnedSceneObjects() foreach (var sobj in spawnedObjects) { - if ((sobj.IsSceneObject != null && sobj.IsSceneObject == true)) + if (sobj.IsSceneObject != null && sobj.IsSceneObject) { SpawnedObjectsList.Remove(sobj); UnityEngine.Object.Destroy(sobj.gameObject); From 4f71b1c2ec24c033ec909a130911b8ea750c95af Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 18 Aug 2021 14:15:34 -0500 Subject: [PATCH 093/106] test fix Fixing an issue that can occur in a testproject helper script where the wrong scene could be applied to the wrong "toggler" when loaded. --- .../SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs index a588d2d93b..b1e24eb859 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/AdditiveSceneToggleHandler.cs @@ -91,7 +91,8 @@ private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) { if (sceneEvent.SceneEventType == SceneEventData.SceneEventTypes.C2S_LoadComplete) { - if (sceneEvent.ClientId == NetworkManager.Singleton.ServerClientId && !m_SceneLoaded.IsValid() && sceneEvent.Scene.IsValid()) + if (sceneEvent.ClientId == NetworkManager.Singleton.ServerClientId && !m_SceneLoaded.IsValid() + && sceneEvent.Scene.IsValid() && sceneEvent.Scene.name == m_SceneToLoad) { m_SceneLoaded = sceneEvent.Scene; m_WaitForSceneLoadOrUnload = false; From 6aaaa5499ef3e5662f612637fb733c1bcdcebdba Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 18 Aug 2021 14:21:30 -0500 Subject: [PATCH 094/106] fix Fixing committed suggested change bug. --- .../Runtime/Spawning/NetworkSpawnManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index e0972303df..df14dad804 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -500,7 +500,7 @@ internal void ServerDestroySpawnedSceneObjects() foreach (var sobj in spawnedObjects) { - if (sobj.IsSceneObject != null && sobj.IsSceneObject) + if (sobj.IsSceneObject != null && sobj.IsSceneObject.Value) { SpawnedObjectsList.Remove(sobj); UnityEngine.Object.Destroy(sobj.gameObject); From 3f76dc187744744a519e82174400efbf4e493b17 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 18 Aug 2021 14:27:24 -0500 Subject: [PATCH 095/106] fix unit test Fixing the change in exception thrown when trying to call NetworkSceneManager.Load or NetworkSceneManager.Unload without having set NetworkConfig.EnableSceneManagement. --- .../Tests/Runtime/NetworkSceneManagerTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs index 6cdb42cd19..c5c5761bdf 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSceneManagerTests.cs @@ -17,7 +17,9 @@ public void SwitchSceneWithoutSceneManagement() } 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 this method.")) + 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; } From d2dc3212bf8c4321b2f76b38cc21456718859299 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Wed, 18 Aug 2021 22:53:19 +0100 Subject: [PATCH 096/106] fix: eliminate bad use-after-free(destroy) pattern --- .../Runtime/Core/NetworkObject.cs | 12 ++++++++++++ .../Runtime/Spawning/NetworkSpawnManager.cs | 17 ++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index e22d2c4063..3dc87e55bb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -380,8 +380,20 @@ public static void NetworkHide(List networkObjects, ulong clientI } } + private bool m_ApplicationQuitting = false; + + private void OnApplicationQuit() + { + m_ApplicationQuitting = true; + } + private void OnDestroy() { + if (m_ApplicationQuitting) + { + return; + } + if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned) { throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead."); diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 41dbb9b68b..2e2928f423 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -685,8 +685,15 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } } - var gobj = networkObject.gameObject; + // for some reason, we can get down here and SpawnedObjects for this + // networkId will no longer be here, even as we check this at the start + // of the function + if (SpawnedObjects.Remove(networkObject.NetworkObjectId)) + { + SpawnedObjectsList.Remove(networkObject); + } + var gobj = networkObject.gameObject; if (destroyGameObject && gobj != null) { if (NetworkManager.PrefabHandler.ContainsHandler(networkObject)) @@ -698,14 +705,6 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec UnityEngine.Object.Destroy(gobj); } } - - // for some reason, we can get down here and SpawnedObjects for this - // networkId will no longer be here, even as we check this at the start - // of the function - if (SpawnedObjects.Remove(networkObject.NetworkObjectId)) - { - SpawnedObjectsList.Remove(networkObject); - } } } } From 00a0e051165b96d6e19c5aa530a1d72811d9350b Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Wed, 18 Aug 2021 22:58:56 +0100 Subject: [PATCH 097/106] remove obsolete comment --- .../Runtime/Spawning/NetworkSpawnManager.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 2e2928f423..500a9d8c17 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -685,9 +685,6 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } } - // for some reason, we can get down here and SpawnedObjects for this - // networkId will no longer be here, even as we check this at the start - // of the function if (SpawnedObjects.Remove(networkObject.NetworkObjectId)) { SpawnedObjectsList.Remove(networkObject); From b5afae7b1fa600eff56640d3a3445a1167e2828b Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Wed, 18 Aug 2021 17:57:48 -0500 Subject: [PATCH 098/106] refactor Removes returning bool to determine if NetworkPrefabHandler should destroy an object with Fatih's awesome fixes from PR-1068 https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/pull/1068 --- .../Runtime/Spawning/NetworkPrefabHandler.cs | 11 ++++------- .../Runtime/Spawning/NetworkSpawnManager.cs | 13 +------------ .../Tests/Runtime/NetworkPrefabHandlerTests.cs | 4 ++-- .../PrefabPool/NetworkPrefabHandlerObjectPool.cs | 3 +-- .../NetworkPrefabHandlerObjectPoolOverride.cs | 4 +--- .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 5 ++--- .../Manual/Scripts/NetworkPrefabPoolAdditive.cs | 5 ++--- .../CustomPrefabSpawnerForPerformanceTests.cs | 3 +-- .../Support/SpawnRpcDespawnInstanceHandler.cs | 4 ++-- 9 files changed, 16 insertions(+), 36 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs index 5539f57e38..b15902ec57 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs @@ -40,8 +40,7 @@ public interface INetworkPrefabInstanceHandler /// The most common approach is to make the inactive by calling . /// /// The being destroyed - /// (true) destroy the parent (false) do not destroy the parent - bool Destroy(NetworkObject networkObject); + void Destroy(NetworkObject networkObject); } /// @@ -279,7 +278,7 @@ internal NetworkObject HandleNetworkPrefabSpawn(uint networkPrefabAssetHash, ulo /// Will invoke the implementation's Destroy method /// /// - internal bool HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) + internal void HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) { var networkObjectInstanceHash = networkObjectInstance.GlobalObjectIdHash; @@ -289,16 +288,14 @@ internal bool HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) var networkPrefabAssetHash = m_PrefabInstanceToPrefabAsset[networkObjectInstanceHash]; if (m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabAssetHash)) { - return m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Destroy(networkObjectInstance); + m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Destroy(networkObjectInstance); } } else // Otherwise the NetworkObject is the source NetworkPrefab if (m_PrefabAssetToPrefabHandler.ContainsKey(networkObjectInstanceHash)) { - return m_PrefabAssetToPrefabHandler[networkObjectInstanceHash].Destroy(networkObjectInstance); + m_PrefabAssetToPrefabHandler[networkObjectInstanceHash].Destroy(networkObjectInstance); } - - return true; } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 9e068b9449..d423a50ed4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -678,18 +678,7 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec { if (NetworkManager.PrefabHandler.ContainsHandler(networkObject)) { - // If the custom prefab handler returns true then we destroy the parent GameObject - // otherwise the custom prefab handler handles how they want to deal with despawning - // the NetworkObject - if (NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObject)) - { - if (SpawnedObjects.Remove(networkObject.NetworkObjectId)) - { - SpawnedObjectsList.Remove(networkObject); - } - UnityEngine.Object.Destroy(gobj); - return; - } + NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObject); } else { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs index 2e30021315..3194feafac 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs @@ -180,12 +180,12 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return networkObjectInstance; } - public bool Destroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { var instancesContainsNetworkObject = m_Instances.Contains(networkObject); Assert.True(instancesContainsNetworkObject); m_Instances.Remove(networkObject); - return true; + UnityEngine.Object.Destroy(networkObject.gameObject); } public bool StillHasInstances() diff --git a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs index 96913bc68e..af69232035 100644 --- a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs +++ b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPool.cs @@ -76,13 +76,12 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return gameObject.GetComponent(); } - public bool Destroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { if (m_ObjectsPool.Contains(networkObject.gameObject)) { networkObject.gameObject.SetActive(false); } - return false; } private IEnumerator SpawnObjects() diff --git a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs index 7e1402d913..f2d0f32679 100644 --- a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs +++ b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs @@ -139,14 +139,12 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return gameObject.GetComponent(); } - public bool Destroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { if (m_NameValidation.Contains(networkObject.gameObject.name)) { networkObject.gameObject.SetActive(false); } - - return false; } /// diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 82b0fdc3cc..803fb93bb0 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -411,7 +411,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni } return null; } - public bool Destroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { var genericBehaviour = networkObject.gameObject.GetComponent(); if (genericBehaviour.IsRegisteredPoolObject) @@ -420,9 +420,8 @@ public bool Destroy(NetworkObject networkObject) } else { - return true; + Object.Destroy(networkObject.gameObject); } - return false; } public MyCustomPrefabSpawnHandler(NetworkPrefabPool objectPool) diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index e96e9f38c0..b4c0c2f515 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -445,7 +445,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni } return null; } - public bool Destroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { var genericBehaviour = networkObject.gameObject.GetComponent(); if (genericBehaviour.IsRegisteredPoolObject) @@ -455,9 +455,8 @@ public bool Destroy(NetworkObject networkObject) } else { - return true; + Object.Destroy(networkObject.gameObject); } - return false; } public MyAdditiveCustomPrefabSpawnHandler(NetworkPrefabPoolAdditive objectPool) diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs index 92f86bac0a..41558e04dc 100644 --- a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs @@ -28,7 +28,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return networkObject; } - public bool Destroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { var behaviour = networkObject.gameObject.GetComponent(); // todo expensive, only used in teardown for now, should optimize eventually m_OnRelease(behaviour); @@ -36,7 +36,6 @@ public bool Destroy(NetworkObject networkObject) netTransform.position = Vector3.zero; netTransform.rotation = Quaternion.identity; m_ObjectPool.Release(behaviour); - return true; } public void Dispose() diff --git a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs index 567c5b34ac..5e2c5b2990 100644 --- a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs +++ b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs @@ -55,7 +55,7 @@ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaterni return networkObject; } - public bool Destroy(NetworkObject networkObject) + public void Destroy(NetworkObject networkObject) { WasDestroyed = true; if (networkObject.NetworkManager.IsClient) @@ -63,7 +63,7 @@ public bool Destroy(NetworkObject networkObject) Assert.AreEqual(NetworkUpdateStage.PostLateUpdate, NetworkUpdateLoop.UpdateStage); } - return true; + Object.Destroy(networkObject.gameObject); } } } From a1b8bba6cd75c6cd1df4fea4b7d069bf54ff800d Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 19 Aug 2021 19:26:07 -0500 Subject: [PATCH 099/106] refactor and fix This resolves the issue where disabling scene management would result in no NetworkObjectSynchronization of client relative observed NetworkObjects. This also further separates NetworkObject serialization even further from the NetworkSceneManager and is contained either in: NetworkSpawnManager, SceneEventData, and NetworkObject itself. This will help in future efforts to further unify this process with some form of "NetworkObjectSynchronization" system that ties together NetworkSceneManager, Snapshot, and NetworkSpawnManager related client/server NetworkObject synchronization/registration related tasks into a single management system that should work independently of other systems (at least something in that general direction). --- .../Runtime/Core/NetworkManager.cs | 21 +- .../Messaging/InternalMessageHandler.cs | 19 +- .../SceneManagement/NetworkSceneManager.cs | 102 +++------ .../Runtime/SceneManagement/SceneEventData.cs | 215 +++++++++++++----- .../Runtime/Spawning/NetworkSpawnManager.cs | 65 ++++++ 5 files changed, 289 insertions(+), 133 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 86f84903e0..20d608b626 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -460,6 +460,7 @@ private void Initialize(bool server) // 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++) { @@ -1375,11 +1376,11 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? ConnectedClients[ownerClientId].PlayerObject = networkObject; } - // Don't send the CONNECTION_APPROVED message if this is the host that connected locally + // Server doesn't send itself the connection approved message if (ownerClientId != ServerClientId) { - var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ConnectionApproved, NetworkChannel.Internal, new ulong[] { ownerClientId }, - NetworkUpdateStage.EarlyUpdate); + var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ConnectionApproved, NetworkChannel.Internal, + new ulong[] { ownerClientId }, NetworkUpdateStage.EarlyUpdate); if (context != null) { @@ -1387,15 +1388,25 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? { nonNullContext.NetworkWriter.WriteUInt64Packed(ownerClientId); nonNullContext.NetworkWriter.WriteInt32Packed(LocalTime.Tick); + + // If scene management is disabled, then just serialize all client relative (observed) NetworkObjects + if (!NetworkConfig.EnableSceneManagement) + { + SpawnManager.SerializeObservedNetworkObjects(ownerClientId, nonNullContext.NetworkWriter); + } } } - // Now inform the newly joined client of the scenes to be loaded as well as synchronize it with all relevant in-scene and dynamically spawned NetworkObjects + // If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization if (NetworkConfig.EnableSceneManagement) { SceneManager.SynchronizeNetworkObjects(ownerClientId); } } + else // Server just adds itself as an observer to all spawned NetworkObjects + { + SpawnManager.UpdateObservedNetworkObjects(ownerClientId); + } OnClientConnectedCallback?.Invoke(ownerClientId); @@ -1414,6 +1425,8 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? } } + + /// /// Spawns the newly approved player /// diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs index 25f01e0e17..82bb82026f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs @@ -53,9 +53,9 @@ public void HandleConnectionRequest(ulong clientId, Stream stream) /// /// Client Side: handles the connection approved message /// - /// - /// - /// + /// transport derived client identifier (currently not used) + /// incoming stream + /// time this message was received (currently not used) public void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime) { using (var reader = PooledNetworkReader.Get(stream)) @@ -67,6 +67,19 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv NetworkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport. NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); + + // Only if scene management is disabled do we handle NetworkObject synchronization at this point + if (!NetworkManager.NetworkConfig.EnableSceneManagement) + { + NetworkManager.SpawnManager.DestroySceneObjects(); + + // is not packed! + var objectCount = reader.ReadUInt16(); + for (ushort i = 0; i < objectCount; i++) + { + NetworkObject.DeserializeSceneObject(reader.GetStream() as NetworkBuffer, reader, m_NetworkManager); + } + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 2281e6bcfc..821b50ecb7 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -510,9 +510,6 @@ public SceneEventProgressStatus UnloadScene(Scene scene) // This will be the message we send to everyone when this scene event sceneEventProgress is complete sceneEventProgress.SceneEventType = SceneEventData.SceneEventTypes.S2C_UnLoadComplete; - // Sends the unload scene notification - SendSceneEventData(m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray()); - m_ScenesLoaded[sceneName].Remove(scene.handle); if (m_ScenesLoaded[sceneName].Count == 0) @@ -604,13 +601,29 @@ private void OnClientUnloadScene() /// /// Server and Client: - /// Invoked when the additively loaded scene is unloaded + /// Invoked when an additively loaded scene is unloaded /// private void OnSceneUnloaded() { + // First thing we do, if we are a server, is to send the unload scene event. + if (m_NetworkManager.IsServer) + { + // Server sends the unload scene notification after unloading because it will despawn all scene relative in-scene NetworkObjects + // If we send this event to all clients before the server is finished unloading they will get warning about an object being + // despawned that no longer exists + SendSceneEventData(m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray()); + + //Second, server sets itself as having finished unloading + if (SceneEventProgressTracking.ContainsKey(SceneEventData.SceneEventGuid)) + { + SceneEventProgressTracking[SceneEventData.SceneEventGuid].AddClientAsDone(m_NetworkManager.ServerClientId); + } + } + + // Next we prepare to send local notifications for unload complete SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_UnloadComplete; - //First, notify the client or server that a scene was unloaded + //Notify the client or server that a scene was unloaded OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventType, @@ -619,18 +632,13 @@ private void OnSceneUnloaded() ClientId = m_NetworkManager.IsServer ? m_NetworkManager.ServerClientId : m_NetworkManager.LocalClientId }); + // Clients send a notification back to the server they have completed the unload scene event if (!m_NetworkManager.IsServer) { SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); } - else //Second, server sets itself as having finished loading - { - if (SceneEventProgressTracking.ContainsKey(SceneEventData.SceneEventGuid)) - { - SceneEventProgressTracking[SceneEventData.SceneEventGuid].AddClientAsDone(m_NetworkManager.ServerClientId); - } - } + // This scene event is now considered "complete" s_IsSceneEventActive = false; } @@ -667,7 +675,7 @@ internal void UnloadAdditivelyLoadedScenes() /// /// Server side: /// Loads the scene name in either additive or single loading mode. - /// When applicable, the is delivered within the via the + /// When applicable, the is delivered within the via /// /// the name of the scene to be loaded /// ( means it was successful) @@ -832,7 +840,7 @@ private void OnSceneLoaded(string sceneName) /// private void OnServerLoadedScene(Scene scene) { - // Register in-scene placed NetworkObjects with the netcode + // Register in-scene placed NetworkObjects with spawn manager foreach (var keyValuePairByGlobalObjectIdHash in ScenePlacedObjects) { foreach (var keyValuePairBySceneHandle in keyValuePairByGlobalObjectIdHash.Value) @@ -853,42 +861,15 @@ private void OnServerLoadedScene(Scene scene) var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId; if (clientId != m_NetworkManager.ServerClientId) { - - uint sceneObjectsToSpawn = 0; - - foreach (var keyValuePairByGlobalObjectIdHash in ScenePlacedObjects) - { - foreach (var keyValuePairBySceneHandle in keyValuePairByGlobalObjectIdHash.Value) - { - if (keyValuePairBySceneHandle.Value.Observers.Contains(clientId)) - { - sceneObjectsToSpawn++; - } - } - } - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, new ulong[] { clientId }, k_NetworkUpdateStage); if (context != null) { + // Set the target client id that will be used during in scene NetworkObject serialization + SceneEventData.TargetClientId = clientId; + using (var nonNullContext = (InternalCommandContext)context) { SceneEventData.OnWrite(nonNullContext.NetworkWriter); - // Write number of scene objects to spawn - nonNullContext.NetworkWriter.WriteUInt32Packed(sceneObjectsToSpawn); - - foreach (var keyValuePairByGlobalObjectIdHash in ScenePlacedObjects) - { - foreach (var keyValuePairBySceneHandle in keyValuePairByGlobalObjectIdHash.Value) - { - if (keyValuePairBySceneHandle.Value.Observers.Contains(clientId)) - { - // Write our server relative scene handle for the NetworkObject being serialized - nonNullContext.NetworkWriter.WriteInt32Packed(keyValuePairBySceneHandle.Key); - // Serialize the NetworkObject - keyValuePairBySceneHandle.Value.SerializeSceneObject(nonNullContext.NetworkWriter, clientId); - } - } - } } } else @@ -923,19 +904,7 @@ private void OnServerLoadedScene(Scene scene) /// private void OnClientLoadedScene(Scene scene) { - using (var reader = PooledNetworkReader.Get(SceneEventData.InternalBuffer)) - { - var newObjectsCount = reader.ReadUInt32Packed(); - - for (int i = 0; i < newObjectsCount; i++) - { - // Set our relative scene to the NetworkObject - SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); - - // Deserialize the NetworkObject - NetworkObject.DeserializeSceneObject(SceneEventData.InternalBuffer as NetworkBuffer, reader, m_NetworkManager); - } - } + SceneEventData.DeserializeScenePlacedObjects(); SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete; SendSceneEventData(new ulong[] { m_NetworkManager.ServerClientId }); @@ -959,19 +928,14 @@ private void OnClientLoadedScene(Scene scene) /// Note: We write out all of the scenes to be loaded first and then all of the NetworkObjects that need to be /// synchronized. /// - /// newly joined client identifier - internal void SynchronizeNetworkObjects(ulong ownerClientId) + /// newly joined client identifier + internal void SynchronizeNetworkObjects(ulong clientId) { - foreach (var sobj in m_NetworkManager.SpawnManager.SpawnedObjectsList) - { - if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(ownerClientId)) - { - sobj.Observers.Add(ownerClientId); - } - } + // Update the clients + m_NetworkManager.SpawnManager.UpdateObservedNetworkObjects(clientId); ClientSynchEventData.InitializeForSynch(); - ClientSynchEventData.TargetClientId = ownerClientId; + ClientSynchEventData.TargetClientId = clientId; ClientSynchEventData.LoadSceneMode = LoadSceneMode.Single; var activeScene = SceneManager.GetActiveScene(); ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Sync; @@ -1000,7 +964,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) ClientSynchEventData.AddSpawnedNetworkObjects(); - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, new ulong[] { ownerClientId }, k_NetworkUpdateStage); + var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(k_MessageType, k_ChannelType, new ulong[] { clientId }, k_NetworkUpdateStage); if (context != null) { using (var nonNullContext = (InternalCommandContext)context) @@ -1013,7 +977,7 @@ internal void SynchronizeNetworkObjects(ulong ownerClientId) OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventType, - ClientId = ownerClientId + ClientId = clientId }); } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 7cd6786ac4..dbd38551b1 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -324,56 +324,115 @@ internal void OnWrite(NetworkWriter writer) writer.WriteUInt32Packed(SceneIndex); writer.WriteInt32Packed(SceneHandle); - if (SceneEventType == SceneEventTypes.S2C_Sync) + switch(SceneEventType) { - // Write the scenes we want to load, in the order we want to load them - writer.WriteUIntArrayPacked(ScenesToSynchronize.ToArray()); - writer.WriteUIntArrayPacked(SceneHandlesToSynchronize.ToArray()); - - // Store our current position in the stream to come back and say how much data we have written - var positionStart = writer.GetStream().Position; + case SceneEventTypes.S2C_Sync: + { + WriteSceneSynchronizationData(writer); + break; + } + case SceneEventTypes.S2C_Load: + { + SerializeScenePlacedObjects(writer); + break; + } + case SceneEventTypes.C2S_SyncComplete: + { + WriteClientSynchronizationResults(writer); + break; + } + case SceneEventTypes.S2C_ReSync: + { + WriteClientReSynchronizationData(writer); + break; + } + case SceneEventTypes.S2C_LoadComplete: + case SceneEventTypes.S2C_UnLoadComplete: + { + WriteSceneEventProgressDone(writer); + break; + } + } + } - // Size Place Holder -- Start - // !!NOTE!!: Since this is a placeholder to be set after we know how much we have written, - // for stream offset purposes this MUST not be a packed value! - writer.WriteUInt32(0); - var totalBytes = 0; + /// + /// Server Side: + /// Called at the end of an S2C_Load event once the scene is loaded and scene placed NetworkObjects + /// have been locally spawned + /// + internal void WriteSceneSynchronizationData(NetworkWriter writer) + { + // Write the scenes we want to load, in the order we want to load them + writer.WriteUIntArrayPacked(ScenesToSynchronize.ToArray()); + writer.WriteUIntArrayPacked(SceneHandlesToSynchronize.ToArray()); - // Write the number of NetworkObjects we are serializing - writer.WriteInt32Packed(m_NetworkObjectsSync.Count()); + // Store our current position in the stream to come back and say how much data we have written + var positionStart = writer.GetStream().Position; - foreach (var networkObject in m_NetworkObjectsSync) - { - var noStart = writer.GetStream().Position; - writer.WriteInt32Packed(networkObject.gameObject.scene.handle); - networkObject.SerializeSceneObject(writer, TargetClientId); - var noStop = writer.GetStream().Position; - totalBytes += (int)(noStop - noStart); - } + // Size Place Holder -- Start + // !!NOTE!!: Since this is a placeholder to be set after we know how much we have written, + // for stream offset purposes this MUST not be a packed value! + writer.WriteUInt32(0); + var totalBytes = 0; - // Size Place Holder -- End - var positionEnd = writer.GetStream().Position; - var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); - writer.GetStream().Position = positionStart; - // Write the total size written to the stream by NetworkObjects being serialized - writer.WriteUInt32(bytesWritten); - writer.GetStream().Position = positionEnd; - } + // Write the number of NetworkObjects we are serializing + writer.WriteInt32Packed(m_NetworkObjectsSync.Count()); - if (SceneEventType == SceneEventTypes.C2S_SyncComplete) + foreach (var networkObject in m_NetworkObjectsSync) { - WriteClientSynchronizationResults(writer); + var noStart = writer.GetStream().Position; + writer.WriteInt32Packed(networkObject.gameObject.scene.handle); + networkObject.SerializeSceneObject(writer, TargetClientId); + var noStop = writer.GetStream().Position; + totalBytes += (int)(noStop - noStart); } - if (SceneEventType == SceneEventTypes.S2C_ReSync) - { - WriteClientReSynchronizationData(writer); - } + // Size Place Holder -- End + var positionEnd = writer.GetStream().Position; + var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); + writer.GetStream().Position = positionStart; + // Write the total size written to the stream by NetworkObjects being serialized + writer.WriteUInt32(bytesWritten); + writer.GetStream().Position = positionEnd; + } - if (SceneEventType == SceneEventTypes.S2C_LoadComplete || SceneEventType == SceneEventTypes.S2C_UnLoadComplete) + /// + /// Server Side: + /// Called at the end of an S2C_Load event once the scene is loaded and scene placed NetworkObjects + /// have been locally spawned + /// Maximum number of objects that could theoretically be synchronized is 65536 + /// + internal void SerializeScenePlacedObjects(NetworkWriter writer) + { + var numberOfObjects = (ushort)0; + var stream = writer.GetStream(); + var headPosition = stream.Position; + + // Write our count place holder (must not be packed!) + writer.WriteUInt16(0); + + foreach (var keyValuePairByGlobalObjectIdHash in m_NetworkManager.SceneManager.ScenePlacedObjects) { - WriteSceneEventProgressDone(writer); + foreach (var keyValuePairBySceneHandle in keyValuePairByGlobalObjectIdHash.Value) + { + if (keyValuePairBySceneHandle.Value.Observers.Contains(TargetClientId)) + { + // Write our server relative scene handle for the NetworkObject being serialized + writer.WriteInt32Packed(keyValuePairBySceneHandle.Key); + // Serialize the NetworkObject + keyValuePairBySceneHandle.Value.SerializeSceneObject(writer, TargetClientId); + numberOfObjects++; + } + } } + + var tailPosition = stream.Position; + // Reposition to our count position to the head before we wrote our object count + stream.Position = headPosition; + // Write number of NetworkObjects serialized (must not be packed!) + writer.WriteUInt16(numberOfObjects); + // Set our position back to the tail + stream.Position = tailPosition; } /// @@ -413,36 +472,78 @@ internal void OnRead(NetworkReader reader) SceneIndex = reader.ReadUInt32Packed(); SceneHandle = reader.ReadInt32Packed(); - if (SceneEventType == SceneEventTypes.S2C_Sync) + switch(SceneEventType) { - m_NetworkObjectsSync.Clear(); + case SceneEventTypes.S2C_Sync: + { + CopySceneSyncrhonizationData(reader); + break; + } + case SceneEventTypes.C2S_SyncComplete: + { + CheckClientSynchronizationResults(reader); + break; + } - ScenesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); - SceneHandlesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); + case SceneEventTypes.S2C_ReSync: + { + ReadClientReSynchronizationData(reader); + break; + } + case SceneEventTypes.S2C_LoadComplete: + case SceneEventTypes.S2C_UnLoadComplete: + { + ReadSceneEventProgressDone(reader); + break; + } + } + } - var sizeToCopy = reader.ReadUInt32(); + /// + /// Client Side: + /// Prepares for a scene synchronization event and copies the scene synchronization data + /// into the internal buffer to be used throughout the synchronization process. + /// + /// + internal void CopySceneSyncrhonizationData(NetworkReader reader) + { + m_NetworkObjectsSync.Clear(); - using (var writer = PooledNetworkWriter.Get(InternalBuffer)) - { - writer.ReadAndWrite(reader, (long)sizeToCopy); - } + ScenesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); + SceneHandlesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); + InternalBuffer.Position = 0; - InternalBuffer.Position = 0; - } + // is not packed! + var sizeToCopy = reader.ReadUInt32(); - if (SceneEventType == SceneEventTypes.C2S_SyncComplete) + using (var writer = PooledNetworkWriter.Get(InternalBuffer)) { - CheckClientSynchronizationResults(reader); + writer.ReadAndWrite(reader, (long)sizeToCopy); } - if (SceneEventType == SceneEventTypes.S2C_ReSync) - { - ReadClientReSynchronizationData(reader); - } + InternalBuffer.Position = 0; + } - if (SceneEventType == SceneEventTypes.S2C_LoadComplete || SceneEventType == SceneEventTypes.S2C_UnLoadComplete) + /// + /// Client Side: + /// This needs to occur at the end of a S2C_Load event when the scene has finished loading + /// Maximum number of objects that could theoretically be synchronized is 65536 + /// + internal void DeserializeScenePlacedObjects() + { + using (var reader = PooledNetworkReader.Get(InternalBuffer)) { - ReadSceneEventProgressDone(reader); + // is not packed! + var newObjectsCount = reader.ReadUInt16(); + + for (ushort i = 0; i < newObjectsCount; i++) + { + // Set our relative scene to the NetworkObject + m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); + + // Deserialize the NetworkObject + NetworkObject.DeserializeSceneObject(InternalBuffer as NetworkBuffer, reader, m_NetworkManager); + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index d423a50ed4..b0dc34d90a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -686,5 +686,70 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } } } + + /// + /// This will write all client observable NetworkObjects to the 's stream while also + /// adding the client to each 's list only if + /// observable to the client. + /// Maximum number of objects that could theoretically be serialized is 65536 for now + /// + /// the client identifier used to determine if a spawned NetworkObject is observable + /// contains the writer used for serialization + internal void SerializeObservedNetworkObjects(ulong clientId, NetworkWriter writer) + { + var stream = writer.GetStream(); + var headPosition = stream.Position; + var numberOfObjects = (ushort)0; + + // Write our count place holder(must not be packed!) + writer.WriteUInt16(0); + + foreach (var sobj in SpawnedObjectsList) + { + if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(clientId)) + { + sobj.Observers.Add(clientId); + sobj.SerializeSceneObject(writer, clientId); + numberOfObjects++; + } + } + + var tailPosition = stream.Position; + // Reposition to our count position to the head before we wrote our object count + stream.Position = headPosition; + // Write number of NetworkObjects serialized (must not be packed!) + writer.WriteUInt16(numberOfObjects); + // Set our position back to the tail + stream.Position = tailPosition; + } + + /// + /// Updates all spawned for the specified client + /// Note: if the clientId is the server then it is observable to all spawned 's + /// + internal void UpdateObservedNetworkObjects(ulong clientId) + { + foreach (var sobj in SpawnedObjectsList) + { + if (sobj.CheckObjectVisibility == null || NetworkManager.IsServer) + { + if(!sobj.Observers.Contains(clientId)) + { + sobj.Observers.Add(clientId); + } + } + else + { + if (sobj.CheckObjectVisibility(clientId)) + { + sobj.Observers.Add(clientId); + } + else if (sobj.Observers.Contains(clientId)) + { + sobj.Observers.Remove(clientId); + } + } + } + } } } From 32022df36e6ed3402fcd0549897f73bd33663e7e Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 19 Aug 2021 20:25:32 -0500 Subject: [PATCH 100/106] refactor Removing the need to set NetworkSceneManager.IsTesting to true. Now NetworkSceneManager automatically detects if a unit test is running. --- .../SceneManagement/NetworkSceneManager.cs | 18 ++++++++++++------ .../NetworkObjectSceneSerializationTests.cs | 2 -- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 821b50ecb7..0798fa998e 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -95,11 +95,6 @@ public class NetworkSceneManager /// private static bool s_IsSceneEventActive = false; - /// - /// For multi-instance unit tests, set this to true if you are use the - /// - internal static bool IsTesting; - /// /// The delegate callback definition for scene event notifications /// For more details review over and @@ -169,6 +164,14 @@ public class NetworkSceneManager private const NetworkChannel k_ChannelType = NetworkChannel.Internal; private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.EarlyUpdate; + +#if UNITY_EDITOR + internal static bool IsNUnitTestRunning() + { + return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.ToLowerInvariant().StartsWith("nunit.framework")); + } +#endif + /// /// Constructor /// @@ -231,7 +234,8 @@ internal Scene GetAndAddNewlyLoadedSceneByName(string sceneName) /// internal void SetTheSceneBeingSynchronized(int serverSceneHandle) { - if (IsTesting) +#if UNITY_EDITOR + if (IsNUnitTestRunning()) { // If we were already set, then ignore if (SceneBeingSynchronized.IsValid() && SceneBeingSynchronized.isLoaded) @@ -241,6 +245,8 @@ internal void SetTheSceneBeingSynchronized(int serverSceneHandle) SceneBeingSynchronized = SceneManager.GetActiveScene(); return; } +#endif + var clientSceneHandle = serverSceneHandle; if (m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(serverSceneHandle)) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index dda03375b2..ace48cf9f0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -156,7 +156,6 @@ public void NetworkObjectSceneSerializationFailure() [SetUp] public void Setup() { - NetworkSceneManager.IsTesting = true; // Create, instantiate, and host NetworkManagerHelper.StartNetworkManager(out NetworkManager networkManager, NetworkManagerHelper.NetworkManagerOperatingMode.None); networkManager.NetworkConfig.EnableSceneManagement = true; @@ -167,7 +166,6 @@ public void Setup() [TearDown] public void TearDown() { - NetworkSceneManager.IsTesting = false; // Stop, shutdown, and destroy NetworkManagerHelper.ShutdownNetworkManager(); } From a2ffdf2c00309878348370925e2697482c765e4f Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 20 Aug 2021 21:56:24 -0500 Subject: [PATCH 101/106] refactor unit test check ------------------------------------------------------------- After further consideration, the best idea for the whole unit test issue is to: 1.) Not try to detect it ( silly idea of mine ) 2,) Make changes to the NetworkSceneManager's private m_ScenesLoaded property by renaming it to ScenesLoaded and making it internal. 3.) Update the one Unit test that was failing to populate ScenesLoaded and ServerSceneHandleToClientSceneHandle tables so the SetTheSceneBeingSynchronized method could properly be tested during the NetworkObjectSceneSerializationFailure test. It turns out my brain decided to take a "(dis)connect" day a few days too early. No need to check for unit tests at all for this PR. ------------------------------------------------------------- This also includes a minor adjustment for when the NetworkPrefabPool (test project script) unsubscribes from the NetworkSceneManager.OnSceneEvent to avoid a null access exception. --- .../SceneManagement/NetworkSceneManager.cs | 72 +++++++------------ .../NetworkObjectSceneSerializationTests.cs | 19 +++++ .../Tests/Manual/Scripts/NetworkPrefabPool.cs | 2 - 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 0798fa998e..aa845c1b41 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -5,6 +5,7 @@ using UnityEngine; using UnityEngine.SceneManagement; + namespace Unity.Netcode { /// @@ -130,7 +131,7 @@ public class NetworkSceneManager // Used to track which scenes are currently loaded // We store scenes as follows: [SceneName][SceneHandle][Scene] - private Dictionary> m_ScenesLoaded = new Dictionary>(); + internal Dictionary> ScenesLoaded = new Dictionary>(); /// /// Since Scene.handle is unique per client, we create a look-up table between the client and server @@ -164,14 +165,6 @@ public class NetworkSceneManager private const NetworkChannel k_ChannelType = NetworkChannel.Internal; private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.EarlyUpdate; - -#if UNITY_EDITOR - internal static bool IsNUnitTestRunning() - { - return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.ToLowerInvariant().StartsWith("nunit.framework")); - } -#endif - /// /// Constructor /// @@ -200,19 +193,19 @@ internal Scene GetAndAddNewlyLoadedSceneByName(string sceneName) var sceneLoaded = SceneManager.GetSceneAt(i); if (sceneLoaded.name == sceneName) { - if (m_ScenesLoaded.ContainsKey(sceneName)) + if (ScenesLoaded.ContainsKey(sceneName)) { - if (!m_ScenesLoaded[sceneName].ContainsKey(sceneLoaded.handle)) + if (!ScenesLoaded[sceneName].ContainsKey(sceneLoaded.handle)) { - m_ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); + ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); return sceneLoaded; } } else { // On a new entry we add the entry and scene then we are done. - m_ScenesLoaded.Add(sceneName, new Dictionary()); - m_ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); + ScenesLoaded.Add(sceneName, new Dictionary()); + ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); return sceneLoaded; } } @@ -234,24 +227,10 @@ internal Scene GetAndAddNewlyLoadedSceneByName(string sceneName) /// internal void SetTheSceneBeingSynchronized(int serverSceneHandle) { -#if UNITY_EDITOR - if (IsNUnitTestRunning()) - { - // If we were already set, then ignore - if (SceneBeingSynchronized.IsValid() && SceneBeingSynchronized.isLoaded) - { - return; - } - SceneBeingSynchronized = SceneManager.GetActiveScene(); - return; - } -#endif - - var clientSceneHandle = serverSceneHandle; - if (m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(serverSceneHandle)) + if (ServerSceneHandleToClientSceneHandle.ContainsKey(serverSceneHandle)) { - clientSceneHandle = m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle[serverSceneHandle]; + clientSceneHandle = ServerSceneHandleToClientSceneHandle[serverSceneHandle]; // If we were already set, then ignore if (SceneBeingSynchronized.IsValid() && SceneBeingSynchronized.isLoaded && SceneBeingSynchronized.handle == clientSceneHandle) { @@ -260,7 +239,7 @@ internal void SetTheSceneBeingSynchronized(int serverSceneHandle) // Find and set the scene currently being synchronized SceneBeingSynchronized = new Scene(); - foreach (var keyValuePairBySceneName in m_ScenesLoaded) + foreach (var keyValuePairBySceneName in ScenesLoaded) { if (keyValuePairBySceneName.Value.ContainsKey(clientSceneHandle)) { @@ -270,7 +249,7 @@ internal void SetTheSceneBeingSynchronized(int serverSceneHandle) if (!SceneBeingSynchronized.IsValid() || !SceneBeingSynchronized.isLoaded) { - throw new Exception($"[{nameof(NetworkSceneManager)}- {nameof(m_ScenesLoaded)}] Could not find the appropriate scene to set as being synchronized!"); + throw new Exception($"[{nameof(NetworkSceneManager)}- {nameof(ScenesLoaded)}] Could not find the appropriate scene to set as being synchronized!"); } } else @@ -502,7 +481,7 @@ public SceneEventProgressStatus UnloadScene(Scene scene) return sceneEventProgress.Status; } - if (!m_ScenesLoaded.ContainsKey(sceneName) || !m_ScenesLoaded[sceneName].ContainsKey(scene.handle)) + if (!ScenesLoaded.ContainsKey(sceneName) || !ScenesLoaded[sceneName].ContainsKey(scene.handle)) { Debug.LogError($"{nameof(UnloadScene)} internal error! {sceneName} with handle {scene.handle} is not within the internal scenes loaded dictionary!"); return SceneEventProgressStatus.InternalNetcodeError; @@ -511,16 +490,16 @@ public SceneEventProgressStatus UnloadScene(Scene scene) SceneEventData.SceneEventGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; - SceneEventData.SceneHandle = m_ScenesLoaded[sceneName][scene.handle].handle; + SceneEventData.SceneHandle = ScenesLoaded[sceneName][scene.handle].handle; // This will be the message we send to everyone when this scene event sceneEventProgress is complete sceneEventProgress.SceneEventType = SceneEventData.SceneEventTypes.S2C_UnLoadComplete; - m_ScenesLoaded[sceneName].Remove(scene.handle); + ScenesLoaded[sceneName].Remove(scene.handle); - if (m_ScenesLoaded[sceneName].Count == 0) + if (ScenesLoaded[sceneName].Count == 0) { - m_ScenesLoaded.Remove(sceneName); + ScenesLoaded.Remove(sceneName); } AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(scene); @@ -559,26 +538,27 @@ private void OnClientUnloadScene() } s_IsSceneEventActive = true; - if (m_ScenesLoaded.ContainsKey(sceneName)) + if (ScenesLoaded.ContainsKey(sceneName)) { if (!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) { throw new Exception("No server to scene handle exist!"); } var sceneHandle = ServerSceneHandleToClientSceneHandle[SceneEventData.SceneHandle]; - if (m_ScenesLoaded[sceneName].ContainsKey(sceneHandle)) + if (ScenesLoaded[sceneName].ContainsKey(sceneHandle)) { - var sceneUnload = SceneManager.UnloadSceneAsync(m_ScenesLoaded[sceneName][sceneHandle]); + + var sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneName][sceneHandle]); sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); - m_ScenesLoaded[sceneName].Remove(sceneHandle); + ScenesLoaded[sceneName].Remove(sceneHandle); // Remove our server to scene handle lookup ServerSceneHandleToClientSceneHandle.Remove(SceneEventData.SceneHandle); - if (m_ScenesLoaded[sceneName].Count == 0) + if (ScenesLoaded[sceneName].Count == 0) { - m_ScenesLoaded.Remove(sceneName); + ScenesLoaded.Remove(sceneName); } // Notify the local client that a scene is going to be unloaded @@ -657,7 +637,7 @@ internal void UnloadAdditivelyLoadedScenes() { // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). var currentActiveScene = SceneManager.GetActiveScene(); - foreach (var keyRootSceneEntry in m_ScenesLoaded) + foreach (var keyRootSceneEntry in ScenesLoaded) { foreach (var keyHandleEntry in keyRootSceneEntry.Value) { @@ -675,7 +655,7 @@ internal void UnloadAdditivelyLoadedScenes() } } // clear out our scenes loaded list - m_ScenesLoaded.Clear(); + ScenesLoaded.Clear(); } /// @@ -1176,7 +1156,7 @@ private void HandleClientSceneEvent(Stream stream) OnSceneEvent?.Invoke(new SceneEvent() { SceneEventType = SceneEventData.SceneEventType, - SceneName = m_NetworkManager.SceneManager.GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), + SceneName = GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex), ClientId = m_NetworkManager.ServerClientId, LoadSceneMode = SceneEventData.LoadSceneMode, ClientsThatCompleted = SceneEventData.ClientsCompleted, diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index ace48cf9f0..5a468b69e4 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -82,6 +82,24 @@ public void NetworkObjectSceneSerializationFailure() networkObjectsToTest.Add(gameObject); writer.WriteInt32Packed(networkObject.gameObject.scene.handle); + + // Handle populating the scenes loaded list + var scene = networkObject.gameObject.scene; + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey(scene.name)) + { + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.Add(scene.name, new Dictionary()); + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded[scene.name].ContainsKey(scene.handle)) + { + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded[scene.name].Add(scene.handle, scene); + } + } + + // Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(networkObject.gameObject.scene.handle)) + { + NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle.Add(networkObject.gameObject.scene.handle, networkObject.gameObject.scene.handle); + } + // Serialize the valid NetworkObject networkObject.SerializeSceneObject(writer, 0); @@ -117,6 +135,7 @@ public void NetworkObjectSceneSerializationFailure() invalidNetworkObjectCount++; } + NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); var deserializedNetworkObject = NetworkObject.DeserializeSceneObject(pooledBuffer, reader, NetworkManagerHelper.NetworkManagerObject); diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 803fb93bb0..bd62dc4c9d 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -130,8 +130,6 @@ private void OnUnloadScene() DeregisterCustomPrefabHandler(); CleanNetworkObjects(); - - NetworkManager.SceneManager.OnSceneEvent -= OnSceneEvent; } From 093bb75f318a74d180c06c24d4ec774f97de8df9 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 20 Aug 2021 22:12:26 -0500 Subject: [PATCH 102/106] refactor Removing the check for a null SpawnManager in NetworkObject.Despawn as this no longer seems to be an issue after the more recent merges in develop. --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 1d27bdc1cb..9b60dc3862 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -539,11 +539,7 @@ public void SpawnAsPlayerObject(ulong clientId, bool destroyWithScene = false) /// (true) the will be destroyed (false) the will persist after being despawned public void Despawn(bool destroy = false) { - // An edge case scenario can occur that will throw an exception due to the fact that SpawnManager is null. - if (NetworkManager.SpawnManager != null) - { - NetworkManager.SpawnManager.DespawnObject(this, destroy); - } + NetworkManager.SpawnManager.DespawnObject(this, destroy); } /// From 7d750620e373bc94d881bd21aa6bed909d1b9c8d Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 21 Aug 2021 16:34:20 -0500 Subject: [PATCH 103/106] refactor Took Fatih's suggestion regarding ScenesLoaded being over-complicated and now the ScenesLoaded is just a dictionary of scene handle keys and each key entry value is the Scene it is associated with. This did reduce some of the complexity in the unloading methods and the GetAndAddNewlyLoadedSceneByName method. Updated the NetworkObjectSceneSerializationFailure unit test to reflect the above changes. --- .../SceneManagement/NetworkSceneManager.cs | 143 +++++++----------- .../NetworkObjectSceneSerializationTests.cs | 9 +- 2 files changed, 61 insertions(+), 91 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index aa845c1b41..12d527083f 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -129,12 +129,19 @@ public class NetworkSceneManager /// internal Scene SceneBeingSynchronized; - // Used to track which scenes are currently loaded - // We store scenes as follows: [SceneName][SceneHandle][Scene] - internal Dictionary> ScenesLoaded = new Dictionary>(); + /// + /// Used to track which scenes are currently loaded + /// We store the scenes as [SceneHandle][Scene] in order to handle the loading and unloading of the same scene additively + /// Scene handle is only unique locally. So, clients depend upon the in order + /// to be able to know which specific scene instance the server is instructing the client to unload. + /// The client links the server scene handle to the client local scene handle upon a scene being loaded + /// + /// + internal Dictionary ScenesLoaded = new Dictionary(); /// - /// Since Scene.handle is unique per client, we create a look-up table between the client and server + /// Since Scene.handle is unique per client, we create a look-up table between the client and server to associate server unique scene + /// instances with client unique scene instances /// internal Dictionary ServerSceneHandleToClientSceneHandle = new Dictionary(); @@ -187,31 +194,20 @@ internal NetworkSceneManager(NetworkManager networkManager) /// internal Scene GetAndAddNewlyLoadedSceneByName(string sceneName) { - for (int i = 0; i < SceneManager.sceneCount; i++) { var sceneLoaded = SceneManager.GetSceneAt(i); if (sceneLoaded.name == sceneName) { - if (ScenesLoaded.ContainsKey(sceneName)) + if (!ScenesLoaded.ContainsKey(sceneLoaded.handle)) { - if (!ScenesLoaded[sceneName].ContainsKey(sceneLoaded.handle)) - { - ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); - return sceneLoaded; - } - } - else - { - // On a new entry we add the entry and scene then we are done. - ScenesLoaded.Add(sceneName, new Dictionary()); - ScenesLoaded[sceneName].Add(sceneLoaded.handle, sceneLoaded); + ScenesLoaded.Add(sceneLoaded.handle, sceneLoaded); return sceneLoaded; } } } - throw new Exception("Failed to find scene that was loaded!"); + throw new Exception($"Failed to find any loaded scene named {sceneName}!"); } /// @@ -237,16 +233,10 @@ internal void SetTheSceneBeingSynchronized(int serverSceneHandle) return; } - // Find and set the scene currently being synchronized - SceneBeingSynchronized = new Scene(); - foreach (var keyValuePairBySceneName in ScenesLoaded) - { - if (keyValuePairBySceneName.Value.ContainsKey(clientSceneHandle)) - { - SceneBeingSynchronized = keyValuePairBySceneName.Value[clientSceneHandle]; - } - } + // Get the scene currently being synchronized + SceneBeingSynchronized = ScenesLoaded.ContainsKey(clientSceneHandle) ? ScenesLoaded[clientSceneHandle] : new Scene(); + // If the scene was not found (invalid) or was not loaded then throw an exception if (!SceneBeingSynchronized.IsValid() || !SceneBeingSynchronized.isLoaded) { throw new Exception($"[{nameof(NetworkSceneManager)}- {nameof(ScenesLoaded)}] Could not find the appropriate scene to set as being synchronized!"); @@ -469,6 +459,7 @@ private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress public SceneEventProgressStatus UnloadScene(Scene scene) { var sceneName = scene.name; + var sceneHandle = scene.handle; if (!scene.isLoaded) { Debug.LogWarning($"{nameof(UnloadScene)} was called, but the scene {scene.name} is not currently loaded!"); @@ -481,7 +472,7 @@ public SceneEventProgressStatus UnloadScene(Scene scene) return sceneEventProgress.Status; } - if (!ScenesLoaded.ContainsKey(sceneName) || !ScenesLoaded[sceneName].ContainsKey(scene.handle)) + if (!ScenesLoaded.ContainsKey(sceneHandle)) { Debug.LogError($"{nameof(UnloadScene)} internal error! {sceneName} with handle {scene.handle} is not within the internal scenes loaded dictionary!"); return SceneEventProgressStatus.InternalNetcodeError; @@ -490,17 +481,12 @@ public SceneEventProgressStatus UnloadScene(Scene scene) SceneEventData.SceneEventGuid = sceneEventProgress.Guid; SceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload; SceneEventData.SceneIndex = SceneNameToIndex[sceneName]; - SceneEventData.SceneHandle = ScenesLoaded[sceneName][scene.handle].handle; + SceneEventData.SceneHandle = sceneHandle; // This will be the message we send to everyone when this scene event sceneEventProgress is complete sceneEventProgress.SceneEventType = SceneEventData.SceneEventTypes.S2C_UnLoadComplete; - ScenesLoaded[sceneName].Remove(scene.handle); - - if (ScenesLoaded[sceneName].Count == 0) - { - ScenesLoaded.Remove(sceneName); - } + ScenesLoaded.Remove(scene.handle); AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(scene); sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(); }; @@ -536,53 +522,43 @@ private void OnClientUnloadScene() return; } - s_IsSceneEventActive = true; - if (ScenesLoaded.ContainsKey(sceneName)) + if (!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) { - if (!ServerSceneHandleToClientSceneHandle.ContainsKey(SceneEventData.SceneHandle)) - { - throw new Exception("No server to scene handle exist!"); - } - var sceneHandle = ServerSceneHandleToClientSceneHandle[SceneEventData.SceneHandle]; - if (ScenesLoaded[sceneName].ContainsKey(sceneHandle)) - { + throw new Exception($"Client failed to unload scene {GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex)} " + + $"because we are missing the client scene handle due to the server scene handle {SceneEventData.SceneHandle} not being found!"); + } - var sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneName][sceneHandle]); + var sceneHandle = ServerSceneHandleToClientSceneHandle[SceneEventData.SceneHandle]; - sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); + if (!ScenesLoaded.ContainsKey(sceneHandle)) + { + // Error scene handle not found! + throw new Exception($"Client failed to unload scene {GetSceneNameFromNetcodeSceneIndex(SceneEventData.SceneIndex)} " + + $"because the client scene handle {sceneHandle} was not found in ScenesLoaded!"); + } + s_IsSceneEventActive = true; - ScenesLoaded[sceneName].Remove(sceneHandle); + var sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneHandle]); - // Remove our server to scene handle lookup - ServerSceneHandleToClientSceneHandle.Remove(SceneEventData.SceneHandle); - if (ScenesLoaded[sceneName].Count == 0) - { - ScenesLoaded.Remove(sceneName); - } + sceneUnload.completed += asyncOp2 => OnSceneUnloaded(); - // Notify the local client that a scene is going to be unloaded - OnSceneEvent?.Invoke(new SceneEvent() - { - AsyncOperation = sceneUnload, - SceneEventType = SceneEventData.SceneEventType, - LoadSceneMode = SceneEventData.LoadSceneMode, - SceneName = sceneName, - ClientId = m_NetworkManager.LocalClientId // Server sent this message to the client, but client is executing it - }); - } - else - { - // Error scene handle not found! - Debug.LogError("Server Scene Handle Not Found!"); - } - } - else + ScenesLoaded.Remove(sceneHandle); + + // Remove our server to scene handle lookup + ServerSceneHandleToClientSceneHandle.Remove(SceneEventData.SceneHandle); + + // Notify the local client that a scene is going to be unloaded + OnSceneEvent?.Invoke(new SceneEvent() { + AsyncOperation = sceneUnload, + SceneEventType = SceneEventData.SceneEventType, + LoadSceneMode = SceneEventData.LoadSceneMode, + SceneName = sceneName, + ClientId = m_NetworkManager.LocalClientId // Server sent this message to the client, but client is executing it + }); + - // Error scene not loaded! - Debug.LogError("Server Scene Handle Not Loaded!"); - } } /// @@ -637,21 +613,18 @@ internal void UnloadAdditivelyLoadedScenes() { // Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ). var currentActiveScene = SceneManager.GetActiveScene(); - foreach (var keyRootSceneEntry in ScenesLoaded) + foreach (var keyHandleEntry in ScenesLoaded) { - foreach (var keyHandleEntry in keyRootSceneEntry.Value) + if (currentActiveScene.name != keyHandleEntry.Value.name) { - if (currentActiveScene.name != keyHandleEntry.Value.name) + OnSceneEvent?.Invoke(new SceneEvent() { - OnSceneEvent?.Invoke(new SceneEvent() - { - AsyncOperation = SceneManager.UnloadSceneAsync(keyHandleEntry.Value), - SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload, - LoadSceneMode = LoadSceneMode.Additive, - SceneName = keyHandleEntry.Value.name, - ClientId = m_NetworkManager.ServerClientId - }); - } + AsyncOperation = SceneManager.UnloadSceneAsync(keyHandleEntry.Value), + SceneEventType = SceneEventData.SceneEventTypes.S2C_Unload, + LoadSceneMode = LoadSceneMode.Additive, + SceneName = keyHandleEntry.Value.name, + ClientId = m_NetworkManager.ServerClientId + }); } } // clear out our scenes loaded list diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index 5a468b69e4..6984baf2cc 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -85,13 +85,10 @@ public void NetworkObjectSceneSerializationFailure() // Handle populating the scenes loaded list var scene = networkObject.gameObject.scene; - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey(scene.name)) + + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey(scene.handle)) { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.Add(scene.name, new Dictionary()); - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded[scene.name].ContainsKey(scene.handle)) - { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded[scene.name].Add(scene.handle, scene); - } + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.Add(scene.handle, scene); } // Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value From 3958f15ae53b6c303c8608dd73a3fd6e645f53ce Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 21 Aug 2021 17:18:23 -0500 Subject: [PATCH 104/106] refactor minor Noticed one minor portion of the client loading process that really shouldn't be outside of SceneEventData. Removed 1 method call from NetworkSceneManager and 1 method from SceneEventData. --- .../SceneManagement/NetworkSceneManager.cs | 2 -- .../Runtime/SceneManagement/SceneEventData.cs | 21 ++++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 12d527083f..391fc67b43 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -694,8 +694,6 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc /// Stream data associated with the event private void OnClientSceneLoadingEvent(Stream objectStream) { - SceneEventData.CopyUnreadFromStream(objectStream); - if (!SceneIndexToString.TryGetValue(SceneEventData.SceneIndex, out string sceneName) || !RegisteredSceneNames.Contains(sceneName)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index dbd38551b1..fa9005a649 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -484,7 +484,15 @@ internal void OnRead(NetworkReader reader) CheckClientSynchronizationResults(reader); break; } - + case SceneEventTypes.S2C_Load: + { + // We store off the trailing in-scene placed serialized NetworkObject data to + // be processed once we are done loading. + InternalBuffer.Position = 0; + InternalBuffer.CopyUnreadFrom(reader.GetStream()); + InternalBuffer.Position = 0; + break; + } case SceneEventTypes.S2C_ReSync: { ReadClientReSynchronizationData(reader); @@ -735,17 +743,6 @@ internal void ReadSceneEventProgressDone(NetworkReader reader) } } - /// - /// Used to store data during an asynchronous scene loading event - /// - /// - internal void CopyUnreadFromStream(Stream stream) - { - InternalBuffer.Position = 0; - InternalBuffer.CopyUnreadFrom(stream); - InternalBuffer.Position = 0; - } - /// /// Used to release the pooled network buffer /// From 4b558a8c33bc33fda0d27b803456e646c95da736 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Sat, 21 Aug 2021 18:20:34 -0500 Subject: [PATCH 105/106] style Removed namespace no longer being used. (Standards check failed) --- .../Runtime/SceneManagement/SceneEventData.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index fa9005a649..0a18cd05d8 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System; -using System.IO; using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; From b8e9404c23ca17b483c7ffa01527997a1614d009 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 23 Aug 2021 18:21:00 +0100 Subject: [PATCH 106/106] `./standards.py --fix` --- .../Runtime/SceneManagement/SceneEventData.cs | 4 ++-- .../Runtime/Spawning/NetworkSpawnManager.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 0a18cd05d8..2144dfc79d 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -323,7 +323,7 @@ internal void OnWrite(NetworkWriter writer) writer.WriteUInt32Packed(SceneIndex); writer.WriteInt32Packed(SceneHandle); - switch(SceneEventType) + switch (SceneEventType) { case SceneEventTypes.S2C_Sync: { @@ -471,7 +471,7 @@ internal void OnRead(NetworkReader reader) SceneIndex = reader.ReadUInt32Packed(); SceneHandle = reader.ReadInt32Packed(); - switch(SceneEventType) + switch (SceneEventType) { case SceneEventTypes.S2C_Sync: { diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 3e743a3633..1e38683f7b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -741,7 +741,7 @@ internal void UpdateObservedNetworkObjects(ulong clientId) { if (sobj.CheckObjectVisibility == null || NetworkManager.IsServer) { - if(!sobj.Observers.Contains(clientId)) + if (!sobj.Observers.Contains(clientId)) { sobj.Observers.Add(clientId); }