Skip to content

Commit

Permalink
fix: prevent exception when showing despawned NetworkObject - up port…
Browse files Browse the repository at this point in the history
… of #3029 (#3030)

* fix

up-port of #3029

* test

up-port test of #3029 while also taking into consideration the two network topologies.

* update

Adding changelog entry.
  • Loading branch information
NoelStephensUnity committed Aug 26, 2024
1 parent 61a2d0d commit 40f84dc
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 8 deletions.
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

-Fixed issue where the `NetworkSpawnManager.HandleNetworkObjectShow` could throw an exception if one of the `NetworkObject` components to show was destroyed during the same frame. (#3030)
- Fixed issue where the `NetworkManagerHelper` was continuing to check for hierarchy changes when in play mode. (#3026)

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1581,20 +1581,46 @@ internal void HandleNetworkObjectShow()
{
foreach (var entry in ClientsToShowObject)
{
SendSpawnCallForObserverUpdate(entry.Value.ToArray(), entry.Key);
if (entry.Key != null && entry.Key.IsSpawned)
{
try
{
SendSpawnCallForObserverUpdate(entry.Value.ToArray(), entry.Key);
}
catch (Exception ex)
{
if (NetworkManager.LogLevel <= LogLevel.Developer)
{
Debug.LogException(ex);
}
}
}
}
ClientsToShowObject.Clear();
ObjectsToShowToClient.Clear();
return;
}

// Handle NetworkObjects to show
// Server or Host handling of NetworkObjects to show
foreach (var client in ObjectsToShowToClient)
{
ulong clientId = client.Key;
foreach (var networkObject in client.Value)
{
SendSpawnCallForObject(clientId, networkObject);
if (networkObject != null && networkObject.IsSpawned)
{
try
{
SendSpawnCallForObject(clientId, networkObject);
}
catch (Exception ex)
{
if (NetworkManager.LogLevel <= LogLevel.Developer)
{
Debug.LogException(ex);
}
}
}
}
}
ObjectsToShowToClient.Clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ namespace Unity.Netcode.RuntimeTests
internal class NetworkVisibilityTests : NetcodeIntegrationTest
{

protected override int NumberOfClients => 1;
protected override int NumberOfClients => 2;
private GameObject m_TestNetworkPrefab;
private bool m_SceneManagementEnabled;
private GameObject m_SpawnedObject;
private NetworkManager m_SessionOwner;

public NetworkVisibilityTests(SceneManagementState sceneManagementState, NetworkTopologyTypes networkTopologyType) : base(networkTopologyType)
{
Expand All @@ -27,7 +29,11 @@ protected override void OnServerAndClientsCreated()
{
m_TestNetworkPrefab = CreateNetworkObjectPrefab("Object");
m_TestNetworkPrefab.AddComponent<NetworkVisibilityComponent>();
m_ServerNetworkManager.NetworkConfig.EnableSceneManagement = m_SceneManagementEnabled;
if (!UseCMBService())
{
m_ServerNetworkManager.NetworkConfig.EnableSceneManagement = m_SceneManagementEnabled;
}

foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
clientNetworkManager.NetworkConfig.EnableSceneManagement = m_SceneManagementEnabled;
Expand All @@ -38,21 +44,58 @@ protected override void OnServerAndClientsCreated()

protected override IEnumerator OnServerAndClientsConnected()
{
SpawnObject(m_TestNetworkPrefab, m_ServerNetworkManager);
m_SessionOwner = UseCMBService() ? m_ClientNetworkManagers[0] : m_ServerNetworkManager;
m_SpawnedObject = SpawnObject(m_TestNetworkPrefab, m_SessionOwner);

yield return base.OnServerAndClientsConnected();
}

[UnityTest]
public IEnumerator HiddenObjectsTest()
{
var expectedCount = UseCMBService() ? 2 : 3;
#if UNITY_2023_1_OR_NEWER
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == 2);
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == expectedCount);
#else
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == 2);
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == expectedCount);
#endif

Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for the visible object count to equal 2!");
}

[UnityTest]
public IEnumerator HideShowAndDeleteTest()
{
var expectedCount = UseCMBService() ? 2 : 3;
#if UNITY_2023_1_OR_NEWER
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == expectedCount);
#else
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == expectedCount);
#endif
AssertOnTimeout("Timed out waiting for the visible object count to equal 2!");

var sessionOwnerNetworkObject = m_SpawnedObject.GetComponent<NetworkObject>();
var clientIndex = UseCMBService() ? 1 : 0;
sessionOwnerNetworkObject.NetworkHide(m_ClientNetworkManagers[clientIndex].LocalClientId);
#if UNITY_2023_1_OR_NEWER
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == expectedCount - 1);
#else
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == expectedCount - 1);
#endif
AssertOnTimeout($"Timed out waiting for {m_SpawnedObject.name} to be hidden from client!");
var networkObjectId = sessionOwnerNetworkObject.NetworkObjectId;
sessionOwnerNetworkObject.NetworkShow(m_ClientNetworkManagers[clientIndex].LocalClientId);
sessionOwnerNetworkObject.Despawn(true);

// Expect no exceptions
yield return s_DefaultWaitForTick;

// Now force a scenario where it normally would have caused an exception
m_SessionOwner.SpawnManager.ObjectsToShowToClient.Add(m_ClientNetworkManagers[clientIndex].LocalClientId, new System.Collections.Generic.List<NetworkObject>());
m_SessionOwner.SpawnManager.ObjectsToShowToClient[m_ClientNetworkManagers[clientIndex].LocalClientId].Add(null);

// Expect no exceptions
yield return s_DefaultWaitForTick;
}
}
}

0 comments on commit 40f84dc

Please sign in to comment.