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