Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Removed

### Fixed

- Fixed issue where `NetworkObject.Observers` was not being cleared when despawned. (#2009)
- Fixed `NetworkAnimator` could not run in the server authoritative mode. (#2003)
- Fixed issue where late joining clients would get a soft synchronization error if any in-scene placed NetworkObjects were parented under another `NetworkObject`. (#1985)
- Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,9 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
SpawnedObjectsList.Remove(networkObject);
}

// Always clear out the observers list when despawned
networkObject.Observers.Clear();

var gobj = networkObject.gameObject;
if (destroyGameObject && gobj != null)
{
Expand Down
45 changes: 45 additions & 0 deletions testproject/Assets/Tests/Runtime/RpcObserverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,51 @@ private IEnumerator RunRpcObserverTest(List<ulong> nonObservers)
}
}

/// <summary>
/// Validates that when a NetworkObject is despawned but not destroyed
/// and then re-spawned that the observer list is cleared
/// </summary>
[UnityTest]
public IEnumerator DespawnRespawnObserverTest()
{
var nonObservers = new List<ulong>();
m_ServerRpcObserverObject.ResetTest();
// Wait for all clients to report they have spawned an instance of our test prefab
yield return WaitForConditionOrTimeOut(m_ServerRpcObserverObject.AllClientsSpawned);

m_ServerRpcObserverObject.ObserverMessageClientRpc();

yield return WaitForConditionOrTimeOut(m_ServerRpcObserverObject.AllObserversReceivedRPC);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for all clients to receive message!\n" +
$"Clients that received the message:{m_ServerRpcObserverObject.GetClientIdsAsString()}");
Assert.False(m_ServerRpcObserverObject.NonObserversReceivedRPC(nonObservers), $"Non-observers ({m_ServerRpcObserverObject.GetClientIdsAsString(nonObservers)}) received the RPC message!");

m_ServerRpcObserverObject.NetworkObject.Despawn(false);

Assert.True(m_ServerRpcObserverObject.NetworkObject.Observers.Count == 0, $"Despawned {m_ServerRpcObserverObject.name} but it still has {m_ServerRpcObserverObject.NetworkObject.Observers.Count} observers!");

for (int i = 4; i < NumberOfClients; i++)
{
nonObservers.Add(m_ClientNetworkManagers[i].LocalClientId);
m_ServerNetworkManager.DisconnectClient(m_ClientNetworkManagers[i].LocalClientId);
}

yield return s_DefaultWaitForTick;
m_ServerRpcObserverObject.ResetTest();
m_ServerRpcObserverObject.NetworkObject.Spawn();
m_ServerRpcObserverObject.ObserverMessageClientRpc();
yield return WaitForConditionOrTimeOut(m_ServerRpcObserverObject.AllObserversReceivedRPC);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for all clients to receive message!\n" +
$"Clients that received the message:{m_ServerRpcObserverObject.GetClientIdsAsString()}");
Assert.False(m_ServerRpcObserverObject.NonObserversReceivedRPC(nonObservers), $"Non-observers ({m_ServerRpcObserverObject.GetClientIdsAsString(nonObservers)}) received the RPC message!");

// Always verify the host received the RPC
if (m_UseHost)
{
Assert.True(m_ServerRpcObserverObject.HostReceivedMessage, $"Host failed to receive the ClientRpc with the following observers: {m_ServerRpcObserverObject.GetClientIdsAsString()}!");
}
}

protected override IEnumerator OnTearDown()
{
// Make sure to dispose of the native array
Expand Down