diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index c34f7f1d90..d80167a252 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 ### Fixed +- Fixed issue where the `NetworkObject.DontDestroyWithOwner` was not being honored. (#3477) - Fixed issue where non-authority `NetworkTransform` instances would not allow non-synchronized axis values to be updated locally. (#3471) - Fixed issue where invoking `NetworkObject.NetworkShow` and `NetworkObject.ChangeOwnership` consecutively within the same call stack location could result in an unnecessary change in ownership error message generated on the target client side. (#3468) - Fixed issue where `NetworkVariable`s on a `NetworkBehaviour` could fail to synchronize changes if one has `NetworkVariableUpdateTraits` set and is dirty but is not ready to send. (#3466) diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index 4d48074d3f..473f3316cc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -1178,7 +1178,7 @@ internal void OnClientDisconnectFromServer(ulong clientId) { // If destroying with owner, then always despawn and destroy (or defer destroying to prefab handler) // Handle an object with no observers other than the current disconnecting client as destroying with owner - if (!ownedObject.DontDestroyWithOwner || ownedObject.Observers.Count == 0 || (ownedObject.Observers.Count == 1 && ownedObject.Observers.Contains(clientId))) + if (!ownedObject.DontDestroyWithOwner && (ownedObject.Observers.Count == 0 || (ownedObject.Observers.Count == 1 && ownedObject.Observers.Contains(clientId)))) { if (NetworkManager.PrefabHandler.ContainsHandler(ownedObject.GlobalObjectIdHash)) { @@ -1244,7 +1244,7 @@ internal void OnClientDisconnectFromServer(ulong clientId) } // Skip destroy with owner objects as they will be processed by the outer loop - if (!childObject.DontDestroyWithOwner || childObject.Observers.Count == 0 || (childObject.Observers.Count == 1 && childObject.Observers.Contains(clientId))) + if (!childObject.DontDestroyWithOwner && (childObject.Observers.Count == 0 || (childObject.Observers.Count == 1 && childObject.Observers.Contains(clientId)))) { continue; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDontDestroyWithOwnerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDontDestroyWithOwnerTests.cs index 2c59647aa9..9466148d38 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDontDestroyWithOwnerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDontDestroyWithOwnerTests.cs @@ -23,6 +23,7 @@ protected override bool UseCMBService() } protected GameObject m_PrefabToSpawn; + protected GameObject m_PrefabNoObserversSpawn; public NetworkObjectDontDestroyWithOwnerTests(HostOrServer hostOrServer) : base(hostOrServer) { } @@ -30,6 +31,11 @@ protected override void OnServerAndClientsCreated() { m_PrefabToSpawn = CreateNetworkObjectPrefab("ClientOwnedObject"); m_PrefabToSpawn.GetComponent().DontDestroyWithOwner = true; + + m_PrefabNoObserversSpawn = CreateNetworkObjectPrefab("NoObserversObject"); + var prefabNoObserversNetworkObject = m_PrefabNoObserversSpawn.GetComponent(); + prefabNoObserversNetworkObject.SpawnWithObservers = false; + prefabNoObserversNetworkObject.DontDestroyWithOwner = true; } [UnityTest] @@ -74,5 +80,30 @@ public IEnumerator DontDestroyWithOwnerTest() Assert.That(networkObject.OwnerClientId == m_ServerNetworkManager.LocalClientId); } } + + [UnityTest] + public IEnumerator NetworkShowThenClientDisconnects() + { + var authorityManager = GetAuthorityNetworkManager(); + var networkObject = SpawnObject(m_PrefabNoObserversSpawn, authorityManager).GetComponent(); + var longWait = new WaitForSeconds(0.25f); + yield return longWait; + var nonAuthorityManager = GetNonAuthorityNetworkManager(); + Assert.False(nonAuthorityManager.SpawnManager.SpawnedObjects.ContainsKey(networkObject.NetworkObjectId), $"[Client-{nonAuthorityManager.LocalClientId}] " + + $"Already has an instance of {networkObject.name} when it should not!"); + networkObject.NetworkShow(nonAuthorityManager.LocalClientId); + networkObject.ChangeOwnership(nonAuthorityManager.LocalClientId); + + yield return WaitForConditionOrTimeOut(() => nonAuthorityManager.SpawnManager.SpawnedObjects.ContainsKey(networkObject.NetworkObjectId) + && nonAuthorityManager.SpawnManager.SpawnedObjects[networkObject.NetworkObjectId].OwnerClientId == nonAuthorityManager.LocalClientId); + AssertOnTimeout($"[Client-{nonAuthorityManager.LocalClientId}] Failed to spawn {networkObject.name} when it was shown!"); + + yield return s_DefaultWaitForTick; + + nonAuthorityManager.Shutdown(); + + yield return longWait; + Assert.True(networkObject.IsSpawned, $"The spawned test prefab was despawned on the authority side when it shouldn't have been!"); + } } }