diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs
new file mode 100644
index 0000000000..f6d07bbc21
--- /dev/null
+++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs
@@ -0,0 +1,364 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.LowLevel;
+using UnityEngine.PlayerLoop;
+
+namespace MLAPI
+{
+ ///
+ /// Defines the required interface of a network update system being executed by the network update loop.
+ ///
+ public interface INetworkUpdateSystem
+ {
+ void NetworkUpdate(NetworkUpdateStage updateStage);
+ }
+
+ ///
+ /// Defines network update stages being executed by the network update loop.
+ ///
+ public enum NetworkUpdateStage : byte
+ {
+ Initialization = 1,
+ EarlyUpdate = 2,
+ FixedUpdate = 3,
+ PreUpdate = 4,
+ Update = 0, // Default
+ PreLateUpdate = 5,
+ PostLateUpdate = 6
+ }
+
+ ///
+ /// Represents the network update loop injected into low-level player loop in Unity.
+ ///
+ public static class NetworkUpdateLoop
+ {
+ private static readonly Dictionary> m_UpdateSystem_Sets;
+ private static readonly Dictionary m_UpdateSystem_Arrays;
+ private const int k_UpdateSystem_InitialArrayCapacity = 1024;
+
+ static NetworkUpdateLoop()
+ {
+ m_UpdateSystem_Sets = new Dictionary>();
+ m_UpdateSystem_Arrays = new Dictionary();
+
+ foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
+ {
+ m_UpdateSystem_Sets.Add(updateStage, new HashSet());
+ m_UpdateSystem_Arrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystem_InitialArrayCapacity]);
+ }
+ }
+
+ ///
+ /// Registers a network update system to be executed in all network update stages.
+ ///
+ public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
+ {
+ foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
+ {
+ RegisterNetworkUpdate(updateSystem, updateStage);
+ }
+ }
+
+ ///
+ /// Registers a network update system to be executed in a specific network update stage.
+ ///
+ public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
+ {
+ var sysSet = m_UpdateSystem_Sets[updateStage];
+ if (!sysSet.Contains(updateSystem))
+ {
+ sysSet.Add(updateSystem);
+
+ int setLen = sysSet.Count;
+ var sysArr = m_UpdateSystem_Arrays[updateStage];
+ int arrLen = sysArr.Length;
+
+ if (setLen > arrLen)
+ {
+ // double capacity
+ sysArr = m_UpdateSystem_Arrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2];
+ }
+
+ sysSet.CopyTo(sysArr);
+
+ if (setLen < arrLen)
+ {
+ // null terminator
+ sysArr[setLen] = null;
+ }
+ }
+ }
+
+ ///
+ /// Unregisters a network update system from all network update stages.
+ ///
+ public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
+ {
+ foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
+ {
+ UnregisterNetworkUpdate(updateSystem, updateStage);
+ }
+ }
+
+ ///
+ /// Unregisters a network update system from a specific network update stage.
+ ///
+ public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
+ {
+ var sysSet = m_UpdateSystem_Sets[updateStage];
+ if (sysSet.Contains(updateSystem))
+ {
+ sysSet.Remove(updateSystem);
+
+ int setLen = sysSet.Count;
+ var sysArr = m_UpdateSystem_Arrays[updateStage];
+ int arrLen = sysArr.Length;
+
+ sysSet.CopyTo(sysArr);
+
+ if (setLen < arrLen)
+ {
+ // null terminator
+ sysArr[setLen] = null;
+ }
+ }
+ }
+
+ ///
+ /// The current network update stage being executed.
+ ///
+ public static NetworkUpdateStage UpdateStage;
+
+ private static void RunNetworkUpdateStage(NetworkUpdateStage updateStage)
+ {
+ UpdateStage = updateStage;
+
+ var sysArr = m_UpdateSystem_Arrays[updateStage];
+ int arrLen = sysArr.Length;
+ for (int curIdx = 0; curIdx < arrLen; curIdx++)
+ {
+ var curSys = sysArr[curIdx];
+ if (curSys == null)
+ {
+ // null terminator
+ break;
+ }
+
+ curSys.NetworkUpdate(updateStage);
+ }
+ }
+
+ private struct NetworkInitialization
+ {
+ public static PlayerLoopSystem CreateLoopSystem()
+ {
+ return new PlayerLoopSystem
+ {
+ type = typeof(NetworkInitialization),
+ updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.Initialization)
+ };
+ }
+ }
+
+ private struct NetworkEarlyUpdate
+ {
+ public static PlayerLoopSystem CreateLoopSystem()
+ {
+ return new PlayerLoopSystem
+ {
+ type = typeof(NetworkEarlyUpdate),
+ updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.EarlyUpdate)
+ };
+ }
+ }
+
+ private struct NetworkFixedUpdate
+ {
+ public static PlayerLoopSystem CreateLoopSystem()
+ {
+ return new PlayerLoopSystem
+ {
+ type = typeof(NetworkFixedUpdate),
+ updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.FixedUpdate)
+ };
+ }
+ }
+
+ private struct NetworkPreUpdate
+ {
+ public static PlayerLoopSystem CreateLoopSystem()
+ {
+ return new PlayerLoopSystem
+ {
+ type = typeof(NetworkPreUpdate),
+ updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PreUpdate)
+ };
+ }
+ }
+
+ private struct NetworkUpdate
+ {
+ public static PlayerLoopSystem CreateLoopSystem()
+ {
+ return new PlayerLoopSystem
+ {
+ type = typeof(NetworkUpdate),
+ updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.Update)
+ };
+ }
+ }
+
+ private struct NetworkPreLateUpdate
+ {
+ public static PlayerLoopSystem CreateLoopSystem()
+ {
+ return new PlayerLoopSystem
+ {
+ type = typeof(NetworkPreLateUpdate),
+ updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PreLateUpdate)
+ };
+ }
+ }
+
+ private struct NetworkPostLateUpdate
+ {
+ public static PlayerLoopSystem CreateLoopSystem()
+ {
+ return new PlayerLoopSystem
+ {
+ type = typeof(NetworkPostLateUpdate),
+ updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PostLateUpdate)
+ };
+ }
+ }
+
+ [RuntimeInitializeOnLoadMethod]
+ private static void Initialize()
+ {
+ 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();
+ {
+ // insert at the bottom of `Initialization`
+ subsystems.Add(NetworkInitialization.CreateLoopSystem());
+ }
+ playerLoopSystem.subSystemList = subsystems.ToArray();
+ }
+ else if (playerLoopSystem.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();
+ }
+ else if (playerLoopSystem.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();
+ }
+ else if (playerLoopSystem.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();
+ }
+ else if (playerLoopSystem.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();
+ }
+ else if (playerLoopSystem.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();
+ }
+ else if (playerLoopSystem.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();
+ }
+
+ customPlayerLoop.subSystemList[i] = playerLoopSystem;
+ }
+
+ PlayerLoop.SetPlayerLoop(customPlayerLoop);
+ }
+ }
+}
\ No newline at end of file
diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoopSystem.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs.meta
similarity index 83%
rename from com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoopSystem.cs.meta
rename to com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs.meta
index bf2b85f2f2..403ee6e42d 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoopSystem.cs.meta
+++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoop.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 812d7f0d8b55ccd44820717e1ae1e9b1
+guid: 0cd9c24b9acfd4e82a71c795f37235c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoopSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoopSystem.cs
deleted file mode 100644
index 9b524ddf50..0000000000
--- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateLoopSystem.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using UnityEngine;
-
-
-namespace MLAPI
-{
- ///
- /// NetworkUpdateLoopBehaviour
- /// Derive from this class if you need to register a NetworkedBehaviour based class
- ///
- public class NetworkUpdateLoopBehaviour:NetworkedBehaviour, INetworkUpdateLoopSystem
- {
- protected virtual Action InternalRegisterNetworkUpdateStage(NetworkUpdateManager.NetworkUpdateStage stage )
- {
- return null;
- }
-
- public Action RegisterUpdate(NetworkUpdateManager.NetworkUpdateStage stage )
- {
- return InternalRegisterNetworkUpdateStage(stage);
- }
-
- protected void RegisterUpdateLoopSystem()
- {
- NetworkUpdateManager.NetworkLoopRegistration(this);
- }
-
- protected void OnNetworkLoopSystemRemove()
- {
- if(onNetworkLoopSystemDestroyed != null)
- {
- onNetworkLoopSystemDestroyed.Invoke(this);
- }
- }
-
- private Action onNetworkLoopSystemDestroyed;
-
- public void RegisterUpdateLoopSystemDestroyCallback(Action networkLoopSystemDestroyedCallback)
- {
- onNetworkLoopSystemDestroyed = networkLoopSystemDestroyedCallback;
- }
- }
-
- ///
- /// UpdateLoopBehaviour
- /// Derive from this class if you only require MonoBehaviour functionality
- ///
- public class UpdateLoopBehaviour:MonoBehaviour, INetworkUpdateLoopSystem
- {
- protected virtual Action InternalRegisterNetworkUpdateStage(NetworkUpdateManager.NetworkUpdateStage stage )
- {
- return null;
- }
-
- public Action RegisterUpdate(NetworkUpdateManager.NetworkUpdateStage stage )
- {
- return InternalRegisterNetworkUpdateStage(stage);
- }
-
- protected void RegisterUpdateLoopSystem()
- {
- NetworkUpdateManager.NetworkLoopRegistration(this);
- }
-
- protected void OnNetworkLoopSystemRemove()
- {
- if(onNetworkLoopSystemDestroyed != null)
- {
- onNetworkLoopSystemDestroyed.Invoke(this);
- }
- }
-
- private Action onNetworkLoopSystemDestroyed;
-
- public void RegisterUpdateLoopSystemDestroyCallback(Action networkLoopSystemDestroyedCallback)
- {
- onNetworkLoopSystemDestroyed = networkLoopSystemDestroyedCallback;
- }
- }
-
- ///
- /// GenericUpdateLoopSystem
- /// Derive from this class for generic (non-MonoBehaviour) classes
- ///
- public class GenericUpdateLoopSystem:INetworkUpdateLoopSystem
- {
- protected virtual Action InternalRegisterNetworkUpdateStage(NetworkUpdateManager.NetworkUpdateStage stage )
- {
- return null;
- }
-
- public Action RegisterUpdate(NetworkUpdateManager.NetworkUpdateStage stage )
- {
- return InternalRegisterNetworkUpdateStage(stage);
- }
-
- protected void RegisterUpdateLoopSystem()
- {
- NetworkUpdateManager.NetworkLoopRegistration(this);
- }
-
- protected void OnNetworkLoopSystemRemove()
- {
- if(onNetworkLoopSystemDestroyed != null)
- {
- onNetworkLoopSystemDestroyed.Invoke(this);
- }
- }
-
- private Action onNetworkLoopSystemDestroyed;
-
- public void RegisterUpdateLoopSystemDestroyCallback(Action networkLoopSystemDestroyedCallback)
- {
- onNetworkLoopSystemDestroyed = networkLoopSystemDestroyedCallback;
- }
- }
-
-
- ///
- /// INetworkUpdateLoopSystem
- /// Use this interface if you need a custom class beyond the scope of GenericUpdateLoopSystem, UpdateLoopBehaviour, and NetworkUpdateLoopBehaviour
- ///
- public interface INetworkUpdateLoopSystem
- {
- Action RegisterUpdate(NetworkUpdateManager.NetworkUpdateStage stage );
-
- void RegisterUpdateLoopSystemDestroyCallback(Action networkLoopSystemDestroyedCallbsack);
- }
-}
diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateManager.cs
deleted file mode 100644
index 0bb8e858e1..0000000000
--- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateManager.cs
+++ /dev/null
@@ -1,306 +0,0 @@
-/// About the Network Update Loop
-/// The NetworkUpdateEngine is a temporary solution for the network update loop implementation.
-/// This will be revised with a more robust and modular implementation in the near future.
-
-using System;
-using System.Text;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.PlayerLoop;
-using UnityEngine.LowLevel;
-
-namespace MLAPI
-{
- ///
- /// NetworkUpdateManager
- /// External public facing class for the registration of and processing of network updates
- ///
- public class NetworkUpdateManager
- {
- public enum NetworkUpdateStage
- {
- Default, //Will default to the UPDATE stage if no setting was made
- PreUpdate, //Invoked after EarlyUpdate.UnityWebRequestUpdate
- FixedUpdate, //Invoked after FixedUpdate.AudioFixedUpdate (prior to any physics being applied or simulated)
- Update, //Invoked after PreUpdate.UpdateVideo (just before the primary Update is invoked)
- LateUpdate //Invoked after PostLateUpdate.ProcessWebSendMessages (after all updates)
- }
-
- private static Dictionary> s_RegisteredUpdateLoopSystems = new Dictionary>();
-
-
- ///
- /// RegisterSystem
- /// Registers all of the defined update stages
- ///
- ///
- private static void RegisterSystem(Dictionary systems)
- {
- var def = PlayerLoop.GetCurrentPlayerLoop();
-
- foreach (var updateStage in systems)
- {
- var InsertAfterType = typeof(EarlyUpdate.UnityWebRequestUpdate);
- switch (updateStage.Key)
- {
- case NetworkUpdateStage.PreUpdate:
- {
- InsertAfterType = typeof(EarlyUpdate.UnityWebRequestUpdate);
- break;
- }
- case NetworkUpdateStage.FixedUpdate:
- {
- InsertAfterType = typeof(FixedUpdate.AudioFixedUpdate);
- break;
- }
- case NetworkUpdateStage.Update:
- {
- InsertAfterType = typeof(PreUpdate.UpdateVideo);
- break;
- }
- case NetworkUpdateStage.LateUpdate:
- {
- InsertAfterType = typeof(PostLateUpdate.ProcessWebSendMessages);
- break;
- }
- }
-
- InsertSystem(ref def, updateStage.Value, InsertAfterType);
- }
-
-#if UNITY_EDITOR
- PrintPlayerLoop(def);
-#endif
-
- PlayerLoop.SetPlayerLoop(def);
- }
-
- ///
- /// OnNetworkLoopSystemDestroyed
- /// This should be invoked by the registered INetworkUpdateLoopSystem when the class is being destroyed or it no longer wants to receive updates
- ///
- ///
- private static void OnNetworkLoopSystemDestroyed(INetworkUpdateLoopSystem networkLoopSystem)
- {
- if (s_RegisteredUpdateLoopSystems.ContainsKey(networkLoopSystem))
- {
- var def = PlayerLoop.GetCurrentPlayerLoop();
- foreach (KeyValuePair updateStage in s_RegisteredUpdateLoopSystems[networkLoopSystem])
- {
- if (!RemoveSystem(ref def, updateStage.Value))
- {
- Debug.LogWarning(updateStage.Value.type.Name + " tried to remove itself but no instsance was found!!!");
- }
- }
-
- s_RegisteredUpdateLoopSystems.Remove(networkLoopSystem);
-#if UNITY_EDITOR
- PrintPlayerLoop(def);
-#endif
- PlayerLoop.SetPlayerLoop(def);
- }
- }
-
- ///
- /// NetworkLoopRegistration
- /// This will register any class that has an INetworkUpdateLoopSystem interface assignment
- ///
- /// class instace to register
- public static void NetworkLoopRegistration(INetworkUpdateLoopSystem networkLoopSystem)
- {
- if (!s_RegisteredUpdateLoopSystems.ContainsKey(networkLoopSystem))
- {
- Dictionary RegisterPlayerLoopSystems = new Dictionary();
-
- foreach (NetworkUpdateStage stage in Enum.GetValues(typeof(NetworkUpdateStage)))
- {
- Action updateFunction = networkLoopSystem.RegisterUpdate(stage);
- if (updateFunction != null)
- {
- PlayerLoopSystem.UpdateFunction callback = new PlayerLoopSystem.UpdateFunction(updateFunction);
- PlayerLoopSystem stageLoop = new PlayerLoopSystem() { updateDelegate = callback, type = networkLoopSystem.GetType() };
- if (stageLoop.updateDelegate != null)
- {
- RegisterPlayerLoopSystems.Add(stage, stageLoop);
- }
- }
- }
-
- if (RegisterPlayerLoopSystems.Count > 0)
- {
- //Keep track of which systems are registered.
- s_RegisteredUpdateLoopSystems.Add(networkLoopSystem, RegisterPlayerLoopSystems);
-
- //Actually register all valid update stages for this system
- RegisterSystem(RegisterPlayerLoopSystems);
-
- //Register the callback to be used when the system is removed/deleted/destroyed
- networkLoopSystem.RegisterUpdateLoopSystemDestroyCallback(OnNetworkLoopSystemDestroyed);
- }
- }
- }
-
- ///
- /// RemoveSystem
- /// Recursively search for the given system type and remove system
- ///
- ///
- ///
- ///
- private static bool RemoveSystem(ref PlayerLoopSystem system, PlayerLoopSystem toRemove)
- {
- if (system.subSystemList == null)
- {
- return false;
- }
-
- for (int i = 0; i < system.subSystemList.Length; ++i)
- {
- if (system.subSystemList[i].type == toRemove.type)
- {
- RemoveSystemAt(ref system, toRemove);
- return true;
- }
- }
-
- for (var i = 0; i < system.subSystemList.Length; i++)
- {
- if (RemoveSystem(ref system.subSystemList[i], toRemove))
- {
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// RemoveSystemAt
- /// Copies the subSystemList of the given system, but excludes the specified PlayerLoopSystem from the list
- ///
- /// PlayerLoopSystem to be inserted into
- /// PlayerLoopSystem to insert
- private static void RemoveSystemAt(ref PlayerLoopSystem system, PlayerLoopSystem toRemove)
- {
- PlayerLoopSystem[] newSubSystems = new PlayerLoopSystem[system.subSystemList.Length - 1];
- for (int i = 0, newSystemIdx = 0; i < newSubSystems.Length - 1; ++i)
- {
- if (system.subSystemList[i].type != toRemove.type && system.subSystemList[i].updateFunction != toRemove.updateFunction)
- {
- newSubSystems[newSystemIdx++] = system.subSystemList[i];
- }
- }
-
- system.subSystemList = newSubSystems;
- }
-
- ///
- /// InsertSystem
- /// Recursively search for the given system type and insert the new system immediately afterwards
- ///
- /// PlayerLoopSystem to search
- /// PlayerLoopSystem to insert
- /// location to insert the PlayerLoopSystem
- ///
- private static bool InsertSystem(ref PlayerLoopSystem system, PlayerLoopSystem toInsert, Type insertAfter)
- {
- if (system.subSystemList == null)
- {
- return false;
- }
-
- for (int i = 0; i < system.subSystemList.Length; ++i)
- {
- if (system.subSystemList[i].type == insertAfter)
- {
- InsertSystemAt(ref system, toInsert, i + 1);
- return true;
- }
- }
-
- for (var i = 0; i < system.subSystemList.Length; i++)
- {
- if (InsertSystem(ref system.subSystemList[i], toInsert, insertAfter))
- {
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// InsertSystemAt
- /// Copies the subSystemList of the given system and inserts a new system at the given index
- ///
- /// PlayerLoopSystem to be inserted into
- /// PlayerLoopSystem to insert
- /// position to insert the PlayerLoopSystem
- private static void InsertSystemAt(ref PlayerLoopSystem system, PlayerLoopSystem toInsert, int pos)
- {
- PlayerLoopSystem[] newSubSystems = new PlayerLoopSystem[system.subSystemList.Length + 1];
- for (int i = 0, oldSystemIdx = 0; i < newSubSystems.Length; ++i)
- {
- if (i == pos)
- {
- newSubSystems[i] = toInsert;
- }
- else
- {
- newSubSystems[i] = system.subSystemList[oldSystemIdx++];
- }
- }
-
- system.subSystemList = newSubSystems;
- }
-
-#if UNITY_EDITOR
- ///
- /// PrintPlayerLoop
- /// Prints all PlayerLoopSystems within the PlayerLoopSystem provided
- ///
- /// PlayerLoopSystem
- private static void PrintPlayerLoop(PlayerLoopSystem pl)
- {
- var sb = new StringBuilder();
- RecursivePlayerLoopPrint(pl, sb, 0);
- Debug.Log(sb.ToString());
- }
-
- ///
- /// RecursivePlayerLoopPrint
- /// Recursively build the entire PlayerLoopSystem list
- ///
- /// PlayerLoopSystem to be added
- /// StringBuilder to add to
- /// Maximum recursion depth
- private static void RecursivePlayerLoopPrint(PlayerLoopSystem def, StringBuilder sb, int depth)
- {
- if (depth == 0)
- {
- sb.AppendLine("ROOT NODE");
- }
- else if (def.type != null)
- {
- for (int i = 0; i < depth; i++)
- {
- sb.Append("\t");
- }
-
- sb.AppendLine(def.type.Name);
- }
-
- if (def.subSystemList != null)
- {
- depth++;
- foreach (var s in def.subSystemList)
- {
- RecursivePlayerLoopPrint(s, sb, depth);
- }
-
- depth--;
- }
- }
-#endif
- }
-}
\ No newline at end of file
diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateManager.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateManager.cs.meta
deleted file mode 100644
index da4e1b466f..0000000000
--- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkUpdateManager.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 09a23caab021bc84ba7a47e790456334
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkedBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkedBehaviour.cs
index 6bf47c925e..40d19751be 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkedBehaviour.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkedBehaviour.cs
@@ -74,11 +74,6 @@ public BitSerializer __beginSendServerRpc(ServerRpcParams serverRpcParams, RpcDe
if (IsHost)
{
- if (serverRpcParams.Send.UpdateStage == NetworkUpdateManager.NetworkUpdateStage.Default)
- {
- serverRpcParams.Send.UpdateStage = NetworkUpdateManager.NetworkUpdateStage.Update;
- }
-
writer = rpcQueueContainer.BeginAddQueueItemToFrame(RpcQueueContainer.QueueItemType.ServerRpc, Time.realtimeSinceStartup, transportChannel, 0,
NetworkingManager.Singleton.ServerClientId, null, QueueHistoryFrame.QueueFrameType.Inbound, serverRpcParams.Send.UpdateStage);
@@ -92,7 +87,7 @@ public BitSerializer __beginSendServerRpc(ServerRpcParams serverRpcParams, RpcDe
else
{
writer = rpcQueueContainer.BeginAddQueueItemToFrame(RpcQueueContainer.QueueItemType.ServerRpc, Time.realtimeSinceStartup, transportChannel, 0,
- NetworkingManager.Singleton.ServerClientId, null, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+ NetworkingManager.Singleton.ServerClientId, null, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
if (!isUsingBatching)
{
writer.WriteBit(false); // Encrypted
@@ -103,16 +98,7 @@ public BitSerializer __beginSendServerRpc(ServerRpcParams serverRpcParams, RpcDe
writer.WriteUInt64Packed(NetworkId); // NetworkObjectId
writer.WriteUInt16Packed(GetBehaviourId()); // NetworkBehaviourId
-
- //Write the update stage in front of RPC related information
- if (serverRpcParams.Send.UpdateStage == NetworkUpdateManager.NetworkUpdateStage.Default)
- {
- writer.WriteUInt16Packed((ushort)NetworkUpdateManager.NetworkUpdateStage.Update);
- }
- else
- {
- writer.WriteUInt16Packed((ushort)serverRpcParams.Send.UpdateStage);
- }
+ writer.WriteByte((byte)serverRpcParams.Send.UpdateStage); // NetworkUpdateStage
return writer.Serializer;
}
@@ -132,13 +118,12 @@ public void __endSendServerRpc(BitSerializer serializer, ServerRpcParams serverR
var rpcQueueContainer = NetworkingManager.Singleton.rpcQueueContainer;
if (IsHost)
{
- rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, QueueHistoryFrame.QueueFrameType.Inbound, serverRpcParams.Send.UpdateStage == NetworkUpdateManager.NetworkUpdateStage.Default ? NetworkUpdateManager.NetworkUpdateStage.Update:serverRpcParams.Send.UpdateStage );
+ rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, QueueHistoryFrame.QueueFrameType.Inbound, serverRpcParams.Send.UpdateStage);
}
else
{
- rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+ rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
}
-
}
[Browsable(false)]
@@ -171,10 +156,6 @@ public BitSerializer __beginSendClientRpc(ClientRpcParams clientRpcParams, RpcDe
var ContainsServerClientId = ClientIds.Contains(NetworkingManager.Singleton.ServerClientId);
if (IsHost && ContainsServerClientId)
{
- if (clientRpcParams.Send.UpdateStage == NetworkUpdateManager.NetworkUpdateStage.Default)
- {
- clientRpcParams.Send.UpdateStage = NetworkUpdateManager.NetworkUpdateStage.Update;
- }
//Always write to the next frame's inbound queue
writer = rpcQueueContainer.BeginAddQueueItemToFrame(RpcQueueContainer.QueueItemType.ClientRpc, Time.realtimeSinceStartup, transportChannel, 0,
NetworkingManager.Singleton.ServerClientId, null, QueueHistoryFrame.QueueFrameType.Inbound, clientRpcParams.Send.UpdateStage);
@@ -187,7 +168,7 @@ public BitSerializer __beginSendClientRpc(ClientRpcParams clientRpcParams, RpcDe
//Switch to the outbound queue
writer = rpcQueueContainer.BeginAddQueueItemToFrame(RpcQueueContainer.QueueItemType.ClientRpc, Time.realtimeSinceStartup, Channel.ReliableRPC, 0, NetworkId,
- ClientIds, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+ ClientIds, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
if (!isUsingBatching)
{
@@ -209,7 +190,7 @@ public BitSerializer __beginSendClientRpc(ClientRpcParams clientRpcParams, RpcDe
else
{
writer = rpcQueueContainer.BeginAddQueueItemToFrame(RpcQueueContainer.QueueItemType.ClientRpc, Time.realtimeSinceStartup, transportChannel, 0, NetworkId,
- ClientIds, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+ ClientIds, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
if (!isUsingBatching)
{
@@ -221,16 +202,7 @@ public BitSerializer __beginSendClientRpc(ClientRpcParams clientRpcParams, RpcDe
writer.WriteUInt64Packed(NetworkId); // NetworkObjectId
writer.WriteUInt16Packed(GetBehaviourId()); // NetworkBehaviourId
-
- // Write the update stage in front of RPC related information
- if (clientRpcParams.Send.UpdateStage == NetworkUpdateManager.NetworkUpdateStage.Default)
- {
- writer.WriteUInt16Packed((ushort)NetworkUpdateManager.NetworkUpdateStage.Update);
- }
- else
- {
- writer.WriteUInt16Packed((ushort)clientRpcParams.Send.UpdateStage);
- }
+ writer.WriteByte((byte)clientRpcParams.Send.UpdateStage); // NetworkUpdateStage
return writer.Serializer;
}
@@ -256,19 +228,16 @@ public void __endSendClientRpc(BitSerializer serializer, ClientRpcParams clientR
{
ClientIds = NetworkingManager.Singleton.ConnectedClientsList.Select(c => c.ClientId).ToArray();
}
- var ContainsServerClientId = ClientIds.Contains(NetworkingManager.Singleton.ServerClientId);
+ var ContainsServerClientId = ClientIds.Contains(NetworkingManager.Singleton.ServerClientId);
if (ContainsServerClientId && ClientIds.Length == 1)
{
- if (clientRpcParams.Send.UpdateStage == NetworkUpdateManager.NetworkUpdateStage.Default)
- {
- clientRpcParams.Send.UpdateStage = NetworkUpdateManager.NetworkUpdateStage.Update;
- }
rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, QueueHistoryFrame.QueueFrameType.Inbound, clientRpcParams.Send.UpdateStage);
return;
}
}
- rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+
+ rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
}
///
diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkingManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkingManager.cs
index b677cc9854..692bd98d22 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkingManager.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkingManager.cs
@@ -35,7 +35,7 @@ namespace MLAPI
/// The main component of the library
///
[AddComponentMenu("MLAPI/NetworkingManager", -100)]
- public class NetworkingManager : UpdateLoopBehaviour
+ public class NetworkingManager : MonoBehaviour, INetworkUpdateSystem
{
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -634,26 +634,41 @@ public void SetSingleton()
OnSingletonReady();
}
+ private void Awake()
+ {
+ rpcQueueContainer = new RpcQueueContainer(false);
+ //Note: Since frame history is not being used, this is set to 0
+ //To test frame history, increase the number to (n) where n > 0
+ rpcQueueContainer.Initialize(0);
+ }
+
private void OnEnable()
{
if (Singleton != null && Singleton != this)
{
- Destroy(this.gameObject);
- }
- else
- {
- RegisterUpdateLoopSystem();
- SetSingleton();
- if (DontDestroy)
- DontDestroyOnLoad(gameObject);
- if (RunInBackground)
- Application.runInBackground = true;
+ Destroy(gameObject);
+ return;
}
+
+ // Register INetworkUpdateSystem
+ this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate);
+ this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
+
+ SetSingleton();
+ if (DontDestroy)
+ DontDestroyOnLoad(gameObject);
+ if (RunInBackground)
+ Application.runInBackground = true;
+ }
+
+ private void OnDisable()
+ {
+ // Unregister INetworkUpdateSystem
+ this.UnregisterAllNetworkUpdates();
}
private void OnDestroy()
{
- OnNetworkLoopSystemRemove();
//NSS: This is ok to leave this check here
rpcQueueContainer?.Shutdown();
@@ -686,42 +701,18 @@ public void Shutdown()
}
}
- ///
- /// InternalRegisterNetworkUpdateStage
- /// Registers all pertinent update stages for NetworkingManager
- ///
- /// update stage to get callback for
- ///
- protected override Action InternalRegisterNetworkUpdateStage(NetworkUpdateManager.NetworkUpdateStage stage)
+ // INetworkUpdateSystem
+ public void NetworkUpdate(NetworkUpdateStage updateStage)
{
- Action updateStageCallback = null;
- switch (stage)
+ switch (updateStage)
{
- case NetworkUpdateManager.NetworkUpdateStage.PreUpdate:
- {
- updateStageCallback = NetworkPreUpdate;
- break;
- }
- case NetworkUpdateManager.NetworkUpdateStage.Update:
- {
- updateStageCallback = NetworkUpdate;
- break;
- }
+ case NetworkUpdateStage.EarlyUpdate:
+ OnNetworkEarlyUpdate();
+ break;
+ case NetworkUpdateStage.PreUpdate:
+ OnNetworkPreUpdate();
+ break;
}
-
- return updateStageCallback;
- }
-
- ///
- /// Awake
- /// Currently this only creates the RpcQueueContainer instance and initializes it.
- ///
- private void Awake()
- {
- rpcQueueContainer = new RpcQueueContainer(false);
- //Note: Since frame history is not being used, this is set to 0
- //To test frame history, increase the number to (n) where n > 0
- rpcQueueContainer.Initialize(0);
}
private float m_LastReceiveTickTime;
@@ -729,11 +720,7 @@ private void Awake()
private float m_EventOvershootCounter;
private float m_LastTimeSyncTime;
- ///
- /// NetworkPreUpdate:
- /// Mostly for handling the receiving of RPCs
- ///
- private void NetworkPreUpdate()
+ private void OnNetworkEarlyUpdate()
{
if (IsListening)
{
@@ -774,11 +761,7 @@ private void NetworkPreUpdate()
}
}
- ///
- /// NetworkUpdate:
- /// Primarily handles all remaining messages, network variable/behavior updates
- ///
- private void NetworkUpdate()
+ private void OnNetworkPreUpdate()
{
if (IsListening)
{
@@ -1253,7 +1236,7 @@ public static void InvokeRpc(RpcFrameQueueItem queueItem)
#endif
var networkObjectId = queueItem.streamReader.ReadUInt64Packed();
var networkBehaviourId = queueItem.streamReader.ReadUInt16Packed();
- var networkUpdateStage = queueItem.streamReader.ReadUInt16Packed();
+ var networkUpdateStage = queueItem.streamReader.ReadByteDirect();
var networkMethodId = queueItem.streamReader.ReadUInt32Packed();
if (__ntable.ContainsKey(networkMethodId))
@@ -1272,7 +1255,7 @@ public static void InvokeRpc(RpcFrameQueueItem queueItem)
{
Receive = new ServerRpcReceiveParams
{
- UpdateStage = (NetworkUpdateManager.NetworkUpdateStage)networkUpdateStage,
+ UpdateStage = (NetworkUpdateStage)networkUpdateStage,
SenderClientId = queueItem.networkId
}
};
@@ -1282,7 +1265,7 @@ public static void InvokeRpc(RpcFrameQueueItem queueItem)
{
Receive = new ClientRpcReceiveParams
{
- UpdateStage = (NetworkUpdateManager.NetworkUpdateStage)networkUpdateStage
+ UpdateStage = (NetworkUpdateStage)networkUpdateStage
}
};
break;
diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcParams.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcParams.cs
index 466acd6353..e12b8a1a7a 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcParams.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcParams.cs
@@ -4,12 +4,12 @@ namespace MLAPI.Messaging
{
public struct ServerRpcSendParams
{
- public NetworkUpdateManager.NetworkUpdateStage UpdateStage;
+ public NetworkUpdateStage UpdateStage;
}
public struct ServerRpcReceiveParams
{
- public NetworkUpdateManager.NetworkUpdateStage UpdateStage;
+ public NetworkUpdateStage UpdateStage;
public ulong SenderClientId;
}
@@ -21,13 +21,13 @@ public struct ServerRpcParams
public struct ClientRpcSendParams
{
- public NetworkUpdateManager.NetworkUpdateStage UpdateStage;
+ public NetworkUpdateStage UpdateStage;
public ulong[] TargetClientIds;
}
public struct ClientRpcReceiveParams
{
- public NetworkUpdateManager.NetworkUpdateStage UpdateStage;
+ public NetworkUpdateStage UpdateStage;
}
public struct ClientRpcParams
diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/RPCQueue.meta b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue.meta
similarity index 100%
rename from com.unity.multiplayer.mlapi/Runtime/Messaging/RPCQueue.meta
rename to com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue.meta
diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/QueueHistoryFrame.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/QueueHistoryFrame.cs
index 99ecd0bdd5..55b3673f97 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/QueueHistoryFrame.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/QueueHistoryFrame.cs
@@ -34,7 +34,7 @@ public enum QueueFrameType
private readonly QueueFrameType m_QueueFrameType;
private int m_MaximumClients;
private long m_CurrentStreamSizeMark;
- private NetworkUpdateManager.NetworkUpdateStage m_StreamUpdateStage; //Update stage specific to RPCs (typically inbound has most potential for variation)
+ private NetworkUpdateStage m_StreamUpdateStage; //Update stage specific to RPCs (typically inbound has most potential for variation)
private const int m_MaxStreamBounds = 131072;
private const int m_MinStreamBounds = 0;
@@ -244,7 +244,7 @@ public void CloseQueue()
/// QueueHistoryFrame Constructor
///
/// type of queue history frame (Inbound/Outbound)
- public QueueHistoryFrame(QueueFrameType queueType, NetworkUpdateManager.NetworkUpdateStage updateStage, int maxClients = 512)
+ public QueueHistoryFrame(QueueFrameType queueType, NetworkUpdateStage updateStage, int maxClients = 512)
{
m_MaximumClients = maxClients;
m_QueueFrameType = queueType;
diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcFrameQueueItem.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcFrameQueueItem.cs
index 28ccec99f8..8fdfe27083 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcFrameQueueItem.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcFrameQueueItem.cs
@@ -14,7 +14,7 @@ namespace MLAPI.Messaging
///
public struct RpcFrameQueueItem
{
- public NetworkUpdateManager.NetworkUpdateStage updateStage;
+ public NetworkUpdateStage updateStage;
public RpcQueueContainer.QueueItemType queueItemType;
public SecuritySendFlags sendFlags;
public ulong networkId; //Sender's network Identifier
diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueContainer.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueContainer.cs
index 32cfeca200..359768c914 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueContainer.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueContainer.cs
@@ -11,7 +11,7 @@ namespace MLAPI.Messaging
/// RpcQueueContainer
/// Handles the management of an Rpc Queue
///
- public class RpcQueueContainer : GenericUpdateLoopSystem
+ public class RpcQueueContainer : INetworkUpdateSystem
{
private const int k_MinQueueHistory = 2; //We need a minimum of 2 queue history buffers in order to properly handle looping back Rpcs when a host
@@ -31,10 +31,9 @@ public enum RpcQueueProcessingTypes
Receive,
}
- //Inbound and Outbound QueueHistoryFrames
- private readonly Dictionary>> QueueHistory =
- new Dictionary>>();
-
+ // Inbound and Outbound QueueHistoryFrames
+ private readonly Dictionary>> QueueHistory =
+ new Dictionary>>();
private RpcQueueProcessor m_RpcQueueProcessor;
@@ -57,77 +56,17 @@ public void EnableBatchedRpcs(bool isbatchingEnabled)
m_IsNotUsingBatching = !isbatchingEnabled;
}
- ///
- /// PreUpdateStage
- /// Predefined internal network loop update system action
- ///
- void PreUpdateStage()
+ // INetworkUpdateSystem
+ public void NetworkUpdate(NetworkUpdateStage updateStage)
{
- ProcessAndFlushRPCQueue(RpcQueueContainer.RpcQueueProcessingTypes.Receive, NetworkUpdateManager.NetworkUpdateStage.PreUpdate);
- }
+ ProcessAndFlushRPCQueue(RpcQueueProcessingTypes.Receive, updateStage);
- ///
- /// FixedUpdateStage
- /// Predefined internal network loop update system action
- ///
- void FixedUpdateStage()
- {
- ProcessAndFlushRPCQueue(RpcQueueContainer.RpcQueueProcessingTypes.Receive, NetworkUpdateManager.NetworkUpdateStage.FixedUpdate);
- }
-
- ///
- /// UpdateStage
- /// Predefined internal network loop update system action
- ///
- void UpdateStage()
- {
- ProcessAndFlushRPCQueue(RpcQueueContainer.RpcQueueProcessingTypes.Receive, NetworkUpdateManager.NetworkUpdateStage.Update);
- }
-
- ///
- /// LateUpdateStage
- /// Predefined internal network loop update system action
- ///
- void LateUpdateStage()
- {
- ProcessAndFlushRPCQueue(RpcQueueContainer.RpcQueueProcessingTypes.Receive, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
- ProcessAndFlushRPCQueue(RpcQueueContainer.RpcQueueProcessingTypes.Send, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
- }
-
- protected override Action InternalRegisterNetworkUpdateStage(NetworkUpdateManager.NetworkUpdateStage stage)
- {
- Action updateStageAction = null;
- if (!m_ProcessUpdateStagesExternally)
+ if (updateStage == NetworkUpdateStage.PostLateUpdate)
{
- switch (stage)
- {
- case NetworkUpdateManager.NetworkUpdateStage.PreUpdate:
- {
- updateStageAction = PreUpdateStage;
- break;
- }
- case NetworkUpdateManager.NetworkUpdateStage.FixedUpdate:
- {
- updateStageAction = FixedUpdateStage;
- break;
- }
- case NetworkUpdateManager.NetworkUpdateStage.Update:
- {
- updateStageAction = UpdateStage;
- break;
- }
- case NetworkUpdateManager.NetworkUpdateStage.LateUpdate:
- {
- updateStageAction = LateUpdateStage;
- break;
- }
- }
+ ProcessAndFlushRPCQueue(RpcQueueProcessingTypes.Send, updateStage);
}
-
- return updateStageAction;
}
-
///
/// GetStreamBufferFrameCount
/// Returns how many frames have been processed (Inbound/Outbound)
@@ -161,7 +100,7 @@ public void AddToInternalMLAPISendQueue(RpcFrameQueueItem queueItem)
/// Will process the RPC queue and then move to the next available frame
///
///
- public void ProcessAndFlushRPCQueue(RpcQueueProcessingTypes queueType, NetworkUpdateManager.NetworkUpdateStage currentUpdateStage)
+ public void ProcessAndFlushRPCQueue(RpcQueueProcessingTypes queueType, NetworkUpdateStage currentUpdateStage)
{
if (m_RpcQueueProcessor == null)
{
@@ -189,7 +128,7 @@ public void ProcessAndFlushRPCQueue(RpcQueueProcessingTypes queueType, NetworkUp
///
///
/// QueueHistoryFrame
- public QueueHistoryFrame GetCurrentFrame(QueueHistoryFrame.QueueFrameType qType, NetworkUpdateManager.NetworkUpdateStage currentUpdateStage)
+ public QueueHistoryFrame GetCurrentFrame(QueueHistoryFrame.QueueFrameType qType, NetworkUpdateStage currentUpdateStage)
{
if (QueueHistory.ContainsKey(qType))
{
@@ -241,7 +180,7 @@ public void AdvanceFrameHistory(QueueHistoryFrame.QueueFrameType queueType)
}
- foreach (KeyValuePair queueHistoryByUpdates in QueueHistory[queueType][StreamBufferIndex])
+ foreach (KeyValuePair queueHistoryByUpdates in QueueHistory[queueType][StreamBufferIndex])
{
QueueHistoryFrame queueHistoryItem = queueHistoryByUpdates.Value;
//This only gets reset when we advanced to next frame (do not reset this in the ResetQueueHistoryFrame)
@@ -333,16 +272,10 @@ internal void AddQueueItemToInboundFrame(QueueItemType qItemType, float timeStam
var shortValue = BR.ReadUInt16Packed(); // NetworkBehaviourId (temporary, we reset position just below)
- ushort updateStageValue = BR.ReadUInt16Packed();
+ var updateStage = (NetworkUpdateStage)BR.ReadByteDirect();
BR.Dispose();
BR = null;
- NetworkUpdateManager.NetworkUpdateStage updateStage = NetworkUpdateManager.NetworkUpdateStage.Update;
- if (System.Enum.IsDefined(typeof(NetworkUpdateManager.NetworkUpdateStage), (int)updateStageValue))
- {
- updateStage = (NetworkUpdateManager.NetworkUpdateStage)updateStageValue;
- }
-
message.Position = originalPosition;
QueueHistoryFrame queueHistoryItem = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound, updateStage);
queueHistoryItem.isDirty = true;
@@ -374,13 +307,13 @@ internal void AddQueueItemToInboundFrame(QueueItemType qItemType, float timeStam
/// Sets the next frame inbond buffer as the loopback queue history frame in the current frame's outbound buffer
///
///
- public void SetLoopBackFrameItem(NetworkUpdateManager.NetworkUpdateStage updateStage)
+ public void SetLoopBackFrameItem(NetworkUpdateStage updateStage)
{
//Get the next frame's inbound queue history frame
QueueHistoryFrame loopbackHistoryframe = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound,updateStage,true);
//Get the current frame's outbound queue history frame
- QueueHistoryFrame queueHistoryItem = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Outbound,NetworkUpdateManager.NetworkUpdateStage.LateUpdate,false);
+ QueueHistoryFrame queueHistoryItem = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate, false);
if (queueHistoryItem != null)
{
@@ -400,9 +333,9 @@ public void SetLoopBackFrameItem(NetworkUpdateManager.NetworkUpdateStage updateS
///
///
///
- public QueueHistoryFrame GetLoopBackHistoryFrame( QueueHistoryFrame.QueueFrameType queueFrameType,NetworkUpdateManager.NetworkUpdateStage updateStage)
+ public QueueHistoryFrame GetLoopBackHistoryFrame(QueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
{
- return GetQueueHistoryFrame(queueFrameType,updateStage,false);
+ return GetQueueHistoryFrame(queueFrameType, updateStage, false);
}
///
@@ -417,13 +350,9 @@ public QueueHistoryFrame GetLoopBackHistoryFrame( QueueHistoryFrame.QueueFrameTy
/// who the rpc is being sent to
///
public PooledBitWriter BeginAddQueueItemToFrame(QueueItemType qItemType, float timeStamp, Channel channel, ushort sendflags, ulong sourceNetworkId, ulong[] targetNetworkIds,
- QueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateManager.NetworkUpdateStage updateStage)
+ QueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
{
- bool getNextFrame = false;
- if (NetworkingManager.Singleton.IsHost && queueFrameType == QueueHistoryFrame.QueueFrameType.Inbound)
- {
- getNextFrame = true;
- }
+ bool getNextFrame = NetworkingManager.Singleton.IsHost && queueFrameType == QueueHistoryFrame.QueueFrameType.Inbound;
QueueHistoryFrame queueHistoryItem = GetQueueHistoryFrame(queueFrameType, updateStage, getNextFrame);
queueHistoryItem.isDirty = true;
@@ -502,13 +431,9 @@ public PooledBitWriter BeginAddQueueItemToFrame(QueueItemType qItemType, float t
/// We store final MSG size and track the total current frame queue size
///
/// writer that was used
- public void EndAddQueueItemToFrame(BitWriter writer, QueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateManager.NetworkUpdateStage updateStage)
+ public void EndAddQueueItemToFrame(BitWriter writer, QueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
{
- bool getNextFrame = false;
- if (NetworkingManager.Singleton.IsHost && queueFrameType == QueueHistoryFrame.QueueFrameType.Inbound)
- {
- getNextFrame = true;
- }
+ bool getNextFrame = NetworkingManager.Singleton.IsHost && queueFrameType == QueueHistoryFrame.QueueFrameType.Inbound;
QueueHistoryFrame queueHistoryItem = GetQueueHistoryFrame(queueFrameType, updateStage, getNextFrame);
QueueHistoryFrame loopBackHistoryFrame = queueHistoryItem.loopbackHistoryFrame;
@@ -610,7 +535,7 @@ public void EndAddQueueItemToFrame(BitWriter writer, QueueHistoryFrame.QueueFram
///
/// inbound or outbound
/// QueueHistoryFrame or null
- public QueueHistoryFrame GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType frameType, NetworkUpdateManager.NetworkUpdateStage updateStage, bool getNextFrame = false)
+ public QueueHistoryFrame GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType frameType, NetworkUpdateStage updateStage, bool getNextFrame = false)
{
int StreamBufferIndex = GetStreamBufferIndex(frameType);
@@ -657,20 +582,20 @@ public void LoopbackSendFrame()
//If we do not have loop back or testing mode enabled then ignore the call
if (m_IsTestingEnabled)
{
- QueueHistoryFrame queueHistoryItemOutbound = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+ QueueHistoryFrame queueHistoryItemOutbound = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
if (queueHistoryItemOutbound.queueItemOffsets.Count > 0)
{
//Reset inbound queues based on update stage
- foreach (NetworkUpdateManager.NetworkUpdateStage stage in System.Enum.GetValues(typeof(NetworkUpdateManager.NetworkUpdateStage)))
+ foreach (NetworkUpdateStage netUpdateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
{
- QueueHistoryFrame queueHistoryItemInbound = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound, stage);
+ QueueHistoryFrame queueHistoryItemInbound = GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound, netUpdateStage);
ResetQueueHistoryFrame(queueHistoryItemInbound);
}
PooledBitStream pooledBitStream = PooledBitStream.Get();
RpcFrameQueueItem rpcFrameQueueItem = queueHistoryItemOutbound.GetFirstQueueItem();
- while (rpcFrameQueueItem.queueItemType != RpcQueueContainer.QueueItemType.None)
+ while (rpcFrameQueueItem.queueItemType != QueueItemType.None)
{
pooledBitStream.SetLength(rpcFrameQueueItem.streamSize);
pooledBitStream.Position = 0;
@@ -706,20 +631,20 @@ public void Initialize(uint maxFrameHistory)
if (!QueueHistory.ContainsKey(QueueHistoryFrame.QueueFrameType.Inbound))
{
- QueueHistory.Add(QueueHistoryFrame.QueueFrameType.Inbound, new Dictionary>());
+ QueueHistory.Add(QueueHistoryFrame.QueueFrameType.Inbound, new Dictionary>());
}
if (!QueueHistory.ContainsKey(QueueHistoryFrame.QueueFrameType.Outbound))
{
- QueueHistory.Add(QueueHistoryFrame.QueueFrameType.Outbound, new Dictionary>());
+ QueueHistory.Add(QueueHistoryFrame.QueueFrameType.Outbound, new Dictionary>());
}
for (int i = 0; i < m_MaxFrameHistory; i++)
{
if (!QueueHistory[QueueHistoryFrame.QueueFrameType.Outbound].ContainsKey(i))
{
- QueueHistory[QueueHistoryFrame.QueueFrameType.Outbound].Add(i, new Dictionary());
- QueueHistoryFrame queueHistoryFrame = new QueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+ QueueHistory[QueueHistoryFrame.QueueFrameType.Outbound].Add(i, new Dictionary());
+ var queueHistoryFrame = new QueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
queueHistoryFrame.queueStream = PooledBitStream.Get();
queueHistoryFrame.queueStream.Position = 0;
queueHistoryFrame.queueWriter = PooledBitWriter.Get(queueHistoryFrame.queueStream);
@@ -727,23 +652,23 @@ public void Initialize(uint maxFrameHistory)
queueHistoryFrame.queueItemOffsets = new List();
//For now all outbound, we will always have a single update in which they are processed (LATEUPDATE)
- QueueHistory[QueueHistoryFrame.QueueFrameType.Outbound][i].Add(NetworkUpdateManager.NetworkUpdateStage.LateUpdate, queueHistoryFrame);
+ QueueHistory[QueueHistoryFrame.QueueFrameType.Outbound][i].Add(NetworkUpdateStage.PostLateUpdate, queueHistoryFrame);
}
if (!QueueHistory[QueueHistoryFrame.QueueFrameType.Inbound].ContainsKey(i))
{
- QueueHistory[QueueHistoryFrame.QueueFrameType.Inbound].Add(i, new Dictionary());
+ QueueHistory[QueueHistoryFrame.QueueFrameType.Inbound].Add(i, new Dictionary());
//For inbound, we create a queue history frame per update stage
- foreach (NetworkUpdateManager.NetworkUpdateStage stage in Enum.GetValues(typeof(NetworkUpdateManager.NetworkUpdateStage)))
+ foreach (NetworkUpdateStage netUpdateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
{
- QueueHistoryFrame queueHistoryFrame = new QueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound, stage);
+ QueueHistoryFrame queueHistoryFrame = new QueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound, netUpdateStage);
queueHistoryFrame.queueStream = PooledBitStream.Get();
queueHistoryFrame.queueStream.Position = 0;
queueHistoryFrame.queueWriter = PooledBitWriter.Get(queueHistoryFrame.queueStream);
queueHistoryFrame.queueReader = PooledBitReader.Get(queueHistoryFrame.queueStream);
queueHistoryFrame.queueItemOffsets = new List();
- QueueHistory[QueueHistoryFrame.QueueFrameType.Inbound][i].Add(stage, queueHistoryFrame);
+ QueueHistory[QueueHistoryFrame.QueueFrameType.Inbound][i].Add(netUpdateStage, queueHistoryFrame);
}
}
}
@@ -752,7 +677,7 @@ public void Initialize(uint maxFrameHistory)
if (!m_ProcessUpdateStagesExternally)
{
//Register with the network update loop system
- RegisterUpdateLoopSystem();
+ this.RegisterAllNetworkUpdates();
}
}
@@ -792,15 +717,15 @@ public void Shutdown()
if (!m_ProcessUpdateStagesExternally)
{
//Remove ourself from the network loop update system
- OnNetworkLoopSystemRemove();
+ this.UnregisterAllNetworkUpdates();
}
//Dispose of any readers and writers
- foreach (KeyValuePair>> queueHistorySection in QueueHistory)
+ foreach (KeyValuePair>> queueHistorySection in QueueHistory)
{
- foreach (KeyValuePair> queueHistoryItemByStage in queueHistorySection.Value)
+ foreach (KeyValuePair> queueHistoryItemByStage in queueHistorySection.Value)
{
- foreach (KeyValuePair queueHistoryItem in queueHistoryItemByStage.Value)
+ foreach (KeyValuePair queueHistoryItem in queueHistoryItemByStage.Value)
{
queueHistoryItem.Value.queueWriter?.Dispose();
queueHistoryItem.Value.queueReader?.Dispose();
diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueProcessor.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueProcessor.cs
index 2f96845734..154a120064 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueProcessor.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueProcessor.cs
@@ -32,7 +32,7 @@ internal class RpcQueueProcessor
/// ProcessReceiveQueue
/// Public facing interface method to start processing all RPCs in the current inbound frame
///
- public void ProcessReceiveQueue(NetworkUpdateManager.NetworkUpdateStage currentStage)
+ public void ProcessReceiveQueue(NetworkUpdateStage currentStage)
{
bool AdvanceFrameHistory = false;
var rpcQueueContainer = NetworkingManager.Singleton.rpcQueueContainer;
@@ -41,8 +41,8 @@ public void ProcessReceiveQueue(NetworkUpdateManager.NetworkUpdateStage currentS
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_MLAPIRPCQueueProcess.Begin();
#endif
- var CurrentFrame = rpcQueueContainer.GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound,currentStage);
- var NextFrame = rpcQueueContainer.GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound,currentStage,true);
+ var CurrentFrame = rpcQueueContainer.GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound, currentStage);
+ var NextFrame = rpcQueueContainer.GetQueueHistoryFrame(QueueHistoryFrame.QueueFrameType.Inbound, currentStage, true);
if (NextFrame.isDirty && NextFrame.hasLoopbackData)
{
AdvanceFrameHistory = true;
@@ -161,7 +161,7 @@ private void RPCQueueSendAndFlush()
var rpcQueueContainer = NetworkingManager.Singleton.rpcQueueContainer;
if (rpcQueueContainer != null)
{
- var CurrentFrame = rpcQueueContainer.GetCurrentFrame(QueueHistoryFrame.QueueFrameType.Outbound,NetworkUpdateManager.NetworkUpdateStage.LateUpdate);
+ var CurrentFrame = rpcQueueContainer.GetCurrentFrame(QueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
if (CurrentFrame != null)
{
diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/SpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/SpawnManager.cs
index 4065ad00a4..f136c01142 100644
--- a/com.unity.multiplayer.mlapi/Runtime/Spawning/SpawnManager.cs
+++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/SpawnManager.cs
@@ -419,7 +419,7 @@ internal static void SendSpawnCallForObject(ulong clientId, NetworkedObject netO
var queueItem = new RpcFrameQueueItem
{
- updateStage = NetworkUpdateManager.NetworkUpdateStage.Update,
+ updateStage = NetworkUpdateStage.Update,
queueItemType = RpcQueueContainer.QueueItemType.CreateObject,
networkId = 0,
itemStream = stream,
@@ -693,6 +693,7 @@ internal static void OnDestroyObject(ulong networkId, bool destroyGameObject)
var queueItem = new RpcFrameQueueItem
{
+ updateStage = NetworkUpdateStage.PostLateUpdate,
queueItemType = RpcQueueContainer.QueueItemType.DestroyObject,
networkId = networkId,
itemStream = stream,
diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs
new file mode 100644
index 0000000000..47a6e67b11
--- /dev/null
+++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Collections;
+using UnityEngine;
+using UnityEngine.TestTools;
+using NUnit.Framework;
+
+namespace MLAPI.RuntimeTests
+{
+ public class NetworkUpdateLoopTests
+ {
+ private struct NetworkUpdateCallbacks
+ {
+ public Action OnInitialization;
+ public Action OnEarlyUpdate;
+ public Action OnFixedUpdate;
+ public Action OnPreUpdate;
+ public Action OnUpdate;
+ public Action OnPreLateUpdate;
+ public Action OnPostLateUpdate;
+ }
+
+ private class MyPlainScript : IDisposable, INetworkUpdateSystem
+ {
+ public NetworkUpdateCallbacks UpdateCallbacks;
+
+ public void Initialize()
+ {
+ this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate);
+ this.RegisterNetworkUpdate(NetworkUpdateStage.PreLateUpdate);
+ }
+
+ public void NetworkUpdate(NetworkUpdateStage updateStage)
+ {
+ switch (updateStage)
+ {
+ case NetworkUpdateStage.Initialization:
+ UpdateCallbacks.OnInitialization();
+ break;
+ case NetworkUpdateStage.EarlyUpdate:
+ UpdateCallbacks.OnEarlyUpdate();
+ break;
+ case NetworkUpdateStage.FixedUpdate:
+ UpdateCallbacks.OnFixedUpdate();
+ break;
+ case NetworkUpdateStage.PreUpdate:
+ UpdateCallbacks.OnPreUpdate();
+ break;
+ case NetworkUpdateStage.Update:
+ UpdateCallbacks.OnUpdate();
+ break;
+ case NetworkUpdateStage.PreLateUpdate:
+ UpdateCallbacks.OnPreLateUpdate();
+ break;
+ case NetworkUpdateStage.PostLateUpdate:
+ UpdateCallbacks.OnPostLateUpdate();
+ break;
+ }
+ }
+
+ public void Dispose()
+ {
+ this.UnregisterAllNetworkUpdates();
+ }
+ }
+
+ [UnityTest]
+ public IEnumerator UpdateStagesPlain()
+ {
+ const int kNetInitializationIndex = 0;
+ const int kNetEarlyUpdateIndex = 1;
+ const int kNetFixedUpdateIndex = 2;
+ const int kNetPreUpdateIndex = 3;
+ const int kNetUpdateIndex = 4;
+ const int kNetPreLateUpdateIndex = 5;
+ const int kNetPostLateUpdateIndex = 6;
+ int[] netUpdates = new int[7];
+
+ bool isTesting = false;
+ using (var plainScript = new MyPlainScript())
+ {
+ plainScript.UpdateCallbacks = new NetworkUpdateCallbacks
+ {
+ OnInitialization = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetInitializationIndex]++;
+ }
+ },
+ OnEarlyUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetEarlyUpdateIndex]++;
+ }
+ },
+ OnFixedUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetFixedUpdateIndex]++;
+ }
+ },
+ OnPreUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetPreUpdateIndex]++;
+ }
+ },
+ OnUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetUpdateIndex]++;
+ }
+ },
+ OnPreLateUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetPreLateUpdateIndex]++;
+ }
+ },
+ OnPostLateUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetPostLateUpdateIndex]++;
+ }
+ }
+ };
+
+ plainScript.Initialize();
+ int nextFrameNumber = Time.frameCount + 1;
+ yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
+ isTesting = true;
+
+ const int kRunTotalFrames = 16;
+ int waitFrameNumber = Time.frameCount + kRunTotalFrames;
+ yield return new WaitUntil(() => Time.frameCount >= waitFrameNumber);
+
+ Assert.AreEqual(0, netUpdates[kNetInitializationIndex]);
+ Assert.AreEqual(kRunTotalFrames, netUpdates[kNetEarlyUpdateIndex]);
+ Assert.AreEqual(0, netUpdates[kNetFixedUpdateIndex]);
+ Assert.AreEqual(0, netUpdates[kNetPreUpdateIndex]);
+ Assert.AreEqual(0, netUpdates[kNetUpdateIndex]);
+ Assert.AreEqual(kRunTotalFrames, netUpdates[kNetPreLateUpdateIndex]);
+ Assert.AreEqual(0, netUpdates[kNetPostLateUpdateIndex]);
+ }
+ }
+
+ private struct MonoBehaviourCallbacks
+ {
+ public Action OnFixedUpdate;
+ public Action OnUpdate;
+ public Action OnLateUpdate;
+ }
+
+ private class MyGameScript : MonoBehaviour, INetworkUpdateSystem
+ {
+ public NetworkUpdateCallbacks UpdateCallbacks;
+ public MonoBehaviourCallbacks BehaviourCallbacks;
+
+ private void Awake()
+ {
+ this.RegisterNetworkUpdate(NetworkUpdateStage.FixedUpdate);
+ this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
+ this.RegisterNetworkUpdate(NetworkUpdateStage.PreLateUpdate);
+ }
+
+ public void NetworkUpdate(NetworkUpdateStage updateStage)
+ {
+ switch (updateStage)
+ {
+ case NetworkUpdateStage.FixedUpdate:
+ UpdateCallbacks.OnFixedUpdate();
+ break;
+ case NetworkUpdateStage.PreUpdate:
+ UpdateCallbacks.OnPreUpdate();
+ break;
+ case NetworkUpdateStage.PreLateUpdate:
+ UpdateCallbacks.OnPreLateUpdate();
+ break;
+ }
+ }
+
+ private void FixedUpdate()
+ {
+ BehaviourCallbacks.OnFixedUpdate();
+ }
+
+ private void Update()
+ {
+ BehaviourCallbacks.OnUpdate();
+ }
+
+ private void LateUpdate()
+ {
+ BehaviourCallbacks.OnLateUpdate();
+ }
+
+ private void OnDestroy()
+ {
+ this.UnregisterAllNetworkUpdates();
+ }
+ }
+
+ [UnityTest]
+ public IEnumerator UpdateStagesMixed()
+ {
+ const int kNetFixedUpdateIndex = 0;
+ const int kNetPreUpdateIndex = 1;
+ const int kNetPreLateUpdateIndex = 2;
+ int[] netUpdates = new int[3];
+ const int kMonoFixedUpdateIndex = 0;
+ const int kMonoUpdateIndex = 1;
+ const int kMonoLateUpdateIndex = 2;
+ int[] monoUpdates = new int[3];
+
+ bool isTesting = false;
+ {
+ var gameObject = new GameObject($"{nameof(NetworkUpdateLoopTests)}.{nameof(UpdateStagesMixed)} (Dummy)");
+ var gameScript = gameObject.AddComponent();
+ gameScript.UpdateCallbacks = new NetworkUpdateCallbacks
+ {
+ OnFixedUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetFixedUpdateIndex]++;
+ Assert.AreEqual(monoUpdates[kMonoFixedUpdateIndex] + 1, netUpdates[kNetFixedUpdateIndex]);
+ }
+ },
+ OnPreUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetPreUpdateIndex]++;
+ Assert.AreEqual(monoUpdates[kMonoUpdateIndex] + 1, netUpdates[kNetPreUpdateIndex]);
+ }
+ },
+ OnPreLateUpdate = () =>
+ {
+ if (isTesting)
+ {
+ netUpdates[kNetPreLateUpdateIndex]++;
+ Assert.AreEqual(monoUpdates[kMonoLateUpdateIndex] + 1, netUpdates[kNetPreLateUpdateIndex]);
+ }
+ }
+ };
+ gameScript.BehaviourCallbacks = new MonoBehaviourCallbacks
+ {
+ OnFixedUpdate = () =>
+ {
+ if (isTesting)
+ {
+ monoUpdates[kMonoFixedUpdateIndex]++;
+ Assert.AreEqual(netUpdates[kNetFixedUpdateIndex], monoUpdates[kMonoFixedUpdateIndex]);
+ }
+ },
+ OnUpdate = () =>
+ {
+ if (isTesting)
+ {
+ monoUpdates[kMonoUpdateIndex]++;
+ Assert.AreEqual(netUpdates[kNetPreUpdateIndex], monoUpdates[kMonoUpdateIndex]);
+ }
+ },
+ OnLateUpdate = () =>
+ {
+ if (isTesting)
+ {
+ monoUpdates[kMonoLateUpdateIndex]++;
+ Assert.AreEqual(netUpdates[kNetPreLateUpdateIndex], monoUpdates[kMonoLateUpdateIndex]);
+ }
+ }
+ };
+
+ int nextFrameNumber = Time.frameCount + 1;
+ yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
+ isTesting = true;
+
+ const int kRunTotalFrames = 16;
+ int waitFrameNumber = Time.frameCount + kRunTotalFrames;
+ yield return new WaitUntil(() => Time.frameCount >= waitFrameNumber);
+
+ Assert.AreEqual(kRunTotalFrames, netUpdates[kNetPreUpdateIndex]);
+ Assert.AreEqual(netUpdates[kNetPreUpdateIndex], monoUpdates[kMonoUpdateIndex]);
+
+ GameObject.DestroyImmediate(gameObject);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/PlaceholderTests.cs.meta b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs.meta
similarity index 100%
rename from com.unity.multiplayer.mlapi/Tests/Runtime/PlaceholderTests.cs.meta
rename to com.unity.multiplayer.mlapi/Tests/Runtime/NetworkUpdateLoopTests.cs.meta
diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/PlaceholderTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/PlaceholderTests.cs
deleted file mode 100644
index a81075798d..0000000000
--- a/com.unity.multiplayer.mlapi/Tests/Runtime/PlaceholderTests.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using NUnit.Framework;
-
-namespace MLAPI.RuntimeTests
-{
- public class PlaceholderTests
- {
- [Test]
- public void Foo()
- {
- }
- }
-}
\ No newline at end of file