using System;
using System.Collections;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace Unity.Netcode.RuntimeTests
{
internal class NetworkManagerEventsTests
{
private NetworkManager m_ClientManager;
private NetworkManager m_ServerManager;
private NetworkManager m_NetworkManagerInstantiated;
private bool m_Instantiated;
private bool m_Destroyed;
///
/// Validates the and event notifications
///
[UnityTest]
public IEnumerator InstantiatedAndDestroyingNotifications()
{
NetworkManager.OnInstantiated += NetworkManager_OnInstantiated;
NetworkManager.OnDestroying += NetworkManager_OnDestroying;
var waitPeriod = new WaitForSeconds(0.01f);
var prefab = new GameObject("InstantiateDestroy");
var networkManagerPrefab = prefab.AddComponent();
Assert.IsTrue(m_Instantiated, $"{nameof(NetworkManager)} prefab did not get instantiated event notification!");
Assert.IsTrue(m_NetworkManagerInstantiated == networkManagerPrefab, $"{nameof(NetworkManager)} prefab parameter did not match!");
m_Instantiated = false;
m_NetworkManagerInstantiated = null;
for (int i = 0; i < 3; i++)
{
var instance = Object.Instantiate(prefab);
var networkManager = instance.GetComponent();
Assert.IsTrue(m_Instantiated, $"{nameof(NetworkManager)} instance-{i} did not get instantiated event notification!");
Assert.IsTrue(m_NetworkManagerInstantiated == networkManager, $"{nameof(NetworkManager)} instance-{i} parameter did not match!");
Object.DestroyImmediate(instance);
Assert.IsTrue(m_Destroyed, $"{nameof(NetworkManager)} instance-{i} did not get destroying event notification!");
m_Instantiated = false;
m_NetworkManagerInstantiated = null;
m_Destroyed = false;
}
m_NetworkManagerInstantiated = networkManagerPrefab;
Object.Destroy(prefab);
yield return null;
Assert.IsTrue(m_Destroyed, $"{nameof(NetworkManager)} prefab did not get destroying event notification!");
NetworkManager.OnInstantiated -= NetworkManager_OnInstantiated;
NetworkManager.OnDestroying -= NetworkManager_OnDestroying;
}
private void NetworkManager_OnInstantiated(NetworkManager networkManager)
{
m_Instantiated = true;
m_NetworkManagerInstantiated = networkManager;
}
private void NetworkManager_OnDestroying(NetworkManager networkManager)
{
m_Destroyed = true;
Assert.True(m_NetworkManagerInstantiated == networkManager, $"Destroying {nameof(NetworkManager)} and current instance is not a match for the one passed into the event!");
}
[UnityTest]
public IEnumerator OnServerStoppedCalledWhenServerStops()
{
bool callbackInvoked = false;
var gameObject = new GameObject(nameof(OnServerStoppedCalledWhenServerStops));
m_ServerManager = gameObject.AddComponent();
// Set dummy transport that does nothing
var transport = gameObject.AddComponent();
m_ServerManager.NetworkConfig = new NetworkConfig() { NetworkTransport = transport };
Action onServerStopped = (bool wasAlsoClient) =>
{
callbackInvoked = true;
Assert.IsFalse(wasAlsoClient);
};
// Start server to cause initialization process
Assert.True(m_ServerManager.StartServer());
Assert.True(m_ServerManager.IsListening);
m_ServerManager.OnServerStopped += onServerStopped;
m_ServerManager.Shutdown();
Object.DestroyImmediate(gameObject);
yield return WaitUntilManagerShutsdown();
Assert.False(m_ServerManager.IsListening);
Assert.True(callbackInvoked, "OnServerStopped wasn't invoked");
}
[UnityTest]
public IEnumerator OnClientStoppedCalledWhenClientStops()
{
yield return InitializeServerAndAClient();
bool callbackInvoked = false;
Action onClientStopped = (bool wasAlsoServer) =>
{
callbackInvoked = true;
Assert.IsFalse(wasAlsoServer);
};
m_ClientManager.OnClientStopped += onClientStopped;
m_ClientManager.Shutdown();
yield return WaitUntilManagerShutsdown();
Assert.True(callbackInvoked, "OnClientStopped wasn't invoked");
}
[UnityTest]
public IEnumerator OnClientAndServerStoppedCalledWhenHostStops()
{
var gameObject = new GameObject(nameof(OnClientAndServerStoppedCalledWhenHostStops));
m_ServerManager = gameObject.AddComponent();
// Set dummy transport that does nothing
var transport = gameObject.AddComponent();
m_ServerManager.NetworkConfig = new NetworkConfig() { NetworkTransport = transport };
int callbacksInvoked = 0;
Action onClientStopped = (bool wasAlsoServer) =>
{
callbacksInvoked++;
Assert.IsTrue(wasAlsoServer);
};
Action onServerStopped = (bool wasAlsoClient) =>
{
callbacksInvoked++;
Assert.IsTrue(wasAlsoClient);
};
// Start server to cause initialization process
Assert.True(m_ServerManager.StartHost());
Assert.True(m_ServerManager.IsListening);
m_ServerManager.OnServerStopped += onServerStopped;
m_ServerManager.OnClientStopped += onClientStopped;
m_ServerManager.Shutdown();
Object.DestroyImmediate(gameObject);
yield return WaitUntilManagerShutsdown();
Assert.False(m_ServerManager.IsListening);
Assert.AreEqual(2, callbacksInvoked, "either OnServerStopped or OnClientStopped wasn't invoked");
}
[UnityTest]
public IEnumerator OnServerStartedCalledWhenServerStarts()
{
var gameObject = new GameObject(nameof(OnServerStartedCalledWhenServerStarts));
m_ServerManager = gameObject.AddComponent();
// Set dummy transport that does nothing
var transport = gameObject.AddComponent();
m_ServerManager.NetworkConfig = new NetworkConfig() { NetworkTransport = transport };
bool callbackInvoked = false;
Action onServerStarted = () =>
{
callbackInvoked = true;
if (!m_ServerManager.IsServer)
{
Assert.Fail("OnServerStarted called when the server is not active yet");
}
};
// Start server to cause initialization process
m_ServerManager.OnServerStarted += onServerStarted;
Assert.True(m_ServerManager.StartServer());
Assert.True(m_ServerManager.IsListening);
yield return WaitUntilServerBufferingIsReady();
Assert.True(callbackInvoked, "OnServerStarted wasn't invoked");
}
[UnityTest]
public IEnumerator OnClientStartedCalledWhenClientStarts()
{
bool callbackInvoked = false;
Action onClientStarted = () =>
{
callbackInvoked = true;
if (!m_ClientManager.IsClient)
{
Assert.Fail("onClientStarted called when the client is not active yet");
}
};
yield return InitializeServerAndAClient(onClientStarted);
Assert.True(callbackInvoked, "OnClientStarted wasn't invoked");
}
[UnityTest]
public IEnumerator OnClientAndServerStartedCalledWhenHostStarts()
{
var gameObject = new GameObject(nameof(OnClientAndServerStartedCalledWhenHostStarts));
m_ServerManager = gameObject.AddComponent();
// Set dummy transport that does nothing
var transport = gameObject.AddComponent();
m_ServerManager.NetworkConfig = new NetworkConfig() { NetworkTransport = transport };
int callbacksInvoked = 0;
Action onClientStarted = () =>
{
callbacksInvoked++;
};
Action onServerStarted = () =>
{
callbacksInvoked++;
};
m_ServerManager.OnServerStarted += onServerStarted;
m_ServerManager.OnClientStarted += onClientStarted;
// Start server to cause initialization process
Assert.True(m_ServerManager.StartHost());
Assert.True(m_ServerManager.IsListening);
yield return WaitUntilServerBufferingIsReady();
Assert.AreEqual(2, callbacksInvoked, "either OnServerStarted or OnClientStarted wasn't invoked");
}
private IEnumerator WaitUntilManagerShutsdown()
{
/* Need two updates to actually shut down. First one to see the transport failing, which
marks the NetworkManager as shutting down. Second one where actual shutdown occurs. */
yield return null;
yield return null;
}
private IEnumerator InitializeServerAndAClient(Action onClientStarted = null)
{
// Create multiple NetworkManager instances
if (!NetcodeIntegrationTestHelpers.Create(1, out m_ServerManager, out NetworkManager[] clients, 30))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}
// passing no clients on purpose to start them manually later
NetcodeIntegrationTestHelpers.Start(false, m_ServerManager, new NetworkManager[] { });
yield return WaitUntilServerBufferingIsReady();
m_ClientManager = clients[0];
if (onClientStarted != null)
{
m_ClientManager.OnClientStarted += onClientStarted;
}
Assert.True(m_ClientManager.StartClient());
NetcodeIntegrationTestHelpers.RegisterHandlers(clients[0]);
// Wait for connection on client side
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(clients);
}
private IEnumerator WaitUntilServerBufferingIsReady()
{
/* wait until at least more than 2 server ticks have passed
Note: Waiting for more than 2 ticks on the server is due
to the time system applying buffering to the received time
in NetworkTimeSystem.Sync */
yield return new WaitUntil(() => m_ServerManager.NetworkTickSystem.ServerTime.Tick > 2);
}
[UnityTearDown]
public virtual IEnumerator Teardown()
{
NetcodeIntegrationTestHelpers.Destroy();
if (m_ServerManager != null)
{
m_ServerManager.ShutdownInternal();
Object.DestroyImmediate(m_ServerManager);
m_ServerManager = null;
}
if (m_ClientManager != null)
{
m_ClientManager.ShutdownInternal();
Object.DestroyImmediate(m_ClientManager);
m_ClientManager = null;
}
yield return null;
}
}
}