From 505fe8f6297591b803ec69ef742fd7402d86a716 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Thu, 11 Mar 2021 01:30:40 +0000 Subject: [PATCH 1/9] fix: reset PlayerLoop back to DefaultPlayerLoop on ExitingPlayMode in the Editor --- .../Runtime/Core/NetworkUpdateLoop.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs index 1ef881a18f..7c56b5d1ab 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs @@ -4,6 +4,9 @@ using UnityEngine; using UnityEngine.LowLevel; using UnityEngine.PlayerLoop; +#if UNITY_EDITOR +using UnityEditor; +#endif namespace MLAPI { @@ -237,6 +240,16 @@ public static PlayerLoopSystem CreateLoopSystem() [RuntimeInitializeOnLoadMethod] private static void Initialize() { +#if UNITY_EDITOR + EditorApplication.playModeStateChanged += stateChange => + { + if (stateChange == PlayModeStateChange.ExitingPlayMode) + { + PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); + } + }; +#endif + var customPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); for (int i = 0; i < customPlayerLoop.subSystemList.Length; i++) @@ -361,4 +374,4 @@ private static void Initialize() PlayerLoop.SetPlayerLoop(customPlayerLoop); } } -} +} \ No newline at end of file From 3548f5fcdb11c200d2fc4703ca7e088e2c3b6482 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Thu, 11 Mar 2021 16:07:51 +0000 Subject: [PATCH 2/9] fix: only uninject NetworkPlayerLoop systems from the PlayerLoop when exiting PlayMode in the Editor --- .../Runtime/Core/NetworkUpdateLoop.cs | 102 +++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs index 7c56b5d1ab..102227d62a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs @@ -6,6 +6,7 @@ using UnityEngine.PlayerLoop; #if UNITY_EDITOR using UnityEditor; + #endif namespace MLAPI @@ -243,13 +244,23 @@ private static void Initialize() #if UNITY_EDITOR EditorApplication.playModeStateChanged += stateChange => { - if (stateChange == PlayModeStateChange.ExitingPlayMode) + switch (stateChange) { - PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); + case PlayModeStateChange.EnteredPlayMode: + InjectSystems(); + break; + case PlayModeStateChange.ExitingPlayMode: + UninjectSystems(); + break; } }; +#else + InjectSystems(); #endif + } + private static void InjectSystems() + { var customPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); for (int i = 0; i < customPlayerLoop.subSystemList.Length; i++) @@ -373,5 +384,92 @@ private static void Initialize() PlayerLoop.SetPlayerLoop(customPlayerLoop); } + +#if UNITY_EDITOR + private static void UninjectSystems() + { + var customPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + + for (int i = 0; i < customPlayerLoop.subSystemList.Length; i++) + { + var playerLoopSystem = customPlayerLoop.subSystemList[i]; + + if (playerLoopSystem.type == typeof(Initialization)) + { + var subsystems = playerLoopSystem.subSystemList.ToList(); + { + // try to find and remove `NetworkInitialization` + int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkInitialization)); + if (systemIndex > -1) subsystems.RemoveAt(systemIndex); + } + playerLoopSystem.subSystemList = subsystems.ToArray(); + } + else if (playerLoopSystem.type == typeof(EarlyUpdate)) + { + var subsystems = playerLoopSystem.subSystemList.ToList(); + { + // try to find and remove `NetworkEarlyUpdate` + int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkEarlyUpdate)); + if (systemIndex > -1) subsystems.RemoveAt(systemIndex); + } + playerLoopSystem.subSystemList = subsystems.ToArray(); + } + else if (playerLoopSystem.type == typeof(FixedUpdate)) + { + var subsystems = playerLoopSystem.subSystemList.ToList(); + { + // try to find and remove `NetworkFixedUpdate` + int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkFixedUpdate)); + if (systemIndex > -1) subsystems.RemoveAt(systemIndex); + } + playerLoopSystem.subSystemList = subsystems.ToArray(); + } + else if (playerLoopSystem.type == typeof(PreUpdate)) + { + var subsystems = playerLoopSystem.subSystemList.ToList(); + { + // try to find and remove `NetworkPreUpdate` + int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkPreUpdate)); + if (systemIndex > -1) subsystems.RemoveAt(systemIndex); + } + playerLoopSystem.subSystemList = subsystems.ToArray(); + } + else if (playerLoopSystem.type == typeof(Update)) + { + var subsystems = playerLoopSystem.subSystemList.ToList(); + { + // try to find and remove `NetworkUpdate` + int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkUpdate)); + if (systemIndex > -1) subsystems.RemoveAt(systemIndex); + } + playerLoopSystem.subSystemList = subsystems.ToArray(); + } + else if (playerLoopSystem.type == typeof(PreLateUpdate)) + { + var subsystems = playerLoopSystem.subSystemList.ToList(); + { + // try to find and remove `NetworkPreLateUpdate` + int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkPreLateUpdate)); + if (systemIndex > -1) subsystems.RemoveAt(systemIndex); + } + playerLoopSystem.subSystemList = subsystems.ToArray(); + } + else if (playerLoopSystem.type == typeof(PostLateUpdate)) + { + var subsystems = playerLoopSystem.subSystemList.ToList(); + { + // try to find and remove `NetworkPostLateUpdate` + int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkPostLateUpdate)); + if (systemIndex > -1) subsystems.RemoveAt(systemIndex); + } + playerLoopSystem.subSystemList = subsystems.ToArray(); + } + + customPlayerLoop.subSystemList[i] = playerLoopSystem; + } + + PlayerLoop.SetPlayerLoop(customPlayerLoop); + } +#endif } } \ No newline at end of file From cb1d918261549f94074d6e549b1b6da0e8329a2b Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Thu, 11 Mar 2021 17:09:37 +0000 Subject: [PATCH 3/9] comment in NetworkUpdateLoop.Initialize() method explaining the flow --- .../Runtime/Core/NetworkUpdateLoop.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs index 102227d62a..2057e2751b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs @@ -241,6 +241,16 @@ public static PlayerLoopSystem CreateLoopSystem() [RuntimeInitializeOnLoadMethod] private static void Initialize() { + // for standalone: + // we only `InjectSystems()` into `PlayerLoop` once by `[RuntimeInitializeOnLoadMethod]` + // but we do NOT `UninjectSystems()` since it is not necessary to do so + // because we will exit PlayMode when we quit from the standalone application + // + // for the editor: + // we do `InjectSystems()` into `PlayerLoop` once by `[RuntimeInitializeOnLoadMethod]` + // and we DO `UninjectSystems()` in the Editor after exiting PlayMode (stop playing) + // because we will still have `PlayerLoop` ticking subsystems until it gets reset again + #if UNITY_EDITOR EditorApplication.playModeStateChanged += stateChange => { From de21a54e27768a28ea2b133f1ecdf5e33346bf92 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Fri, 12 Mar 2021 02:05:30 +0000 Subject: [PATCH 4/9] implement tests for NetworkUpdateLoop InjectSystems() and UninjectSystems() --- .../Runtime/Core/NetworkUpdateLoop.cs | 5 +-- .../Tests/Runtime/NetworkUpdateLoopTests.cs | 39 ++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs index 2057e2751b..c55459a386 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs @@ -6,7 +6,6 @@ using UnityEngine.PlayerLoop; #if UNITY_EDITOR using UnityEditor; - #endif namespace MLAPI @@ -269,7 +268,7 @@ private static void Initialize() #endif } - private static void InjectSystems() + internal static void InjectSystems() { var customPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); @@ -396,7 +395,7 @@ private static void InjectSystems() } #if UNITY_EDITOR - private static void UninjectSystems() + internal static void UninjectSystems() { var customPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs index 187b037088..f02140fd4f 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs @@ -11,6 +11,43 @@ namespace MLAPI.RuntimeTests { public class NetworkUpdateLoopTests { + [UnityTest] + public IEnumerator InjectAndUninjectSystems() + { + // caching the current PlayerLoop (it will have NetworkUpdateLoop systems injected) + var cachedPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + { + // since current PlayerLoop already took NetworkUpdateLoop systems inside, + // we are going to swap it with the default PlayerLoop temporarily for testing + PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); + var oldPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + + NetworkUpdateLoop.InjectSystems(); + + int waitFrameNumber = Time.frameCount + 8; + yield return new WaitUntil(() => Time.frameCount >= waitFrameNumber); + + NetworkUpdateLoop.UninjectSystems(); + + var newPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + + // recursively compare old and new PlayerLoop systems and their subsystems + AssertAreEqualPlayerLoopSystems(newPlayerLoop, oldPlayerLoop); + } + // replace the current PlayerLoop with the cached PlayerLoop after the test + PlayerLoop.SetPlayerLoop(cachedPlayerLoop); + } + + private void AssertAreEqualPlayerLoopSystems(PlayerLoopSystem leftPlayerLoop, PlayerLoopSystem rightPlayerLoop) + { + Assert.AreEqual(leftPlayerLoop.type, rightPlayerLoop.type); + Assert.AreEqual(leftPlayerLoop.subSystemList?.Length ?? 0, rightPlayerLoop.subSystemList?.Length ?? 0); + for (int i = 0; i < (leftPlayerLoop.subSystemList?.Length ?? 0); i++) + { + AssertAreEqualPlayerLoopSystems(leftPlayerLoop.subSystemList[i], rightPlayerLoop.subSystemList[i]); + } + } + [Test] public void UpdateStageInjection() { @@ -369,4 +406,4 @@ public IEnumerator UpdateStagesMixed() } } } -} +} \ No newline at end of file From 8b1334c80c99e09be211cb9e4d8d85301ea6838a Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Fri, 12 Mar 2021 02:21:12 +0000 Subject: [PATCH 5/9] refactor NetworkUpdateLoop.UninjectSystems() --- .../Runtime/Core/NetworkUpdateLoop.cs | 92 +++++++++---------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs index c55459a386..573325a750 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs @@ -405,73 +405,59 @@ internal static void UninjectSystems() if (playerLoopSystem.type == typeof(Initialization)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // try to find and remove `NetworkInitialization` - int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkInitialization)); - if (systemIndex > -1) subsystems.RemoveAt(systemIndex); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + playerLoopSystem.subSystemList = + playerLoopSystem.subSystemList + .ToList() + .TryRemoveSystem(typeof(NetworkInitialization)) + .ToArray(); } else if (playerLoopSystem.type == typeof(EarlyUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // try to find and remove `NetworkEarlyUpdate` - int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkEarlyUpdate)); - if (systemIndex > -1) subsystems.RemoveAt(systemIndex); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + playerLoopSystem.subSystemList = + playerLoopSystem.subSystemList + .ToList() + .TryRemoveSystem(typeof(NetworkEarlyUpdate)) + .ToArray(); } else if (playerLoopSystem.type == typeof(FixedUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // try to find and remove `NetworkFixedUpdate` - int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkFixedUpdate)); - if (systemIndex > -1) subsystems.RemoveAt(systemIndex); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + playerLoopSystem.subSystemList = + playerLoopSystem.subSystemList + .ToList() + .TryRemoveSystem(typeof(NetworkFixedUpdate)) + .ToArray(); } else if (playerLoopSystem.type == typeof(PreUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // try to find and remove `NetworkPreUpdate` - int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkPreUpdate)); - if (systemIndex > -1) subsystems.RemoveAt(systemIndex); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + playerLoopSystem.subSystemList = + playerLoopSystem.subSystemList + .ToList() + .TryRemoveSystem(typeof(NetworkPreUpdate)) + .ToArray(); } else if (playerLoopSystem.type == typeof(Update)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // try to find and remove `NetworkUpdate` - int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkUpdate)); - if (systemIndex > -1) subsystems.RemoveAt(systemIndex); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + playerLoopSystem.subSystemList = + playerLoopSystem.subSystemList + .ToList() + .TryRemoveSystem(typeof(NetworkUpdate)) + .ToArray(); } else if (playerLoopSystem.type == typeof(PreLateUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // try to find and remove `NetworkPreLateUpdate` - int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkPreLateUpdate)); - if (systemIndex > -1) subsystems.RemoveAt(systemIndex); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + playerLoopSystem.subSystemList = + playerLoopSystem.subSystemList + .ToList() + .TryRemoveSystem(typeof(NetworkPreLateUpdate)) + .ToArray(); } else if (playerLoopSystem.type == typeof(PostLateUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // try to find and remove `NetworkPostLateUpdate` - int systemIndex = subsystems.FindIndex(s => s.type == typeof(NetworkPostLateUpdate)); - if (systemIndex > -1) subsystems.RemoveAt(systemIndex); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + playerLoopSystem.subSystemList = + playerLoopSystem.subSystemList + .ToList() + .TryRemoveSystem(typeof(NetworkPostLateUpdate)) + .ToArray(); } customPlayerLoop.subSystemList[i] = playerLoopSystem; @@ -479,6 +465,14 @@ internal static void UninjectSystems() PlayerLoop.SetPlayerLoop(customPlayerLoop); } + + private static List TryRemoveSystem(this List systemList, Type systemType) + { + int systemIndex = systemList.FindIndex(s => s.type == systemType); + if (systemIndex > -1) systemList.RemoveAt(systemIndex); + + return systemList; + } #endif } } \ No newline at end of file From 3ac43609e67fce132018b1beed80251182e5948d Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Sat, 13 Mar 2021 18:23:26 +0000 Subject: [PATCH 6/9] refactor NetworkUpdateLoop: more performance, less duplicates, better namings --- .../Runtime/Core/NetworkUpdateLoop.cs | 300 +++++++----------- .../Tests/Runtime/NetworkUpdateLoopTests.cs | 10 +- 2 files changed, 115 insertions(+), 195 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs index 573325a750..a127ed0eb7 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs @@ -1,12 +1,8 @@ using System; -using System.Linq; using System.Collections.Generic; using UnityEngine; using UnityEngine.LowLevel; using UnityEngine.PlayerLoop; -#if UNITY_EDITOR -using UnityEditor; -#endif namespace MLAPI { @@ -237,242 +233,166 @@ public static PlayerLoopSystem CreateLoopSystem() } } - [RuntimeInitializeOnLoadMethod] + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void Initialize() { - // for standalone: - // we only `InjectSystems()` into `PlayerLoop` once by `[RuntimeInitializeOnLoadMethod]` - // but we do NOT `UninjectSystems()` since it is not necessary to do so - // because we will exit PlayMode when we quit from the standalone application - // - // for the editor: - // we do `InjectSystems()` into `PlayerLoop` once by `[RuntimeInitializeOnLoadMethod]` - // and we DO `UninjectSystems()` in the Editor after exiting PlayMode (stop playing) - // because we will still have `PlayerLoop` ticking subsystems until it gets reset again - -#if UNITY_EDITOR - EditorApplication.playModeStateChanged += stateChange => + UnregisterLoopSystems(); + RegisterLoopSystems(); + } + + private enum LoopSystemPosition + { + After, + Before + } + + private static bool TryAddLoopSystem(ref PlayerLoopSystem parentLoopSystem, PlayerLoopSystem childLoopSystem, Type anchorSystemType, LoopSystemPosition loopSystemPosition) + { + int systemPosition = -1; + if (anchorSystemType != null) { - switch (stateChange) + for (int i = 0; i < parentLoopSystem.subSystemList.Length; i++) { - case PlayModeStateChange.EnteredPlayMode: - InjectSystems(); - break; - case PlayModeStateChange.ExitingPlayMode: - UninjectSystems(); + var subsystem = parentLoopSystem.subSystemList[i]; + if (subsystem.type == anchorSystemType) + { + systemPosition = loopSystemPosition == LoopSystemPosition.After ? i + 1 : i; break; + } } - }; -#else - InjectSystems(); -#endif + } + else + { + systemPosition = loopSystemPosition == LoopSystemPosition.After ? parentLoopSystem.subSystemList.Length : 0; + } + + if (systemPosition == -1) return false; + + var newSubsystemList = new PlayerLoopSystem[parentLoopSystem.subSystemList.Length + 1]; + + // begin = systemsBefore + systemsAfter + // + systemsBefore + Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition); + // + childSystem + newSubsystemList[systemPosition] = childLoopSystem; + // + systemsAfter + Array.Copy(parentLoopSystem.subSystemList, systemPosition, newSubsystemList, systemPosition + 1, parentLoopSystem.subSystemList.Length - systemPosition); + // end = systemsBefore + childSystem + systemsAfter + + parentLoopSystem.subSystemList = newSubsystemList; + + return true; + } + + private static bool TryRemoveLoopSystem(ref PlayerLoopSystem parentLoopSystem, Type childSystemType) + { + int systemPosition = -1; + for (int i = 0; i < parentLoopSystem.subSystemList.Length; i++) + { + var subsystem = parentLoopSystem.subSystemList[i]; + if (subsystem.type == childSystemType) + { + systemPosition = i; + break; + } + } + + if (systemPosition == -1) return false; + + var newSubsystemList = new PlayerLoopSystem[parentLoopSystem.subSystemList.Length - 1]; + + // begin = systemsBefore + childSystem + systemsAfter + // + systemsBefore + Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition); + // + systemsAfter + Array.Copy(parentLoopSystem.subSystemList, systemPosition + 1, newSubsystemList, systemPosition, parentLoopSystem.subSystemList.Length - systemPosition - 1); + // end = systemsBefore + systemsAfter + + parentLoopSystem.subSystemList = newSubsystemList; + + return true; } - internal static void InjectSystems() + internal static void RegisterLoopSystems() { - var customPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + var rootPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); - for (int i = 0; i < customPlayerLoop.subSystemList.Length; i++) + for (int i = 0; i < rootPlayerLoop.subSystemList.Length; i++) { - var playerLoopSystem = customPlayerLoop.subSystemList[i]; + ref var currentSystem = ref rootPlayerLoop.subSystemList[i]; - if (playerLoopSystem.type == typeof(Initialization)) + if (currentSystem.type == typeof(Initialization)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - // insert at the bottom of `Initialization` - subsystems.Add(NetworkInitialization.CreateLoopSystem()); - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + TryAddLoopSystem(ref currentSystem, NetworkInitialization.CreateLoopSystem(), null, LoopSystemPosition.After); } - else if (playerLoopSystem.type == typeof(EarlyUpdate)) + else if (currentSystem.type == typeof(EarlyUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - int subsystemCount = subsystems.Count; - for (int k = 0; k < subsystemCount; k++) - { - if (subsystems[k].type == typeof(EarlyUpdate.ScriptRunDelayedStartupFrame)) - { - // insert before `EarlyUpdate.ScriptRunDelayedStartupFrame` - subsystems.Insert(k, NetworkEarlyUpdate.CreateLoopSystem()); - break; - } - } - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + TryAddLoopSystem(ref currentSystem, NetworkEarlyUpdate.CreateLoopSystem(), typeof(EarlyUpdate.ScriptRunDelayedStartupFrame), LoopSystemPosition.Before); } - else if (playerLoopSystem.type == typeof(FixedUpdate)) + else if (currentSystem.type == typeof(FixedUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - int subsystemCount = subsystems.Count; - for (int k = 0; k < subsystemCount; k++) - { - if (subsystems[k].type == typeof(FixedUpdate.ScriptRunBehaviourFixedUpdate)) - { - // insert before `FixedUpdate.ScriptRunBehaviourFixedUpdate` - subsystems.Insert(k, NetworkFixedUpdate.CreateLoopSystem()); - break; - } - } - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + TryAddLoopSystem(ref currentSystem, NetworkFixedUpdate.CreateLoopSystem(), typeof(FixedUpdate.ScriptRunBehaviourFixedUpdate), LoopSystemPosition.Before); } - else if (playerLoopSystem.type == typeof(PreUpdate)) + else if (currentSystem.type == typeof(PreUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - int subsystemCount = subsystems.Count; - for (int k = 0; k < subsystemCount; k++) - { - if (subsystems[k].type == typeof(PreUpdate.PhysicsUpdate)) - { - // insert before `PreUpdate.PhysicsUpdate` - subsystems.Insert(k, NetworkPreUpdate.CreateLoopSystem()); - break; - } - } - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + TryAddLoopSystem(ref currentSystem, NetworkPreUpdate.CreateLoopSystem(), typeof(PreUpdate.PhysicsUpdate), LoopSystemPosition.Before); } - else if (playerLoopSystem.type == typeof(Update)) + else if (currentSystem.type == typeof(Update)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - int subsystemCount = subsystems.Count; - for (int k = 0; k < subsystemCount; k++) - { - if (subsystems[k].type == typeof(Update.ScriptRunBehaviourUpdate)) - { - // insert before `Update.ScriptRunBehaviourUpdate` - subsystems.Insert(k, NetworkUpdate.CreateLoopSystem()); - break; - } - } - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + TryAddLoopSystem(ref currentSystem, NetworkUpdate.CreateLoopSystem(), typeof(Update.ScriptRunBehaviourUpdate), LoopSystemPosition.Before); } - else if (playerLoopSystem.type == typeof(PreLateUpdate)) + else if (currentSystem.type == typeof(PreLateUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - int subsystemCount = subsystems.Count; - for (int k = 0; k < subsystemCount; k++) - { - if (subsystems[k].type == typeof(PreLateUpdate.ScriptRunBehaviourLateUpdate)) - { - // insert before `PreLateUpdate.ScriptRunBehaviourLateUpdate` - subsystems.Insert(k, NetworkPreLateUpdate.CreateLoopSystem()); - break; - } - } - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + TryAddLoopSystem(ref currentSystem, NetworkPreLateUpdate.CreateLoopSystem(), typeof(PreLateUpdate.ScriptRunBehaviourLateUpdate), LoopSystemPosition.Before); } - else if (playerLoopSystem.type == typeof(PostLateUpdate)) + else if (currentSystem.type == typeof(PostLateUpdate)) { - var subsystems = playerLoopSystem.subSystemList.ToList(); - { - int subsystemCount = subsystems.Count; - for (int k = 0; k < subsystemCount; k++) - { - if (subsystems[k].type == typeof(PostLateUpdate.PlayerSendFrameComplete)) - { - // insert after `PostLateUpdate.PlayerSendFrameComplete` - subsystems.Insert(k + 1, NetworkPostLateUpdate.CreateLoopSystem()); - break; - } - } - } - playerLoopSystem.subSystemList = subsystems.ToArray(); + TryAddLoopSystem(ref currentSystem, NetworkPostLateUpdate.CreateLoopSystem(), typeof(PostLateUpdate.PlayerSendFrameComplete), LoopSystemPosition.After); } - - customPlayerLoop.subSystemList[i] = playerLoopSystem; } - PlayerLoop.SetPlayerLoop(customPlayerLoop); + PlayerLoop.SetPlayerLoop(rootPlayerLoop); } -#if UNITY_EDITOR - internal static void UninjectSystems() + internal static void UnregisterLoopSystems() { - var customPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + var rootPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); - for (int i = 0; i < customPlayerLoop.subSystemList.Length; i++) + for (int i = 0; i < rootPlayerLoop.subSystemList.Length; i++) { - var playerLoopSystem = customPlayerLoop.subSystemList[i]; + ref var currentSystem = ref rootPlayerLoop.subSystemList[i]; - if (playerLoopSystem.type == typeof(Initialization)) + if (currentSystem.type == typeof(Initialization)) { - playerLoopSystem.subSystemList = - playerLoopSystem.subSystemList - .ToList() - .TryRemoveSystem(typeof(NetworkInitialization)) - .ToArray(); + TryRemoveLoopSystem(ref currentSystem, typeof(NetworkInitialization)); } - else if (playerLoopSystem.type == typeof(EarlyUpdate)) + else if (currentSystem.type == typeof(EarlyUpdate)) { - playerLoopSystem.subSystemList = - playerLoopSystem.subSystemList - .ToList() - .TryRemoveSystem(typeof(NetworkEarlyUpdate)) - .ToArray(); + TryRemoveLoopSystem(ref currentSystem, typeof(NetworkEarlyUpdate)); } - else if (playerLoopSystem.type == typeof(FixedUpdate)) + else if (currentSystem.type == typeof(FixedUpdate)) { - playerLoopSystem.subSystemList = - playerLoopSystem.subSystemList - .ToList() - .TryRemoveSystem(typeof(NetworkFixedUpdate)) - .ToArray(); + TryRemoveLoopSystem(ref currentSystem, typeof(NetworkFixedUpdate)); } - else if (playerLoopSystem.type == typeof(PreUpdate)) + else if (currentSystem.type == typeof(PreUpdate)) { - playerLoopSystem.subSystemList = - playerLoopSystem.subSystemList - .ToList() - .TryRemoveSystem(typeof(NetworkPreUpdate)) - .ToArray(); + TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPreUpdate)); } - else if (playerLoopSystem.type == typeof(Update)) + else if (currentSystem.type == typeof(Update)) { - playerLoopSystem.subSystemList = - playerLoopSystem.subSystemList - .ToList() - .TryRemoveSystem(typeof(NetworkUpdate)) - .ToArray(); + TryRemoveLoopSystem(ref currentSystem, typeof(NetworkUpdate)); } - else if (playerLoopSystem.type == typeof(PreLateUpdate)) + else if (currentSystem.type == typeof(PreLateUpdate)) { - playerLoopSystem.subSystemList = - playerLoopSystem.subSystemList - .ToList() - .TryRemoveSystem(typeof(NetworkPreLateUpdate)) - .ToArray(); + TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPreLateUpdate)); } - else if (playerLoopSystem.type == typeof(PostLateUpdate)) + else if (currentSystem.type == typeof(PostLateUpdate)) { - playerLoopSystem.subSystemList = - playerLoopSystem.subSystemList - .ToList() - .TryRemoveSystem(typeof(NetworkPostLateUpdate)) - .ToArray(); + TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPostLateUpdate)); } - - customPlayerLoop.subSystemList[i] = playerLoopSystem; } - PlayerLoop.SetPlayerLoop(customPlayerLoop); - } - - private static List TryRemoveSystem(this List systemList, Type systemType) - { - int systemIndex = systemList.FindIndex(s => s.type == systemType); - if (systemIndex > -1) systemList.RemoveAt(systemIndex); - - return systemList; + PlayerLoop.SetPlayerLoop(rootPlayerLoop); } -#endif } } \ No newline at end of file diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs index f02140fd4f..2194899ef8 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs @@ -12,9 +12,9 @@ namespace MLAPI.RuntimeTests public class NetworkUpdateLoopTests { [UnityTest] - public IEnumerator InjectAndUninjectSystems() + public IEnumerator RegisterAndUnregisterSystems() { - // caching the current PlayerLoop (it will have NetworkUpdateLoop systems injected) + // caching the current PlayerLoop (it will have NetworkUpdateLoop systems registered) var cachedPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); { // since current PlayerLoop already took NetworkUpdateLoop systems inside, @@ -22,12 +22,12 @@ public IEnumerator InjectAndUninjectSystems() PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); var oldPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); - NetworkUpdateLoop.InjectSystems(); + NetworkUpdateLoop.RegisterLoopSystems(); int waitFrameNumber = Time.frameCount + 8; yield return new WaitUntil(() => Time.frameCount >= waitFrameNumber); - NetworkUpdateLoop.UninjectSystems(); + NetworkUpdateLoop.UnregisterLoopSystems(); var newPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); @@ -49,7 +49,7 @@ private void AssertAreEqualPlayerLoopSystems(PlayerLoopSystem leftPlayerLoop, Pl } [Test] - public void UpdateStageInjection() + public void UpdateStageSystems() { var currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); for (int i = 0; i < currentPlayerLoop.subSystemList.Length; i++) From 1b6df9bd315621e99b7be0213900a5eee2ee73fc Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Sun, 14 Mar 2021 04:26:45 +0000 Subject: [PATCH 7/9] micro-optimization --- .../Runtime/Core/NetworkUpdateLoop.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs index a127ed0eb7..7eee1143b6 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs @@ -272,11 +272,11 @@ private static bool TryAddLoopSystem(ref PlayerLoopSystem parentLoopSystem, Play // begin = systemsBefore + systemsAfter // + systemsBefore - Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition); + if (systemPosition > 0) Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition); // + childSystem newSubsystemList[systemPosition] = childLoopSystem; // + systemsAfter - Array.Copy(parentLoopSystem.subSystemList, systemPosition, newSubsystemList, systemPosition + 1, parentLoopSystem.subSystemList.Length - systemPosition); + if (systemPosition < parentLoopSystem.subSystemList.Length) Array.Copy(parentLoopSystem.subSystemList, systemPosition, newSubsystemList, systemPosition + 1, parentLoopSystem.subSystemList.Length - systemPosition); // end = systemsBefore + childSystem + systemsAfter parentLoopSystem.subSystemList = newSubsystemList; @@ -303,9 +303,9 @@ private static bool TryRemoveLoopSystem(ref PlayerLoopSystem parentLoopSystem, T // begin = systemsBefore + childSystem + systemsAfter // + systemsBefore - Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition); + if (systemPosition > 0) Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition); // + systemsAfter - Array.Copy(parentLoopSystem.subSystemList, systemPosition + 1, newSubsystemList, systemPosition, parentLoopSystem.subSystemList.Length - systemPosition - 1); + if (systemPosition < parentLoopSystem.subSystemList.Length - 1) Array.Copy(parentLoopSystem.subSystemList, systemPosition + 1, newSubsystemList, systemPosition, parentLoopSystem.subSystemList.Length - systemPosition - 1); // end = systemsBefore + systemsAfter parentLoopSystem.subSystemList = newSubsystemList; From 6cf633106429097c3b9ef67eef8455eb8a8d21e3 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 15 Mar 2021 19:14:07 +0000 Subject: [PATCH 8/9] implement RegisterCustomLoopInTheMiddle test --- .../Tests/Runtime/NetworkUpdateLoopTests.cs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs index 2194899ef8..6881c3f03c 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs @@ -11,6 +11,31 @@ namespace MLAPI.RuntimeTests { public class NetworkUpdateLoopTests { + [Test] + public void RegisterCustomLoopInTheMiddle() + { + // caching the current PlayerLoop (to prevent side-effects on other tests) + var cachedPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + { + NetworkUpdateLoop.RegisterLoopSystems(); + + var curPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + int initSubsystemCount = curPlayerLoop.subSystemList[0].subSystemList.Length; + var newInitSubsystems = new PlayerLoopSystem[initSubsystemCount + 1]; + Array.Copy(curPlayerLoop.subSystemList[0].subSystemList, newInitSubsystems, initSubsystemCount); + newInitSubsystems[initSubsystemCount] = new PlayerLoopSystem { type = typeof(NetworkUpdateLoopTests) }; + curPlayerLoop.subSystemList[0].subSystemList = newInitSubsystems; + PlayerLoop.SetPlayerLoop(curPlayerLoop); + + NetworkUpdateLoop.UnregisterLoopSystems(); + + // our custom `PlayerLoopSystem` with the type of `NetworkUpdateLoopTests` should still exist + Assert.AreEqual(typeof(NetworkUpdateLoopTests), PlayerLoop.GetCurrentPlayerLoop().subSystemList[0].subSystemList.Last().type); + } + // replace the current PlayerLoop with the cached PlayerLoop after the test + PlayerLoop.SetPlayerLoop(cachedPlayerLoop); + } + [UnityTest] public IEnumerator RegisterAndUnregisterSystems() { @@ -24,8 +49,8 @@ public IEnumerator RegisterAndUnregisterSystems() NetworkUpdateLoop.RegisterLoopSystems(); - int waitFrameNumber = Time.frameCount + 8; - yield return new WaitUntil(() => Time.frameCount >= waitFrameNumber); + int nextFrameNumber = Time.frameCount + 1; + yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); NetworkUpdateLoop.UnregisterLoopSystems(); From 5895919c67741d8babd2bbb6edf17e74fab7f051 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 15 Mar 2021 19:24:47 +0000 Subject: [PATCH 9/9] RegisterCustomLoopInTheMiddle test to operate on a default PlayerLoop --- .../Tests/Runtime/NetworkUpdateLoopTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs index 6881c3f03c..ad796532ac 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs @@ -17,6 +17,10 @@ public void RegisterCustomLoopInTheMiddle() // caching the current PlayerLoop (to prevent side-effects on other tests) var cachedPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); { + // since current PlayerLoop already took NetworkUpdateLoop systems inside, + // we are going to swap it with the default PlayerLoop temporarily for testing + PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); + NetworkUpdateLoop.RegisterLoopSystems(); var curPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); @@ -45,6 +49,7 @@ public IEnumerator RegisterAndUnregisterSystems() // since current PlayerLoop already took NetworkUpdateLoop systems inside, // we are going to swap it with the default PlayerLoop temporarily for testing PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); + var oldPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); NetworkUpdateLoop.RegisterLoopSystems();