Skip to content

Commit

Permalink
fix: prevent exception when showing despawned or destroyed NetworkObj…
Browse files Browse the repository at this point in the history
…ect (#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.
  • Loading branch information
NoelStephensUnity authored Aug 26, 2024
1 parent ca23b3c commit a597b9f
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 3 deletions.
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,24 @@ internal void HandleNetworkObjectShow()
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public enum SceneManagementState
private GameObject m_TestNetworkPrefab;
private bool m_SceneManagementEnabled;

private GameObject m_SpawnedObject;

public NetworkVisibilityTests(SceneManagementState sceneManagementState)
{
m_SceneManagementEnabled = sceneManagementState == SceneManagementState.SceneManagementEnabled;
Expand All @@ -40,7 +42,7 @@ protected override void OnServerAndClientsCreated()

protected override IEnumerator OnServerAndClientsConnected()
{
SpawnObject(m_TestNetworkPrefab, m_ServerNetworkManager);
m_SpawnedObject = SpawnObject(m_TestNetworkPrefab, m_ServerNetworkManager);

yield return base.OnServerAndClientsConnected();
}
Expand All @@ -54,7 +56,43 @@ public IEnumerator HiddenObjectsTest()
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;
}
}
}

0 comments on commit a597b9f

Please sign in to comment.