diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index d2b4a83fa..5d2c1967d 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -15,6 +15,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - The default listen address of `UnityTransport` is now 0.0.0.0. (#2307) ### Fixed +- Fixed server side issue where, depending upon component ordering, some NetworkBehaviour components might not have their OnNetworkDespawn method invoked if the client side disconnected. (#2323) - Fixed an issue in `UnityTransport` where an exception would be thrown if starting a Relay host/server on WebGL. This exception should only be thrown if using direct connections (where WebGL can't act as a host/server). (#2321) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index a88d3538c..fac713e28 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -2082,7 +2082,12 @@ namespace Unity.Netcode } else { - Destroy(playerObject.gameObject); + // Call despawn to assure NetworkBehaviour.OnNetworkDespawn is invoked + // on the server-side (when the client side disconnected). + // This prevents the issue (when just destroying the GameObject) where + // any NetworkBehaviour component(s) destroyed before the NetworkObject + // would not have OnNetworkDespawn invoked. + SpawnManager.DespawnObject(playerObject, true); } } else diff --git a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs index e44157b2e..4ddf4dd54 100644 --- a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs @@ -241,11 +241,20 @@ namespace Unity.Netcode.TestHelpers.Runtime { } + /// + /// Invoked immediately after the player prefab GameObject is created + /// prior to adding a NetworkObject component + /// + protected virtual void OnPlayerPrefabGameObjectCreated() + { + } + private void CreatePlayerPrefab() { VerboseDebug($"Entering {nameof(CreatePlayerPrefab)}"); // Create playerPrefab m_PlayerPrefab = new GameObject("Player"); + OnPlayerPrefabGameObjectCreated(); NetworkObject networkObject = m_PlayerPrefab.AddComponent(); // Make it a prefab diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs index c51527a59..1d8dd7679 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs @@ -1,5 +1,6 @@ using System.Collections; using UnityEngine; +using NUnit.Framework; using UnityEngine.TestTools; using Unity.Netcode.TestHelpers.Runtime; using Unity.Netcode.Components; @@ -147,5 +148,35 @@ namespace Unity.Netcode.RuntimeTests // (validating the fix) Object.Destroy(parentObject); } + + protected override void OnPlayerPrefabGameObjectCreated() + { + // Adds the SimpleNetworkBehaviour before the NetworkObject + // for OnNetworkDespawnInvokedWhenClientDisconnects testing + m_PlayerPrefab.AddComponent(); + } + + /// + /// This validates that upon a client disconnecting, the server-side + /// client's player clone will invoke NetworkBehaviour.OnNetworkDespawn + /// when the component precedes the NetworkObject component.(PR-2323) + /// + [UnityTest] + public IEnumerator OnNetworkDespawnInvokedWhenClientDisconnects() + { + m_AllowServerToStart = true; + + // Now just start the Host + yield return StartServerAndClients(); + + // Now create and connect a new client + yield return CreateAndStartNewClient(); + + var serverSidePlayer = m_PlayerNetworkObjects[NetworkManager.ServerClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent(); + + yield return StopOneClient(m_ClientNetworkManagers[0]); + + Assert.True(serverSidePlayer.OnNetworkDespawnCalled, $"Server-side player clone did not invoke OnNetworkDespawn!"); + } } }