diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index 4a3edac16b..634d544c84 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -1495,6 +1495,11 @@ public override bool StartServer() /// public override void Shutdown() { + if (m_NetworkManager && !m_NetworkManager.ShutdownInProgress) + { + Debug.LogWarning("Directly calling `UnityTransport.Shutdown()` results in unexpected shutdown behaviour. All pending events will be lost. Use `NetworkManager.Shutdown()` instead."); + } + if (m_Driver.IsCreated) { while (ProcessEvent() && m_Driver.IsCreated) @@ -1519,6 +1524,7 @@ public override void Shutdown() DisposeInternals(); m_ReliableReceiveQueues.Clear(); + m_State = State.Disconnected; // We must reset this to zero because UTP actually re-uses clientIds if there is a clean disconnect m_ServerClientId = 0; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTestHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTestHelpers.cs index 2523dd9257..926b2fa7bc 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTestHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTestHelpers.cs @@ -36,6 +36,24 @@ public static IEnumerator WaitForNetworkEvent(NetworkEvent type, List events, float timeout = MaxNetworkEventWaitTime) + { + int initialCount = events.Count; + float startTime = Time.realtimeSinceStartup; + + while (Time.realtimeSinceStartup - startTime < timeout) + { + if (events.Count > initialCount) + { + Assert.Fail("Received unexpected network event."); + } + + yield return new WaitForSeconds(0.01f); + } + } + + // Common code to initialize a UnityTransport that logs its events. public static void InitializeTransport(out UnityTransport transport, out List events, int maxPayloadSize = UnityTransport.InitialMaxPayloadSize, int maxSendQueueSize = 0, NetworkFamily family = NetworkFamily.Ipv4) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs index d5b720dada..016d45746c 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs @@ -499,5 +499,46 @@ public IEnumerator ReliablePayloadsCanBeLargerThanMaximum() yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events); } + + public enum AfterShutdownAction + { + Send, + DisconnectRemoteClient, + DisconnectLocalClient, + } + + [UnityTest] + public IEnumerator DoesNotActAfterShutdown([Values] AfterShutdownAction afterShutdownAction) + { + InitializeTransport(out m_Server, out m_ServerEvents); + InitializeTransport(out m_Client1, out m_Client1Events); + + m_Server.StartServer(); + m_Client1.StartClient(); + + yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events); + + m_Server.Shutdown(); + + if (afterShutdownAction == AfterShutdownAction.Send) + { + var data = new ArraySegment(new byte[16]); + m_Server.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable); + + yield return EnsureNoNetworkEvent(m_Client1Events); + } + else if (afterShutdownAction == AfterShutdownAction.DisconnectRemoteClient) + { + m_Server.DisconnectRemoteClient(m_Client1.ServerClientId); + + LogAssert.Expect(LogType.Assert, "DisconnectRemoteClient should be called on a listening server"); + } + else if (afterShutdownAction == AfterShutdownAction.DisconnectLocalClient) + { + m_Server.DisconnectLocalClient(); + + yield return EnsureNoNetworkEvent(m_Client1Events); + } + } } }