diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstance/MultiInstanceHelpers.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstance/MultiInstanceHelpers.cs index d9918dc61d..b7dd37d2a0 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstance/MultiInstanceHelpers.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstance/MultiInstanceHelpers.cs @@ -6,14 +6,18 @@ using NUnit.Framework; using UnityEngine; using UnityEngine.SceneManagement; +using Object = UnityEngine.Object; namespace MLAPI.RuntimeTests { /// /// Provides helpers for running multi instance tests. /// - internal static class MultiInstanceHelpers + public static class MultiInstanceHelpers { + + public static List NetworkManagerInstances = new List(); + /// /// Creates NetworkingManagers and configures them for use in a multi instance setting. /// @@ -28,11 +32,10 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ { // Create gameObject var go = new GameObject("NetworkManager - Client - " + i); - // Create networkManager component clients[i] = go.AddComponent(); - // Set config + // Set the NetworkConfig clients[i].NetworkConfig = new NetworkConfig() { // Set the current scene to prevent unexpected log messages which would trigger a failure @@ -42,14 +45,17 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ }; } + NetworkManagerInstances = new List(clients); + { // Create gameObject var go = new GameObject("NetworkManager - Server"); // Create networkManager component server = go.AddComponent(); + NetworkManagerInstances.Insert(0, server); - // Set config + // Set the NetworkConfig server.NetworkConfig = new NetworkConfig() { // Set the current scene to prevent unexpected log messages which would trigger a failure @@ -62,6 +68,25 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ return true; } + + public static void ShutdownAndClean() + { + // Shutdown the server which forces clients to disconnect + foreach (var networkManager in NetworkManagerInstances) + { + if (networkManager.IsServer) + { + networkManager.StopHost(); + } + } + + // Destroy the network manager instances + foreach (var networkManager in NetworkManagerInstances) + { + Object.Destroy(networkManager.gameObject); + } + } + /// /// Starts NetworkManager instances created by the Create method. /// @@ -170,6 +195,52 @@ public static IEnumerator WaitForClientConnected(NetworkManager client, Coroutin } } + public static IEnumerator WaitForClientsConnected(NetworkManager[] clients, CoroutineResultWrapper result = null, int maxFrames = 64) + { + // Make sure none are the host client + foreach (var client in clients) + { + if (client.IsServer) + { + throw new InvalidOperationException("Cannot wait for connected as server"); + } + } + + + int startFrame = Time.frameCount; + var allConnected = true; + while (Time.frameCount - startFrame <= maxFrames) + { + allConnected = true; + foreach (var client in clients) + { + if (!client.IsConnectedClient) + { + allConnected = false; + break; + } + } + if (allConnected) + { + break; + } + int nextFrameId = Time.frameCount + 1; + yield return new WaitUntil(() => Time.frameCount >= nextFrameId); + } + + if (result != null) + { + result.Result = allConnected; + } + else + { + foreach (var client in clients) + { + Assert.True(client.IsConnectedClient, $"Client {client.LocalClientId} never connected"); + } + } + } + /// /// Waits on the server side for 1 client to be connected /// @@ -199,7 +270,40 @@ public static IEnumerator WaitForClientConnectedToServer(NetworkManager server, } else { - Assert.True(res, "Client never connected to server"); + Assert.True(res, "A Client never connected to server"); + } + } + + /// + /// Waits on the server side for 1 client to be connected + /// + /// The server + /// The result. If null, it will automatically assert + /// The max frames to wait for + public static IEnumerator WaitForClientsConnectedToServer(NetworkManager server, int clientCount, CoroutineResultWrapper result = null, int maxFrames = 64) + { + if (!server.IsServer) + { + throw new InvalidOperationException("Cannot wait for connected as client"); + } + + int startFrame = Time.frameCount; + + while (Time.frameCount - startFrame <= maxFrames && server.ConnectedClients.Count != clientCount) + { + int nextFrameId = Time.frameCount + 1; + yield return new WaitUntil(() => Time.frameCount >= nextFrameId); + } + + bool res = server.ConnectedClients.Count == clientCount; + + if (result != null) + { + result.Result = res; + } + else + { + Assert.True(res, "A client never connected to server"); } } diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/Transport/SIPTransport.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/Transport/SIPTransport.cs index c86ab36c69..866d6b8019 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/Transport/SIPTransport.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/Transport/SIPTransport.cs @@ -44,24 +44,35 @@ public override void DisconnectLocalClient() { Type = NetworkEvent.Disconnect, Channel = NetworkChannel.Internal, - ConnectionId = m_LocalConnection.ConnectionId, + ConnectionId = m_LocalConnection != null ? m_LocalConnection.ConnectionId : ServerClientId, Data = new ArraySegment() }); - // Inject local disconnect - m_LocalConnection.IncomingBuffer.Enqueue(new Event + if (m_LocalConnection != null) { - Type = NetworkEvent.Disconnect, - Channel = NetworkChannel.Internal, - ConnectionId = m_LocalConnection.ConnectionId, - Data = new ArraySegment() - }); + // Inject local disconnect + m_LocalConnection.IncomingBuffer.Enqueue(new Event + { + Type = NetworkEvent.Disconnect, + Channel = NetworkChannel.Internal, + ConnectionId = m_LocalConnection.ConnectionId, + Data = new ArraySegment() + }); + + if (s_Server != null && m_LocalConnection != null) + { + // Remove the connection + s_Server.Transport.m_Clients.Remove(m_LocalConnection.ConnectionId); + } - // Remove the connection - s_Server.Transport.m_Clients.Remove(m_LocalConnection.ConnectionId); + if (m_LocalConnection.ConnectionId == ServerClientId) + { + s_Server = null; + } - // Remove the local connection - m_LocalConnection = null; + // Remove the local connection + m_LocalConnection = null; + } } // Called by server @@ -115,6 +126,12 @@ public override void Shutdown() Data = new ArraySegment() }); } + + if (m_LocalConnection != null && m_LocalConnection.ConnectionId == ServerClientId) + { + s_Server = null; + } + // TODO: Cleanup } diff --git a/testproject/Assets/Tests/Manual/HybridScripts.meta b/testproject/Assets/Tests/Manual/HybridScripts.meta new file mode 100644 index 0000000000..b207e036d7 --- /dev/null +++ b/testproject/Assets/Tests/Manual/HybridScripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1954bbeed4577d44ab11f794044a28cf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/Scripts/RpcQueueManualTests.cs b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs similarity index 61% rename from testproject/Assets/Tests/Manual/Scripts/RpcQueueManualTests.cs rename to testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs index 6deec92028..cf920d57bb 100644 --- a/testproject/Assets/Tests/Manual/Scripts/RpcQueueManualTests.cs +++ b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs @@ -9,7 +9,7 @@ namespace TestProject.ManualTests /// /// This class is used to verify that the RPC Queue allows for the /// sending of ClientRpcs to specific clients. It has several "direct" - /// methods of sending as is defined in the enum ClientRpcDirectTestingModes: + /// methods of sending as is defined in the ENUM ClientRpcDirectTestingModes: /// Single: This will send to a single client at a time /// Striped: This will send to alternating client pairs at a time /// Unified: This sends to all clients at the same time @@ -18,13 +18,21 @@ namespace TestProject.ManualTests /// During direct testing mode the following is also tested: /// all clients are updating the server with a continually growing counter /// the server is updating all clients with a global counter - /// During all tests the following additional Rpc Tests are performed: + /// During all tests the following additional RPC Tests are performed: /// Send client to server no parameters and then multiple parameters /// Send server to client no parameters and then multiple parameters /// public class RpcQueueManualTests : NetworkBehaviour { + public static bool UnitTesting; + private const float k_ProgressBarDivisor = 1.0f / 200.0f; + [SerializeField] + private bool m_RunInTestMode; + + [SerializeField] + [Range(1, 10)] + private int m_IterationsToRun; [SerializeField] private Text m_CounterTextObject; @@ -35,13 +43,23 @@ public class RpcQueueManualTests : NetworkBehaviour [SerializeField] private GameObject m_ConnectionModeButtonParent; + [SerializeField] + private NetworkManager m_ManualTestNetworkManager; + private Dictionary m_ClientSpecificCounters = new Dictionary(); private List m_ClientIds = new List(); private List m_ClientIndices = new List(); + + private bool m_BeginTest; + private bool m_HasBeenInitialized; + private bool m_ContinueToRun; private bool m_ConnectionEventOccurred; private bool m_MultiParameterCanSend; + + private int m_MaxGlobalDirectCounter; + private int m_TotalIterations; private int m_MultiParameterIntValue; private int m_MultiParameterValuesCount; private int m_MultiParameterNoneCount; @@ -57,6 +75,7 @@ public class RpcQueueManualTests : NetworkBehaviour private long m_MultiParameterLongValue; private ulong m_LocalClientId; + private float m_MesageSendDelay; private float m_MultiParameterFloatValue; private float m_GlobalCounterDelay; private float m_DirectGlobalCounterDelay; @@ -87,9 +106,79 @@ private enum NetworkManagerMode private ClientRpcParams m_ClientRpcParams; private ClientRpcParams m_ClientRpcParamsMultiParameter; + private ServerRpcParams m_MultiParameterTargets; + + + public void DisableManualNetworkManager() + { + if (m_ManualTestNetworkManager) + { + m_ManualTestNetworkManager.gameObject.SetActive(false); + } + } + + public bool IsFinishedWithTest() + { + if (m_RunInTestMode) + { + return !m_ContinueToRun; + } + return false; + } + + public void BeginTest() + { + m_BeginTest = true; + } + + public void SetTestingMode(bool enabled, int iterationCount) + { + m_RunInTestMode = enabled; + m_IterationsToRun = Mathf.Clamp(iterationCount, 1, 10); + } + + public string GetCurrentServerStatusInfo() + { + return m_ServerUpdateInfo; + } + + public string GetCurrentClientStatusInfo() + { + return m_ClientUpdateInfo; + } private void Start() { + m_ContinueToRun = true; + if (!UnitTesting) + { + m_BeginTest = true; + Initialize(); + m_MaxGlobalDirectCounter = 100; + m_MesageSendDelay = 0.20f; + } + else + { + m_ClientRpcParams.Send.TargetClientIds = new ulong[] { 0 }; + m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new ulong[] { 0 }; + //For unit tests we will only send 10 per update stage + m_MaxGlobalDirectCounter = 10; + m_BeginTest = false; + m_MesageSendDelay = 0.01f; + + var gameObject = GameObject.Find("NetworkManager"); + if (gameObject != null) + { + gameObject.SetActive(false); + Destroy(gameObject); + Debug.Log($"Found scene {nameof(NetworkManager)}, disabled it, and destroyed it."); + } + } + } + + private void Initialize() + { + m_TotalIterations = 0; //Start at a smaller resolution until connection mode is selected. Screen.SetResolution(320, 320, FullScreenMode.Windowed); if (m_CounterTextObject) @@ -106,6 +195,7 @@ private void Start() connectionModeScript.OnNotifyConnectionEventServer += OnNotifyConnectionEventServer; } } + } private void OnNotifyConnectionEventServer() @@ -154,14 +244,18 @@ public void OnCreateServer() } /// - /// Handles common and NetworkManager mode specifc initializations + /// Handles common and NetworkManager mode specific initializations /// private void InitializeNetworkManager() { m_ClientRpcParams.Send.TargetClientIds = new ulong[] { 0 }; m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new ulong[] { 0 }; + m_ClientRpcDirectTestingMode = ClientRpcDirectTestingModes.Single; - m_ConnectionModeButtonParent.SetActive(false); + if (m_ConnectionModeButtonParent) + { + m_ConnectionModeButtonParent.SetActive(false); + } m_MultiParameterCanSend = true; m_GlobalDirectScale = 2; @@ -176,9 +270,9 @@ private void InitializeNetworkManager() { case NetworkManagerMode.Client: { - if (!m_ConnectionEventOccurred) + if (!m_ConnectionEventOccurred && !UnitTesting) { - NetworkManager.Singleton.StartClient(); + NetworkManager.StartClient(); } m_ServerRpcParams.Send.UpdateStage = NetworkUpdateStage.Update; Screen.SetResolution(800, 80, FullScreenMode.Windowed); @@ -186,9 +280,9 @@ private void InitializeNetworkManager() } case NetworkManagerMode.Host: { - if (!m_ConnectionEventOccurred) + if (!m_ConnectionEventOccurred && !UnitTesting) { - NetworkManager.Singleton.StartHost(); + NetworkManager.StartHost(); } m_ClientRpcParams.Send.UpdateStage = NetworkUpdateStage.PreUpdate; Screen.SetResolution(800, 480, FullScreenMode.Windowed); @@ -196,9 +290,9 @@ private void InitializeNetworkManager() } case NetworkManagerMode.Server: { - if (!m_ConnectionEventOccurred) + if (!m_ConnectionEventOccurred && !UnitTesting) { - NetworkManager.Singleton.StartServer(); + NetworkManager.StartServer(); } m_ClientProgressBar.enabled = false; m_ClientRpcParams.Send.UpdateStage = NetworkUpdateStage.PostLateUpdate; @@ -208,6 +302,7 @@ private void InitializeNetworkManager() } m_RpcPerSecondTimer = Time.realtimeSinceStartup; + m_HasBeenInitialized = true; } /// @@ -217,12 +312,12 @@ public override void NetworkStart() { if (IsServer) { - NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnectedCallback; - NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback; + NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback; + NetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback; if (IsHost) { - m_ClientSpecificCounters.Add(NetworkManager.Singleton.LocalClientId, 0); - m_ClientIds.Add(NetworkManager.Singleton.LocalClientId); + m_ClientSpecificCounters.Add(NetworkManager.LocalClientId, 0); + m_ClientIds.Add(NetworkManager.LocalClientId); } } } @@ -234,8 +329,8 @@ private void OnDestroy() { if (IsServer) { - NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnectedCallback; - NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback; + NetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback; + NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback; } } @@ -264,7 +359,7 @@ private void OnClientConnectedCallback(ulong clientId) if (IsServer) { //Exclude the server local id if only a server - if (!IsHost && clientId == NetworkManager.Singleton.LocalClientId) + if (!IsHost && clientId == NetworkManager.LocalClientId) { return; } @@ -284,80 +379,101 @@ private void OnClientConnectedCallback(ulong clientId) /// private void Update() { - if (NetworkManager.Singleton && NetworkManager.Singleton.IsListening) + if (NetworkManager != null && NetworkManager.IsListening && ((IsServer && NetworkManager.ConnectedClientsList.Count > 1) || IsClient) && m_ContinueToRun && m_BeginTest) { - if (IsServer) + if (UnitTesting && !m_HasBeenInitialized) { - if (m_ClientSpecificCounters.Count > 0) + if (IsClient) { - if (m_GlobalCounterDelay < Time.realtimeSinceStartup) - { - m_GlobalCounterDelay = Time.realtimeSinceStartup + 0.200f; - m_GlobalCounter++; - OnSendGlobalCounterClientRpc(m_GlobalCounter); - m_RpcMessagesSent++; - } + OnCreateClient(); + } + else + { + OnCreateHost(); + } + return; + } - if (m_DirectGlobalCounterDelay < Time.realtimeSinceStartup) + if (m_RunInTestMode && m_IterationsToRun <= m_TotalIterations) + { + m_ContinueToRun = false; + } + else + { + if (IsServer) + { + if (m_ClientSpecificCounters.Count > 0) { - switch (m_ClientRpcDirectTestingMode) + if (m_GlobalCounterDelay < Time.realtimeSinceStartup) { - case ClientRpcDirectTestingModes.Single: - { - SingleDirectUpdate(); - break; - } - case ClientRpcDirectTestingModes.Striped: - { - StripedDirectUpdate(); - break; - } - case ClientRpcDirectTestingModes.Unified: - { - UnifiedDirectUpdate(); - break; - } + m_GlobalCounterDelay = Time.realtimeSinceStartup + 0.200f; + m_GlobalCounter++; + OnSendGlobalCounterClientRpc(m_GlobalCounter); + m_RpcMessagesSent++; + } + + if (m_DirectGlobalCounterDelay < Time.realtimeSinceStartup) + { + switch (m_ClientRpcDirectTestingMode) + { + case ClientRpcDirectTestingModes.Single: + { + SingleDirectUpdate(); + break; + } + case ClientRpcDirectTestingModes.Striped: + { + StripedDirectUpdate(); + break; + } + case ClientRpcDirectTestingModes.Unified: + { + UnifiedDirectUpdate(); + break; + } + } + m_RpcMessagesSent++; + m_DirectGlobalCounterDelay = Time.realtimeSinceStartup + m_GlobalDirectFrequency; } - m_RpcMessagesSent++; - m_DirectGlobalCounterDelay = Time.realtimeSinceStartup + m_GlobalDirectFrequency; } } - } - //Hosts and Clients execute this - if (IsHost || IsClient) - { - if (m_LocalCounterDelay < Time.realtimeSinceStartup) + //Hosts and Clients execute this + if (IsHost || IsClient) { - m_LocalCounterDelay = Time.realtimeSinceStartup + 0.25f; - m_LocalClientCounter++; - OnSendCounterServerRpc(m_LocalClientCounter, m_ServerRpcParams); - m_RpcMessagesSent++; - } - else if (m_LocalMultiDelay < Time.realtimeSinceStartup) - { - m_LocalMultiDelay = Time.realtimeSinceStartup + 0.325f; - if (m_MultiParameterCanSend) + if (m_LocalCounterDelay < Time.realtimeSinceStartup) { - m_MultiParameterCanSend = false; - //Multi Parameters - OnSendMultiParametersServerRpc(m_MultiParameterIntValue, m_MultiParameterFloatValue, m_MultiParameterLongValue, m_ServerRpcParams); + m_LocalCounterDelay = Time.realtimeSinceStartup + m_MesageSendDelay + 0.01f; + m_LocalClientCounter++; + + OnSendCounterServerRpc(m_LocalClientCounter, NetworkManager.LocalClientId, m_ServerRpcParams); m_RpcMessagesSent++; } - else + else if (m_LocalMultiDelay < Time.realtimeSinceStartup) { - m_MultiParameterCanSend = true; - OnSendNoParametersServerRpc(m_ServerRpcParams); - m_RpcMessagesSent++; + m_LocalMultiDelay = Time.realtimeSinceStartup + m_MesageSendDelay + 0.015f; + if (m_MultiParameterCanSend) + { + m_MultiParameterCanSend = false; + //Multi Parameters + OnSendMultiParametersServerRpc(m_MultiParameterIntValue, m_MultiParameterFloatValue, m_MultiParameterLongValue, m_ServerRpcParams); + m_RpcMessagesSent++; + } + else + { + m_MultiParameterCanSend = true; + OnSendNoParametersServerRpc(m_ServerRpcParams); + m_RpcMessagesSent++; + } } } - } - if (Time.realtimeSinceStartup - m_RpcPerSecondTimer > 1.0f) - { - m_RpcPerSecondTimer = Time.realtimeSinceStartup; - m_RpcPerSecond = m_RpcMessagesSent; - m_RpcMessagesSent = 0; + if (Time.realtimeSinceStartup - m_RpcPerSecondTimer > 1.0f) + { + m_RpcPerSecondTimer = Time.realtimeSinceStartup; + m_RpcPerSecond = m_RpcMessagesSent; + m_RpcMessagesSent = 0; + } } } } @@ -388,6 +504,10 @@ private void SelectNextDirectUpdateMethod() case ClientRpcDirectTestingModes.Unified: { m_ClientRpcDirectTestingMode = ClientRpcDirectTestingModes.Single; + if (m_RunInTestMode) + { + m_TotalIterations++; + } break; } } @@ -399,7 +519,7 @@ private void SelectNextDirectUpdateMethod() /// private void SingleDirectUpdate() { - if (m_GlobalDirectCounter == 100) + if (m_GlobalDirectCounter == m_MaxGlobalDirectCounter) { m_GlobalDirectCurrentClientIdIndex++; if (m_GlobalDirectCurrentClientIdIndex >= m_ClientIds.Count) @@ -418,9 +538,10 @@ private void SingleDirectUpdate() m_ClientRpcParams.Send.TargetClientIds = m_ClientIndices.ToArray(); - m_GlobalDirectCounter = Mathf.Clamp(m_GlobalDirectCounter += m_GlobalDirectScale, 0, 100); + m_GlobalDirectCounter = Mathf.Clamp(m_GlobalDirectCounter += m_GlobalDirectScale, 0, m_MaxGlobalDirectCounter); OnSendDirectCounterClientRpc(m_GlobalDirectCounter, m_ClientRpcParams); + m_ServerDirectTotalRpcCount++; } /// @@ -429,7 +550,7 @@ private void SingleDirectUpdate() /// private void StripedDirectUpdate() { - if (m_GlobalDirectCounter == 100) + if (m_GlobalDirectCounter == m_MaxGlobalDirectCounter) { m_GlobalDirectCurrentClientIdIndex++; if (m_GlobalDirectCurrentClientIdIndex >= m_ClientIds.Count) @@ -450,9 +571,10 @@ private void StripedDirectUpdate() } m_ClientRpcParams.Send.TargetClientIds = m_ClientIndices.ToArray(); - m_GlobalDirectCounter = Mathf.Clamp(m_GlobalDirectCounter += m_GlobalDirectScale, 0, 100); + m_GlobalDirectCounter = Mathf.Clamp(m_GlobalDirectCounter += m_GlobalDirectScale, 0, m_MaxGlobalDirectCounter); OnSendDirectCounterClientRpc(m_GlobalDirectCounter, m_ClientRpcParams); + m_ServerDirectTotalRpcCount += m_ClientIndices.Count; } /// @@ -461,16 +583,16 @@ private void StripedDirectUpdate() /// private void UnifiedDirectUpdate() { - if (m_GlobalDirectCounter == 100) + if (m_GlobalDirectCounter == m_MaxGlobalDirectCounter) { SelectNextDirectUpdateMethod(); return; } m_ClientRpcParams.Send.TargetClientIds = m_ClientIds.ToArray(); - m_GlobalDirectCounter = Mathf.Clamp(m_GlobalDirectCounter += m_GlobalDirectScale, 0, 100); - + m_GlobalDirectCounter = Mathf.Clamp(m_GlobalDirectCounter += m_GlobalDirectScale, 0, m_MaxGlobalDirectCounter); OnSendDirectCounterClientRpc(m_GlobalDirectCounter, m_ClientRpcParams); + m_ServerDirectTotalRpcCount += m_ClientIds.Count; } /// @@ -480,16 +602,19 @@ private void UnifiedDirectUpdate() /// the client side counter /// [ServerRpc(RequireOwnership = false)] - private void OnSendCounterServerRpc(int counter, ServerRpcParams parameters = default) + private void OnSendCounterServerRpc(int counter, ulong clientId, ServerRpcParams parameters = default) { //This is just for debug purposes so I can trap for "non-local" clients - if (IsHost && parameters.Receive.SenderClientId == 0) + if (m_ClientSpecificCounters.ContainsKey(parameters.Receive.SenderClientId)) { - m_ClientSpecificCounters[parameters.Receive.SenderClientId] = counter; - } - else if (m_ClientSpecificCounters.ContainsKey(parameters.Receive.SenderClientId)) - { - m_ClientSpecificCounters[parameters.Receive.SenderClientId] = counter; + if(m_ClientSpecificCounters[parameters.Receive.SenderClientId] < counter) + { + m_ClientSpecificCounters[parameters.Receive.SenderClientId] = counter; + } + else + { + Debug.LogWarning($"Client counter was sent {counter} value but it already was at a value of {m_ClientSpecificCounters[parameters.Receive.SenderClientId]}"); + } } } @@ -515,7 +640,7 @@ private void OnSendNoParametersServerRpc(ServerRpcParams parameters = default) private void OnSendMultiParametersServerRpc(int count, float floatValue, long longValue, ServerRpcParams parameters = default) { m_ClientRpcParamsMultiParameter.Send.TargetClientIds[0] = parameters.Receive.SenderClientId; - m_ClientRpcParamsMultiParameter.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate; + m_ClientRpcParamsMultiParameter.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate; OnSendMultiParametersClientRpc(count, floatValue, longValue, m_ClientRpcParamsMultiParameter); } @@ -533,7 +658,7 @@ private void OnSendNoParametersClientRpc(ClientRpcParams parameters = default) /// /// [Tests] Server to Client - /// Sends multiple parameters to the server + /// Sends multiple parameters to the client /// /// [ClientRpc] @@ -578,6 +703,7 @@ private void OnSendGlobalCounterClientRpc(int counter) private void OnSendDirectCounterClientRpc(int counter, ClientRpcParams parameters = default) { m_GlobalDirectCounter = counter; + m_ClientDirectTotalRpcCount++; } /// @@ -585,72 +711,84 @@ private void OnSendDirectCounterClientRpc(int counter, ClientRpcParams parameter /// private void OnGUI() { - if (m_CounterTextObject) + if (IsServer && !IsHost || (IsHost && !UnitTesting)) { - if (IsServer) - { - UpdateServerInfo(); - } - else - { - UpdateClientInfo(); - } + UpdateServerInfo(); + } + else if (IsHost && UnitTesting) + { + UpdateServerInfo(); + UpdateClientInfo(); + } + else + { + UpdateClientInfo(); } } + private string m_ClientUpdateInfo; + private int m_ClientDirectTotalRpcCount; + /// /// Update the client text info and progress bar /// private void UpdateClientInfo() { - if (m_LocalClientId == 0 && NetworkManager.Singleton && NetworkManager.Singleton.IsListening) + if (m_LocalClientId == 0 && NetworkManager && NetworkManager.IsListening) { - m_LocalClientId = NetworkManager.Singleton.LocalClientId; + m_LocalClientId = NetworkManager.LocalClientId; } + m_ClientUpdateInfo = $"Client-ID [{m_LocalClientId}] Broadcast Rpcs Received: {m_GlobalCounter - m_GlobalCounterOffset} | Direct Rpcs Received: {(UnitTesting ? m_ClientDirectTotalRpcCount : m_GlobalDirectCounter)} \n"; + m_ClientUpdateInfo += $"{nameof(m_MultiParameterValuesCount)} : {m_MultiParameterValuesCount} | {nameof(m_MultiParameterNoneCount)} : {m_MultiParameterNoneCount}"; - m_CounterTextObject.text = $"Client-ID [{m_LocalClientId}] Broadcast Rpcs Received: {m_GlobalCounter - m_GlobalCounterOffset} | Direct Rpcs Received: {m_GlobalDirectCounter} \n"; - m_CounterTextObject.text += $"{nameof(m_MultiParameterValuesCount)} : {m_MultiParameterValuesCount} | {nameof(m_MultiParameterNoneCount)} : {m_MultiParameterNoneCount}"; - - if (m_ClientProgressBar) + if (!UnitTesting) { - m_ClientProgressBar.fillAmount = Mathf.Clamp((2.0f * (float)m_GlobalDirectCounter) * k_ProgressBarDivisor, 0.01f, 1.0f); + m_CounterTextObject.text = m_ClientUpdateInfo; + if (m_ClientProgressBar) + { + m_ClientProgressBar.fillAmount = Mathf.Clamp((2.0f * (float)m_GlobalDirectCounter) * k_ProgressBarDivisor, 0.01f, 1.0f); + } } } + private string m_ServerUpdateInfo; + private int m_ServerDirectTotalRpcCount; /// /// Updates the server text info and host progress bar /// private void UpdateServerInfo() { - string updatedCounters = string.Empty; + m_ServerUpdateInfo = string.Empty; foreach (var entry in m_ClientSpecificCounters) { - if (entry.Key == 0 && IsHost) + if (entry.Key == NetworkManager.LocalClientId && IsHost) { - updatedCounters += $"Client-ID [{entry.Key}] Client to Server Rpcs Received: {entry.Value} | Broadcast Rpcs Sent:{m_GlobalCounter} -- Direct Rpcs Sent:{m_GlobalDirectCounter}\n"; - updatedCounters += $"{nameof(m_MultiParameterValuesCount)} : {m_MultiParameterValuesCount} | {nameof(m_MultiParameterNoneCount)} : {m_MultiParameterNoneCount}\n"; - updatedCounters += $"{nameof(m_RpcPerSecond)} : {m_RpcPerSecond}\n "; + m_ServerUpdateInfo += $"Client-ID [{entry.Key}] Client to Server Rpcs Received: {entry.Value} | Broadcast Rpcs Sent:{m_GlobalCounter} -- Direct Rpcs Sent:{(UnitTesting ? m_ServerDirectTotalRpcCount : m_GlobalDirectCounter)}\n"; + m_ServerUpdateInfo += $"{nameof(m_MultiParameterValuesCount)} : {m_MultiParameterValuesCount} | {nameof(m_MultiParameterNoneCount)} : {m_MultiParameterNoneCount}\n"; + m_ServerUpdateInfo += $"{nameof(m_RpcPerSecond)} : {m_RpcPerSecond}\n "; } else { - updatedCounters += $"Client-ID [{entry.Key}] Client to Server Rpcs Received: {entry.Value}\n"; + m_ServerUpdateInfo += $"Client-ID [{entry.Key}] Client to Server Rpcs Received: {entry.Value}\n"; } } - updatedCounters += $"{nameof(m_ClientRpcDirectTestingMode)} : {m_ClientRpcDirectTestingMode}"; + m_ServerUpdateInfo += $"{nameof(m_ClientRpcDirectTestingMode)} : {m_ClientRpcDirectTestingMode}"; - if (IsHost) + if (!UnitTesting) { - if (m_GlobalDirectCurrentClientIdIndex < m_ClientIds.Count) + if (IsHost) { - if (m_ClientProgressBar && m_ClientIndices.Contains(NetworkManager.Singleton.LocalClientId)) + if (m_GlobalDirectCurrentClientIdIndex < m_ClientIds.Count) { - m_ClientProgressBar.fillAmount = Mathf.Clamp((2.0f * (float)m_GlobalDirectCounter) * k_ProgressBarDivisor, 0.01f, 1.0f); + if (m_ClientProgressBar && m_ClientIndices.Contains(NetworkManager.LocalClientId)) + { + m_ClientProgressBar.fillAmount = Mathf.Clamp((2.0f * (float)m_GlobalDirectCounter) * k_ProgressBarDivisor, 0.01f, 1.0f); + } } } + m_CounterTextObject.text = m_ServerUpdateInfo; } - - m_CounterTextObject.text = updatedCounters; } } } diff --git a/testproject/Assets/Tests/Manual/Scripts/RpcQueueManualTests.cs.meta b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs.meta similarity index 100% rename from testproject/Assets/Tests/Manual/Scripts/RpcQueueManualTests.cs.meta rename to testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs.meta diff --git a/testproject/Assets/Tests/Manual/RpcTesting/RpcTesting.unity b/testproject/Assets/Tests/Manual/RpcTesting/RpcTesting.unity index 73a4eb47ed..8abf854678 100644 --- a/testproject/Assets/Tests/Manual/RpcTesting/RpcTesting.unity +++ b/testproject/Assets/Tests/Manual/RpcTesting/RpcTesting.unity @@ -758,7 +758,7 @@ GameObject: - component: {fileID: 1207168974} - component: {fileID: 1207168973} m_Layer: 5 - m_Name: ClientRpcTestsCanvas + m_Name: RpcTestsCanvas m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -875,9 +875,12 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 47f41f8e19d061b49866be44887f53ee, type: 3} m_Name: m_EditorClassIdentifier: + m_RunInTestMode: 1 + m_IterationsToRun: 5 m_CounterTextObject: {fileID: 1859881229} m_ClientProgressBar: {fileID: 521221618} m_ConnectionModeButtonParent: {fileID: 1705962118} + m_ManualTestNetworkManager: {fileID: 0} --- !u!1 &1544159781 GameObject: m_ObjectHideFlags: 0 diff --git a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs new file mode 100644 index 0000000000..bb59595813 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs @@ -0,0 +1,162 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using TestProject.ManualTests; +using MLAPI.RuntimeTests; +using MLAPI; +using Debug = UnityEngine.Debug; + +namespace TestProject.RuntimeTests +{ + public class RPCTestsAutomated + { + private bool m_TimedOut; + + /// + /// Default Mode (Batched RPCs Enabled) + /// + /// + [UnityTest] + public IEnumerator ManualRpcTestsAutomated() + { + return AutomatedRpcTestsHandler(9); + } + + /// + /// Same test with Batched RPC turned off + /// + /// + [UnityTest] + public IEnumerator ManualRpcTestsAutomatedNoBatching() + { + return AutomatedRpcTestsHandler(3, false); + } + + + /// + /// This just helps to simplify any further tests that can leverage from + /// the RpcQueueManualTests' wide array of RPC testing under different + /// conditions. + /// Currently this allows for the adjustment of client count and whether + /// RPC Batching is enabled or not. + /// + /// + /// + /// + private IEnumerator AutomatedRpcTestsHandler(int numClients, bool useBatching = true) + { + // Set RpcQueueManualTests into unit testing mode + RpcQueueManualTests.UnitTesting = true; + + // Create Host and (numClients) clients + Assert.True(MultiInstanceHelpers.Create(numClients, out NetworkManager server, out NetworkManager[] clients)); + + // Create a default player GameObject to use + var playerPrefab = new GameObject("Player"); + var networkObject = playerPrefab.AddComponent(); + + // Add our RpcQueueManualTests component + playerPrefab.AddComponent(); + + // Make it a prefab + MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject); + + // Set the player prefab + server.NetworkConfig.PlayerPrefab = playerPrefab; + + + // Set all of the client's player prefab + for (int i = 0; i < clients.Length; i++) + { + clients[i].NetworkConfig.PlayerPrefab = playerPrefab; + } + + // Start the instances + if (!MultiInstanceHelpers.Start(true, server, clients)) + { + Debug.LogError("Failed to start instances"); + Assert.Fail("Failed to start instances"); + } + + // Set the Rpc Batch sending mode + server.RpcQueueContainer.EnableBatchedRpcs(useBatching); + + for (int i = 0; i < clients.Length; i++) + { + clients[i].RpcQueueContainer.EnableBatchedRpcs(useBatching); + } + + // [Client-Side] Wait for a connection to the server + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients)); + + // [Host-Side] Check to make sure all clients are connected + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnectedToServer(server, clients.Length + 1)); + + // [Host-Side] Get the Host owned instance of the RpcQueueManualTests + var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == clients[0].LocalClientId), server, serverClientPlayerResult)); + + var serverRpcTests = serverClientPlayerResult.Result.GetComponent(); + Assert.IsNotNull(serverRpcTests); + + // [Host-Side] Set the (unit) testing mode + serverRpcTests.SetTestingMode(true, 1); + + // [Client-Side] Get all of the RpcQueueManualTests instances relative to each client + var clientRpcQueueManualTestInstsances = new List(); + foreach (var client in clients) + { + var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == clients[0].LocalClientId), client, clientClientPlayerResult)); + var clientRpcTests = clientClientPlayerResult.Result.GetComponent(); + Assert.IsNotNull(clientRpcTests); + clientRpcQueueManualTestInstsances.Add(clientRpcTests); + } + + // [Client-Side] Set the (unit) testing mode for each client + foreach (var rpcClientSideTest in clientRpcQueueManualTestInstsances) + { + rpcClientSideTest.SetTestingMode(true, 1); + } + + // [Host-Side] Begin testing on the host + serverRpcTests.BeginTest(); + + // [Client-Side] Begin testing on each client + foreach (var rpcClientSideTest in clientRpcQueueManualTestInstsances) + { + rpcClientSideTest.BeginTest(); + } + + // Wait for the all of the tests to finish + var timeOut = Time.realtimeSinceStartup + 5; + while (!serverRpcTests.IsFinishedWithTest()) + { + yield return new WaitForSeconds(0.01f); + if (timeOut < Time.realtimeSinceStartup) + { + m_TimedOut = true; + break; + } + } + + // Verify we didn't time out (i.e. all tests ran and finished) + Assert.IsFalse(m_TimedOut); + + // Log the output for visual confirmation (Acceptance Test for this test) that all Rpc test types (tracked by counters) executed multiple times + Debug.Log("Final Host-Server Status Info:"); + Debug.Log(serverRpcTests.GetCurrentServerStatusInfo()); + + foreach (var rpcClientSideTest in clientRpcQueueManualTestInstsances) + { + Debug.Log($"Final Client {rpcClientSideTest.NetworkManager.LocalClientId} Status Info:"); + Debug.Log(rpcClientSideTest.GetCurrentClientStatusInfo()); + } + + // Shutdown and clean up both of our NetworkManager instances + MultiInstanceHelpers.ShutdownAndClean(); + } + } +} diff --git a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs.meta b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs.meta new file mode 100644 index 0000000000..9800bda42a --- /dev/null +++ b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0cad0e6495da2e4c8dc8541f7850e6f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef b/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef index 962ebf0b4c..2aa7afc6f7 100644 --- a/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef +++ b/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef @@ -1,11 +1,11 @@ { "name": "TestProject.RuntimeTests", "references": [ - "Unity.Multiplayer.MLAPI.Runtime" + "Unity.Multiplayer.MLAPI.Runtime", + "Unity.Multiplayer.MLAPI.RuntimeTests", + "TestProject.ManualTests" ], "optionalUnityReferences": [ "TestAssemblies" - ], - "includePlatforms": [], - "excludePlatforms": [] + ] } \ No newline at end of file