fix: prevent exception when showing despawned or destroyed NetworkObject (#3029)

* fix

Adding protections against exceptions in the NetworkSpawnManager.HandleNetworkObjectShow method.

* test

validate the fix

* update

adding changelog entry.

* update

adding PR number to changelog entry.

* fix and style

Removing whitespace
Adding 2022.3 define for find objects.
This commit is contained in:
Noel Stephens 2024-08-26 17:54:03 -05:00 коммит произвёл GitHub
Родитель ca23b3c67f
Коммит a597b9f7ca
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 60 добавлений и 3 удалений

Просмотреть файл

@ -13,10 +13,12 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Fixed
- Fixed issue where collections v2.2.x was not supported when using UTP v2.2.x within Unity v2022.3. (#3033)
- Fixed issue where the `NetworkSpawnManager.HandleNetworkObjectShow` could throw an exception if one of the `NetworkObject` components to show was destroyed during the same frame. (#3029)
- Fixed issue where the `NetworkManagerHelper` was continuing to check for hierarchy changes when in play mode. (#3027)
### Changed
## [1.11.0] - 2024-08-20
### Added

Просмотреть файл

@ -1152,7 +1152,24 @@ namespace Unity.Netcode
ulong clientId = client.Key;
foreach (var networkObject in client.Value)
{
SendSpawnCallForObject(clientId, networkObject);
// Ignore if null or not spawned (v1.x.x the server should only show what is spawned)
if (networkObject != null && networkObject.IsSpawned)
{
// Prevent exceptions from interrupting this iteration
// so the ObjectsToShowToClient list will be fully processed
// and cleard.
try
{
SendSpawnCallForObject(clientId, networkObject);
}
catch (Exception ex)
{
if (NetworkManager.LogLevel <= LogLevel.Developer)
{
Debug.LogException(ex);
}
}
}
}
}
ObjectsToShowToClient.Clear();

Просмотреть файл

@ -20,6 +20,8 @@ namespace Unity.Netcode.RuntimeTests
private GameObject m_TestNetworkPrefab;
private bool m_SceneManagementEnabled;
private GameObject m_SpawnedObject;
public NetworkVisibilityTests(SceneManagementState sceneManagementState)
{
m_SceneManagementEnabled = sceneManagementState == SceneManagementState.SceneManagementEnabled;
@ -40,7 +42,7 @@ namespace Unity.Netcode.RuntimeTests
protected override IEnumerator OnServerAndClientsConnected()
{
SpawnObject(m_TestNetworkPrefab, m_ServerNetworkManager);
m_SpawnedObject = SpawnObject(m_TestNetworkPrefab, m_ServerNetworkManager);
yield return base.OnServerAndClientsConnected();
}
@ -54,7 +56,43 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == 2);
#endif
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for the visible object count to equal 2!");
AssertOnTimeout("Timed out waiting for the visible object count to equal 2!");
}
[UnityTest]
public IEnumerator HideShowAndDeleteTest()
{
#if UNITY_2023_1_OR_NEWER
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == 2);
#else
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == 2);
#endif
AssertOnTimeout("Timed out waiting for the visible object count to equal 2!");
var serverNetworkObject = m_SpawnedObject.GetComponent<NetworkObject>();
serverNetworkObject.NetworkHide(m_ClientNetworkManagers[0].LocalClientId);
#if UNITY_2023_1_OR_NEWER
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == 1);
#else
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == 1);
#endif
AssertOnTimeout($"Timed out waiting for {m_SpawnedObject.name} to be hidden from client!");
var networkObjectId = serverNetworkObject.NetworkObjectId;
serverNetworkObject.NetworkShow(m_ClientNetworkManagers[0].LocalClientId);
serverNetworkObject.Despawn(true);
// Expect no exceptions
yield return s_DefaultWaitForTick;
// Now force a scenario where it normally would have caused an exception
m_ServerNetworkManager.SpawnManager.ObjectsToShowToClient.Add(m_ClientNetworkManagers[0].LocalClientId, new System.Collections.Generic.List<NetworkObject>());
m_ServerNetworkManager.SpawnManager.ObjectsToShowToClient[m_ClientNetworkManagers[0].LocalClientId].Add(null);
// Expect no exceptions
yield return s_DefaultWaitForTick;
}
}
}