Skip to content

Commit 40f84dc

Browse files
fix: prevent exception when showing despawned NetworkObject - up port 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.
1 parent 61a2d0d commit 40f84dc

File tree

3 files changed

+78
-8
lines changed

3 files changed

+78
-8
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1212

1313
### Fixed
1414

15+
-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)
1516
- Fixed issue where the `NetworkManagerHelper` was continuing to check for hierarchy changes when in play mode. (#3026)
1617

1718
### Changed

com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,20 +1581,46 @@ internal void HandleNetworkObjectShow()
15811581
{
15821582
foreach (var entry in ClientsToShowObject)
15831583
{
1584-
SendSpawnCallForObserverUpdate(entry.Value.ToArray(), entry.Key);
1584+
if (entry.Key != null && entry.Key.IsSpawned)
1585+
{
1586+
try
1587+
{
1588+
SendSpawnCallForObserverUpdate(entry.Value.ToArray(), entry.Key);
1589+
}
1590+
catch (Exception ex)
1591+
{
1592+
if (NetworkManager.LogLevel <= LogLevel.Developer)
1593+
{
1594+
Debug.LogException(ex);
1595+
}
1596+
}
1597+
}
15851598
}
15861599
ClientsToShowObject.Clear();
15871600
ObjectsToShowToClient.Clear();
15881601
return;
15891602
}
15901603

1591-
// Handle NetworkObjects to show
1604+
// Server or Host handling of NetworkObjects to show
15921605
foreach (var client in ObjectsToShowToClient)
15931606
{
15941607
ulong clientId = client.Key;
15951608
foreach (var networkObject in client.Value)
15961609
{
1597-
SendSpawnCallForObject(clientId, networkObject);
1610+
if (networkObject != null && networkObject.IsSpawned)
1611+
{
1612+
try
1613+
{
1614+
SendSpawnCallForObject(clientId, networkObject);
1615+
}
1616+
catch (Exception ex)
1617+
{
1618+
if (NetworkManager.LogLevel <= LogLevel.Developer)
1619+
{
1620+
Debug.LogException(ex);
1621+
}
1622+
}
1623+
}
15981624
}
15991625
}
16001626
ObjectsToShowToClient.Clear();

com.unity.netcode.gameobjects/Tests/Runtime/NetworkVisibilityTests.cs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ namespace Unity.Netcode.RuntimeTests
1414
internal class NetworkVisibilityTests : NetcodeIntegrationTest
1515
{
1616

17-
protected override int NumberOfClients => 1;
17+
protected override int NumberOfClients => 2;
1818
private GameObject m_TestNetworkPrefab;
1919
private bool m_SceneManagementEnabled;
20+
private GameObject m_SpawnedObject;
21+
private NetworkManager m_SessionOwner;
2022

2123
public NetworkVisibilityTests(SceneManagementState sceneManagementState, NetworkTopologyTypes networkTopologyType) : base(networkTopologyType)
2224
{
@@ -27,7 +29,11 @@ protected override void OnServerAndClientsCreated()
2729
{
2830
m_TestNetworkPrefab = CreateNetworkObjectPrefab("Object");
2931
m_TestNetworkPrefab.AddComponent<NetworkVisibilityComponent>();
30-
m_ServerNetworkManager.NetworkConfig.EnableSceneManagement = m_SceneManagementEnabled;
32+
if (!UseCMBService())
33+
{
34+
m_ServerNetworkManager.NetworkConfig.EnableSceneManagement = m_SceneManagementEnabled;
35+
}
36+
3137
foreach (var clientNetworkManager in m_ClientNetworkManagers)
3238
{
3339
clientNetworkManager.NetworkConfig.EnableSceneManagement = m_SceneManagementEnabled;
@@ -38,21 +44,58 @@ protected override void OnServerAndClientsCreated()
3844

3945
protected override IEnumerator OnServerAndClientsConnected()
4046
{
41-
SpawnObject(m_TestNetworkPrefab, m_ServerNetworkManager);
47+
m_SessionOwner = UseCMBService() ? m_ClientNetworkManagers[0] : m_ServerNetworkManager;
48+
m_SpawnedObject = SpawnObject(m_TestNetworkPrefab, m_SessionOwner);
4249

4350
yield return base.OnServerAndClientsConnected();
4451
}
4552

4653
[UnityTest]
4754
public IEnumerator HiddenObjectsTest()
4855
{
56+
var expectedCount = UseCMBService() ? 2 : 3;
4957
#if UNITY_2023_1_OR_NEWER
50-
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == 2);
58+
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == expectedCount);
5159
#else
52-
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == 2);
60+
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == expectedCount);
5361
#endif
5462

5563
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for the visible object count to equal 2!");
5664
}
65+
66+
[UnityTest]
67+
public IEnumerator HideShowAndDeleteTest()
68+
{
69+
var expectedCount = UseCMBService() ? 2 : 3;
70+
#if UNITY_2023_1_OR_NEWER
71+
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == expectedCount);
72+
#else
73+
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == expectedCount);
74+
#endif
75+
AssertOnTimeout("Timed out waiting for the visible object count to equal 2!");
76+
77+
var sessionOwnerNetworkObject = m_SpawnedObject.GetComponent<NetworkObject>();
78+
var clientIndex = UseCMBService() ? 1 : 0;
79+
sessionOwnerNetworkObject.NetworkHide(m_ClientNetworkManagers[clientIndex].LocalClientId);
80+
#if UNITY_2023_1_OR_NEWER
81+
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == expectedCount - 1);
82+
#else
83+
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == expectedCount - 1);
84+
#endif
85+
AssertOnTimeout($"Timed out waiting for {m_SpawnedObject.name} to be hidden from client!");
86+
var networkObjectId = sessionOwnerNetworkObject.NetworkObjectId;
87+
sessionOwnerNetworkObject.NetworkShow(m_ClientNetworkManagers[clientIndex].LocalClientId);
88+
sessionOwnerNetworkObject.Despawn(true);
89+
90+
// Expect no exceptions
91+
yield return s_DefaultWaitForTick;
92+
93+
// Now force a scenario where it normally would have caused an exception
94+
m_SessionOwner.SpawnManager.ObjectsToShowToClient.Add(m_ClientNetworkManagers[clientIndex].LocalClientId, new System.Collections.Generic.List<NetworkObject>());
95+
m_SessionOwner.SpawnManager.ObjectsToShowToClient[m_ClientNetworkManagers[clientIndex].LocalClientId].Add(null);
96+
97+
// Expect no exceptions
98+
yield return s_DefaultWaitForTick;
99+
}
57100
}
58101
}

0 commit comments

Comments
 (0)