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: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- Fixed issue where `NetworkClient` could persist some settings if re-using the same `NetworkManager` instance. (#3494)
- Fixed issue where a pooled `NetworkObject` was not resetting the internal latest parent property when despawned. (#3494)
- Fixed issue where the initial client synchronization pre-serialization process was not excluding spawned `NetworkObjects` that already had pending visibility for the client being synchronized. (#3493)
- 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. (#3493)
- 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. (#3465)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1265,8 +1265,8 @@ internal void ShutdownInternal()
// In the event shutdown is invoked within OnClientStopped or OnServerStopped, set it to false again
m_ShuttingDown = false;

// Reset the client's roles
ConnectionManager.LocalClient.SetRole(false, false);
// Completely reset the NetworkClient
ConnectionManager.LocalClient = new NetworkClient();

// This cleans up the internal prefabs list
NetworkConfig?.Prefabs?.Shutdown();
Expand Down
8 changes: 8 additions & 0 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,14 @@ public void Despawn(bool destroy = true)
NetworkManager.SpawnManager.DespawnObject(this, destroy);
}

internal void ResetOnDespawn()
{
// Always clear out the observers list when despawned
Observers.Clear();
IsSpawned = false;
m_LatestParent = null;
}

/// <summary>
/// Removes all ownership of an object from any client. Can only be called from server
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1191,15 +1191,13 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
}
}

networkObject.IsSpawned = false;

if (SpawnedObjects.Remove(networkObject.NetworkObjectId))
{
SpawnedObjectsList.Remove(networkObject);
}

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

var gobj = networkObject.gameObject;
if (destroyGameObject && gobj != null)
Expand Down
26 changes: 22 additions & 4 deletions com.unity.netcode.gameobjects/Tests/Runtime/DisconnectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,27 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client

if (clientDisconnectType == ClientDisconnectType.ServerDisconnectsClient)
{
m_ClientNetworkManagers[0].OnClientDisconnectCallback += OnClientDisconnectCallback;
m_ClientNetworkManagers[0].OnConnectionEvent += OnConnectionEvent;
clientManager.OnClientDisconnectCallback += OnClientDisconnectCallback;
clientManager.OnConnectionEvent += OnConnectionEvent;
m_ServerNetworkManager.OnConnectionEvent += OnConnectionEvent;
m_ServerNetworkManager.DisconnectClient(m_ClientId);
}
else
{
m_ServerNetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback;
m_ServerNetworkManager.OnConnectionEvent += OnConnectionEvent;
m_ClientNetworkManagers[0].OnConnectionEvent += OnConnectionEvent;
clientManager.OnConnectionEvent += OnConnectionEvent;

yield return StopOneClient(m_ClientNetworkManagers[0]);
yield return StopOneClient(clientManager);

if (clientManager.ConnectionManager != null)
{
Assert.False(clientManager.ConnectionManager.LocalClient.IsClient, $"{clientManager.name} still has IsClient setting!");
Assert.False(clientManager.ConnectionManager.LocalClient.IsConnected, $"{clientManager.name} still has IsConnected setting!");
Assert.False(clientManager.ConnectionManager.LocalClient.ClientId != 0, $"{clientManager.name} still has ClientId ({clientManager.ConnectionManager.LocalClient.ClientId}) setting!");
Assert.False(clientManager.ConnectionManager.LocalClient.IsApproved, $"{clientManager.name} still has IsApproved setting!");
Assert.IsNull(clientManager.ConnectionManager.LocalClient.PlayerObject, $"{clientManager.name} still has Player assigned!");
}
}

yield return WaitForConditionOrTimeOut(() => m_ClientDisconnected);
Expand Down Expand Up @@ -216,6 +225,15 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client

Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ServerNetworkManager), $"Could not find the server {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent[m_ServerNetworkManager].ClientId == NetworkManager.ServerClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the server {nameof(NetworkManager)} disconnect event entry!");
yield return s_DefaultWaitForTick;
if (m_ServerNetworkManager.ConnectionManager != null)
{
Assert.False(m_ServerNetworkManager.ConnectionManager.LocalClient.IsClient, $"{m_ServerNetworkManager.name} still has IsClient setting!");
Assert.False(m_ServerNetworkManager.ConnectionManager.LocalClient.IsConnected, $"{m_ServerNetworkManager.name} still has IsConnected setting!");
Assert.False(m_ServerNetworkManager.ConnectionManager.LocalClient.ClientId != 0, $"{m_ServerNetworkManager.name} still has ClientId ({clientManager.ConnectionManager.LocalClient.ClientId}) setting!");
Assert.False(m_ServerNetworkManager.ConnectionManager.LocalClient.IsApproved, $"{m_ServerNetworkManager.name} still has IsApproved setting!");
Assert.IsNull(m_ServerNetworkManager.ConnectionManager.LocalClient.PlayerObject, $"{m_ServerNetworkManager.name} still has Player assigned!");
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,5 +352,53 @@ public override void OnNetworkDespawn()
OnNetworkDespawnCalledCount++;
}
}

private bool AllClientsSpawnedObject()
{
foreach (var networkManager in m_ClientNetworkManagers)
{
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_SpawnedInstanceId))
{
return false;
}
}
return true;
}

private bool AllClientsDespawnedObject()
{
foreach (var networkManager in m_ClientNetworkManagers)
{
if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_SpawnedInstanceId))
{
return false;
}
}
return true;
}

private ulong m_SpawnedInstanceId;
/// <summary>
/// Validates that NetworkObject is reset properly when despawned but not destroyed.
/// </summary>
/// <returns>IEnumerator</returns>
[UnityTest]
public IEnumerator NetworkObjectResetOnDespawn()
{
var authorityNetworkManager = m_ServerNetworkManager;
var instance = SpawnObject(m_ObserverPrefab, authorityNetworkManager).GetComponent<NetworkObject>();
m_SpawnedInstanceId = instance.NetworkObjectId;
yield return WaitForConditionOrTimeOut(AllClientsSpawnedObject);
AssertOnTimeout($"Not all clients spawned an instance of {instance.name}!");

instance.Despawn(false);

yield return WaitForConditionOrTimeOut(AllClientsDespawnedObject);
AssertOnTimeout($"Not all clients de-spawned an instance of {instance.name}!");

Assert.IsNull(instance.GetNetworkParenting(), "Last parent was not reset!");

Object.Destroy(instance.gameObject);
}
}
}