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!");
+ }
}
}