Skip to content

Commit b91cf27

Browse files
fix: NetworkAnimator does not unsubscribe to OnClientConnectedCallback [MTT-4080] (#2074)
1 parent ebfd5f6 commit b91cf27

File tree

4 files changed

+129
-10
lines changed

4 files changed

+129
-10
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
8-
98
## [Unreleased]
109

1110
### Changed
1211

1312
### Fixed
1413

14+
- Fixed issue where NetworkAnimator was not removing its subscription from OnClientConnectedCallback when despawned during the shutdown sequence. (#2074)
15+
- Fixed IsServer and IsClient being set to false before object despawn during the shutdown sequence. (#2074)
1516
- Fixed NetworkLists not populating on client. NetworkList now uses the most recent list as opposed to the list at the end of previous frame, when sending full updates to dynamically spawned NetworkObject. The difference in behaviour is required as scene management spawns those objects at a different time in the frame, relative to updates. (#2062)
16-
1717
- Fixed NetworkList Value event on the server. PreviousValue is now set correctly when a new value is set through property setter. (#2067)
1818

1919
## [1.0.0] - 2022-06-27

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,19 @@ internal void ShutdownInternal()
13841384
}
13851385

13861386
IsConnectedClient = false;
1387+
1388+
// We need to clean up NetworkObjects before we reset the IsServer
1389+
// and IsClient properties. This provides consistency of these two
1390+
// property values for NetworkObjects that are still spawned when
1391+
// the shutdown cycle begins.
1392+
if (SpawnManager != null)
1393+
{
1394+
SpawnManager.DespawnAndDestroyNetworkObjects();
1395+
SpawnManager.ServerResetShudownStateForSceneObjects();
1396+
1397+
SpawnManager = null;
1398+
}
1399+
13871400
IsServer = false;
13881401
IsClient = false;
13891402

@@ -1406,14 +1419,6 @@ internal void ShutdownInternal()
14061419
NetworkConfig.NetworkTransport.OnTransportEvent -= HandleRawTransportPoll;
14071420
}
14081421

1409-
if (SpawnManager != null)
1410-
{
1411-
SpawnManager.DespawnAndDestroyNetworkObjects();
1412-
SpawnManager.ServerResetShudownStateForSceneObjects();
1413-
1414-
SpawnManager = null;
1415-
}
1416-
14171422
if (DeferredMessageManager != null)
14181423
{
14191424
DeferredMessageManager.CleanupAllTriggers();

testproject/Assets/Tests/Runtime/Animation/AnimatorTestHelper.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using UnityEngine;
34
using Unity.Netcode;
@@ -49,8 +50,14 @@ public override void OnNetworkSpawn()
4950
base.OnNetworkSpawn();
5051
}
5152

53+
public Action<bool, bool> OnCheckIsServerIsClient;
54+
5255
public override void OnNetworkDespawn()
5356
{
57+
// This verifies the issue where IsServer and IsClient were
58+
// being reset prior to NetworkObjects being despawned during
59+
// the shutdown period.
60+
OnCheckIsServerIsClient?.Invoke(IsServer, IsClient);
5461
if (ClientSideInstances.ContainsKey(NetworkManager.LocalClientId))
5562
{
5663
ClientSideInstances.Remove(NetworkManager.LocalClientId);

testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
using System.Collections;
3+
using System.Linq;
34
using NUnit.Framework;
45
using UnityEngine;
56
using UnityEngine.TestTools;
@@ -383,5 +384,111 @@ public IEnumerator LateJoinSynchronizationTest([Values] OwnerShipMode ownerShipM
383384
yield return StopOneClient(newlyJoinedClient);
384385
VerboseDebug($" ------------------ Late-Join Test [{TriggerTest.Iteration}][{ownerShipMode}] Stopping ------------------ ");
385386
}
387+
388+
private bool m_ClientDisconnected;
389+
/// <summary>
390+
/// This validates that NetworkAnimator properly removes its subscription to the
391+
/// OnClientConnectedCallback when it is despawned and destroyed during the
392+
/// shutdown sequence on both the server and the client.
393+
/// </summary>
394+
[UnityTest]
395+
public IEnumerator ShutdownWhileSpawnedAndStartBackUpTest()
396+
{
397+
VerboseDebug($" ++++++++++++++++++ Disconnect-Reconnect Server Test Starting ++++++++++++++++++ ");
398+
// Spawn our test animator object
399+
var objectInstance = SpawnPrefab(false, AuthoritativeMode.ServerAuth);
400+
var networkObjectInstance = objectInstance.GetComponent<NetworkObject>();
401+
var serverAnimatorTestHelper = objectInstance.GetComponent<AnimatorTestHelper>();
402+
m_ServerNetworkManager.OnClientDisconnectCallback += ServerNetworkManager_OnClientDisconnectCallback;
403+
// Wait for it to spawn server-side
404+
yield return WaitForConditionOrTimeOut(() => AnimatorTestHelper.ServerSideInstance != null);
405+
AssertOnTimeout($"Timed out waiting for the server-side instance of {GetNetworkAnimatorName(AuthoritativeMode.ServerAuth)} to be spawned!");
406+
407+
// Wait for it to spawn client-side
408+
yield return WaitForConditionOrTimeOut(WaitForClientsToInitialize);
409+
AssertOnTimeout($"Timed out waiting for the client-side instance of {GetNetworkAnimatorName(AuthoritativeMode.ServerAuth)} to be spawned!");
410+
411+
var clientAnimatorTestHelper = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId].Values.Where((c) => c.GetComponent<AnimatorTestHelper>() != null).First().GetComponent<AnimatorTestHelper>();
412+
Assert.IsNotNull(clientAnimatorTestHelper, $"Could not find the client side {nameof(AnimatorTestHelper)}!");
413+
VerboseDebug($" ++++++++++++++++++ Disconnect-Reconnect Shutting Down Client and Server ++++++++++++++++++ ");
414+
clientAnimatorTestHelper.OnCheckIsServerIsClient += Client_OnCheckIsServerIsClient;
415+
416+
// Now shutdown the client-side to verify this fix.
417+
// The client-side spawned NetworkObject should get despawned
418+
// and invoke the Client_OnCheckIsServerIsClient action.
419+
m_ClientNetworkManagers[0].Shutdown(true);
420+
421+
// Wait for the server to receive the client disconnection notification
422+
yield return WaitForConditionOrTimeOut(() => m_ClientDisconnected);
423+
AssertOnTimeout($"Timed out waiting for the client to disconnect!");
424+
425+
Assert.IsTrue(m_ClientTestHelperDespawned, $"Client-Side {nameof(AnimatorTestHelper)} did not have a valid IsClient setting!");
426+
427+
serverAnimatorTestHelper.OnCheckIsServerIsClient += Server_OnCheckIsServerIsClient;
428+
m_ServerNetworkManager.OnClientDisconnectCallback -= ServerNetworkManager_OnClientDisconnectCallback;
429+
430+
// Now shutdown the server-side to verify this fix.
431+
// The server-side spawned NetworkObject should get despawned
432+
// and invoke the Server_OnCheckIsServerIsClient action.
433+
m_ServerNetworkManager.Shutdown();
434+
435+
yield return s_DefaultWaitForTick;
436+
437+
yield return WaitForConditionOrTimeOut(() => !m_ServerNetworkManager.ShutdownInProgress);
438+
439+
Assert.IsTrue(m_ServerTestHelperDespawned, $"Server-Side {nameof(AnimatorTestHelper)} did not have a valid IsServer setting!");
440+
AssertOnTimeout($"Timed out waiting for the server to shutdown!");
441+
442+
VerboseDebug($" ++++++++++++++++++ Disconnect-Reconnect Restarting Server and Client ++++++++++++++++++ ");
443+
// Since the dynamically generated PlayerPrefab is destroyed when the server shuts down,
444+
// we need to create a new one and assign it to NetworkPrefab index 0
445+
m_PlayerPrefab = new GameObject("Player");
446+
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();
447+
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
448+
m_ServerNetworkManager.NetworkConfig.NetworkPrefabs[0].Prefab = m_PlayerPrefab;
449+
m_ClientNetworkManagers[0].NetworkConfig.NetworkPrefabs[0].Prefab = m_PlayerPrefab;
450+
OnCreatePlayerPrefab();
451+
452+
// Now, restart the server and the client
453+
m_ServerNetworkManager.StartHost();
454+
m_ClientNetworkManagers[0].StartClient();
455+
456+
// Wait for the server and client to start and connect
457+
yield return WaitForClientsConnectedOrTimeOut();
458+
459+
VerboseDebug($" ++++++++++++++++++ Disconnect-Reconnect Server Test Stopping ++++++++++++++++++ ");
460+
}
461+
462+
private bool m_ServerTestHelperDespawned;
463+
/// <summary>
464+
/// Server-Side
465+
/// This callback will be invoked as the spawned prefab is destroyed during shutdown
466+
/// </summary>
467+
private void Server_OnCheckIsServerIsClient(bool isServer, bool isClient)
468+
{
469+
// Validates this is still set when the NetworkObject is despawned during shutdown
470+
Assert.IsTrue(isServer);
471+
m_ServerTestHelperDespawned = true;
472+
}
473+
474+
private bool m_ClientTestHelperDespawned;
475+
/// <summary>
476+
/// Client-Side
477+
/// This callback will be invoked as the spawned prefab is destroyed during shutdown
478+
/// </summary>
479+
private void Client_OnCheckIsServerIsClient(bool isServer, bool isClient)
480+
{
481+
// Validates this is still set when the NetworkObject is despawned during shutdown
482+
Assert.IsTrue(isClient);
483+
m_ClientTestHelperDespawned = true;
484+
}
485+
486+
/// <summary>
487+
/// Verifies the client has disconnected
488+
/// </summary>
489+
private void ServerNetworkManager_OnClientDisconnectCallback(ulong obj)
490+
{
491+
m_ClientDisconnected = true;
492+
}
386493
}
387494
}

0 commit comments

Comments
 (0)