Skip to content

Commit cc817cf

Browse files
committed
InScenePlacedNetworkObjectTests rework
1 parent dca9712 commit cc817cf

File tree

7 files changed

+439
-455
lines changed

7 files changed

+439
-455
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1621,7 +1621,7 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
16211621
return;
16221622
}
16231623

1624-
if (destroyGameObject && networkObject.IsSceneObject == true && NetworkLog.CurrentLogLevel <= LogLevel.Normal)
1624+
if (destroyGameObject && networkObject.IsSceneObject == true && NetworkLog.CurrentLogLevel <= LogLevel.Developer)
16251625
{
16261626
Debug.LogWarning("Destroying in-scene network objects can lead to unexpected behavior. It is recommended to use NetworkObject.Despawn(false) instead.");
16271627
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
using System.Collections;
2+
using System.Linq;
3+
using Unity.Netcode;
4+
using Unity.Netcode.TestHelpers.Runtime;
5+
using UnityEngine.SceneManagement;
6+
7+
namespace TestProject.RuntimeTests
8+
{
9+
public class InScenePlacedNetworkObjectBase : IntegrationTestWithApproximation
10+
{
11+
protected override int NumberOfClients => 2;
12+
13+
internal const string SceneToLoad = "InSceneNetworkObject";
14+
private Scene m_AuthoritySideSceneLoaded;
15+
private Scene m_PreviousSceneLoaded;
16+
17+
protected InScenePlacedNetworkObjectBase(NetworkTopologyTypes networkTopologyType, HostOrServer hostOrServer) : base(networkTopologyType, hostOrServer) { }
18+
19+
// Constructor that is used by InScenePlacedNetworkObjectDestroyTests
20+
protected InScenePlacedNetworkObjectBase(NetworkTopologyTypes networkTopologyType) : base(networkTopologyType) { }
21+
22+
protected override IEnumerator OnSetup()
23+
{
24+
NetworkObjectTestComponent.Reset();
25+
NetworkObjectTestComponent.VerboseDebug = m_EnableVerboseDebug;
26+
m_AuthoritySideSceneLoaded = default;
27+
return base.OnSetup();
28+
}
29+
30+
/// <summary>
31+
/// Very important to always have a backup "unloading" catch
32+
/// in the event your test fails it could not potentially unload
33+
/// a scene and the proceeding tests could be impacted by this!
34+
/// </summary>
35+
/// <returns></returns>
36+
protected override IEnumerator OnTearDown()
37+
{
38+
GetAuthorityNetworkManager().SceneManager.OnSceneEvent -= OnSceneEvent;
39+
yield return CleanUpLoadedScene();
40+
}
41+
42+
protected override IEnumerator OnStartedServerAndClients()
43+
{
44+
GetAuthorityNetworkManager().SceneManager.OnSceneEvent += OnSceneEvent;
45+
return base.OnStartedServerAndClients();
46+
}
47+
48+
/// <summary>
49+
/// Gets the currently loaded scene.
50+
/// If multiple scenes are loaded this will get the most recently loaded scene
51+
/// </summary>
52+
internal Scene GetLoadedScene()
53+
{
54+
return m_AuthoritySideSceneLoaded;
55+
}
56+
57+
private void OnSceneEvent(SceneEvent sceneEvent)
58+
{
59+
switch (sceneEvent.SceneEventType)
60+
{
61+
case SceneEventType.Load:
62+
// Reset the loaded scene when the load starts to avoid data leaking
63+
// m_AuthoritySideSceneLoaded tracks the most recently loaded scene.
64+
// Any existing scene is no longer valid when a new scene begins loading.
65+
m_AuthoritySideSceneLoaded = default;
66+
break;
67+
case SceneEventType.LoadComplete:
68+
if (sceneEvent.ClientId == GetAuthorityNetworkManager().LocalClientId && sceneEvent.Scene.IsValid() && sceneEvent.Scene.isLoaded)
69+
{
70+
m_AuthoritySideSceneLoaded = sceneEvent.Scene;
71+
}
72+
break;
73+
case SceneEventType.Unload:
74+
m_PreviousSceneLoaded = m_AuthoritySideSceneLoaded;
75+
m_AuthoritySideSceneLoaded = default;
76+
break;
77+
}
78+
}
79+
80+
private IEnumerator CleanUpLoadedScene()
81+
{
82+
if (m_AuthoritySideSceneLoaded.IsValid() && m_AuthoritySideSceneLoaded.isLoaded)
83+
{
84+
VerboseDebug($"Cleaning up loaded scene [{m_AuthoritySideSceneLoaded.name}-{m_AuthoritySideSceneLoaded.handle}]");
85+
var authority = GetAuthorityNetworkManager();
86+
authority.SceneManager.UnloadScene(m_AuthoritySideSceneLoaded);
87+
yield return WaitForConditionOrTimeOut(() => m_ClientNetworkManagers.Any(c => c.IsListening));
88+
AssertOnTimeout($"[CleanUpLoadedScene] Timed out waiting for all in-scene instances to be despawned! Current spawned count: {m_ClientNetworkManagers.Count(c => !c.IsListening)}");
89+
}
90+
}
91+
92+
/// <summary>
93+
/// Checks if all clients have loaded the most recently loaded scene.
94+
/// </summary>
95+
internal bool HaveAllClientsLoadedScene()
96+
{
97+
if (!(m_AuthoritySideSceneLoaded.IsValid() && m_AuthoritySideSceneLoaded.isLoaded))
98+
{
99+
return false;
100+
}
101+
102+
foreach (var manager in m_NetworkManagers)
103+
{
104+
// default will have isLoaded as false so we can get the scene or default and test on isLoaded
105+
var loadedScene = manager.SceneManager.ScenesLoaded.Values.FirstOrDefault(scene => scene.name == m_AuthoritySideSceneLoaded.name);
106+
if (!loadedScene.isLoaded)
107+
{
108+
return false;
109+
}
110+
111+
if (manager.SceneManager.SceneEventProgressTracking.Count > 0)
112+
{
113+
return false;
114+
}
115+
}
116+
117+
return true;
118+
}
119+
120+
/// <summary>
121+
/// Checks if all clients have unloaded the most recently loaded scene
122+
/// </summary>
123+
internal bool HaveAllClientsUnloadedScene()
124+
{
125+
if (m_AuthoritySideSceneLoaded.IsValid() || m_AuthoritySideSceneLoaded.isLoaded)
126+
{
127+
return false;
128+
}
129+
130+
foreach (var manager in m_NetworkManagers)
131+
{
132+
if (manager.SceneManager.ScenesLoaded.Values.Any(scene => scene.name == m_PreviousSceneLoaded.name))
133+
{
134+
return false;
135+
}
136+
137+
if (manager.SceneManager.SceneEventProgressTracking.Count > 0)
138+
{
139+
return false;
140+
}
141+
}
142+
return true;
143+
}
144+
145+
146+
internal bool HaveAllClientsDespawnedInSceneObject()
147+
{
148+
// Make sure we despawned all instances
149+
if (NetworkObjectTestComponent.DespawnedInstances.Count < TotalClients)
150+
{
151+
return false;
152+
}
153+
154+
foreach (var despawnedInstance in NetworkObjectTestComponent.DespawnedInstances)
155+
{
156+
if (despawnedInstance && despawnedInstance.gameObject && despawnedInstance.gameObject.activeInHierarchy)
157+
{
158+
return false;
159+
}
160+
}
161+
162+
return true;
163+
}
164+
165+
internal bool HaveAllClientsSpawnedInSceneObject()
166+
{
167+
// Make sure we despawned all instances
168+
if (NetworkObjectTestComponent.SpawnedInstances.Count < TotalClients)
169+
{
170+
return false;
171+
}
172+
173+
foreach (var despawnedInstance in NetworkObjectTestComponent.SpawnedInstances)
174+
{
175+
if (!despawnedInstance.gameObject.activeInHierarchy)
176+
{
177+
return false;
178+
}
179+
}
180+
181+
return true;
182+
}
183+
}
184+
}

testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObjectBase.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObjectDestroyTests.cs

Lines changed: 25 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,31 @@
22
using System.Linq;
33
using NUnit.Framework;
44
using Unity.Netcode;
5-
using Unity.Netcode.TestHelpers.Runtime;
5+
using UnityEngine;
66
using UnityEngine.SceneManagement;
77
using UnityEngine.TestTools;
88

99
namespace TestProject.RuntimeTests
1010
{
11+
/// <summary>
12+
/// These tests extend off InScenePlacedNetworkObjectTests.
13+
/// Extending allows the DeferDespawn tests to not be created for client-server topology.
14+
/// These tests specifically test destroying and despawning in-scene placed network objects.
15+
/// </summary>
1116
[TestFixture(NetworkTopologyTypes.DistributedAuthority, DespawnMode.Despawn)]
1217
[TestFixture(NetworkTopologyTypes.DistributedAuthority, DespawnMode.DeferDespawn)]
1318
[TestFixture(NetworkTopologyTypes.ClientServer, DespawnMode.Despawn)]
14-
public class InScenePlacedNetworkObjectDestroyTests : IntegrationTestWithApproximation
19+
public class InScenePlacedNetworkObjectDestroyTests : InScenePlacedNetworkObjectBase
1520
{
1621
protected override int NumberOfClients => 2;
1722

18-
private const string k_SceneToLoad = "InSceneNetworkObject";
19-
private Scene m_ServerSideSceneLoaded;
20-
21-
// private string m_SceneLoading = k_SceneToLoad;
2223
private readonly DespawnMode m_DespawnMode;
2324

2425
public InScenePlacedNetworkObjectDestroyTests(NetworkTopologyTypes networkTopologyType, DespawnMode despawnMode) : base(networkTopologyType)
2526
{
2627
m_DespawnMode = despawnMode;
2728
}
2829

29-
protected override IEnumerator OnSetup()
30-
{
31-
NetworkObjectTestComponent.VerboseDebug = m_EnableVerboseDebug;
32-
return base.OnSetup();
33-
}
34-
35-
/// <summary>
36-
/// Very important to always have a backup "unloading" catch
37-
/// in the event your test fails it could not potentially unload
38-
/// a scene and the proceeding tests could be impacted by this!
39-
/// </summary>
40-
/// <returns></returns>
41-
protected override IEnumerator OnTearDown()
42-
{
43-
NetworkObjectTestComponent.Reset();
44-
yield return CleanUpLoadedScene();
45-
}
46-
4730
public enum DespawnMode
4831
{
4932
Despawn,
@@ -53,7 +36,14 @@ public enum DespawnMode
5336
private enum DestroyMode
5437
{
5538
DestroyGameObject,
56-
DontDestroyGameObject,
39+
DespawnGameObject,
40+
}
41+
42+
private NetworkObject m_JoinedClientDespawnedNetworkObject;
43+
private void OnInSceneObjectDespawned(NetworkObject networkObject)
44+
{
45+
m_JoinedClientDespawnedNetworkObject = networkObject;
46+
NetworkObjectTestComponent.OnInSceneObjectDespawned -= OnInSceneObjectDespawned;
5747
}
5848

5949
/// <summary>
@@ -76,17 +66,16 @@ private IEnumerator LoadSceneAndDespawnObject(DestroyMode destroyMode)
7666
var authority = GetAuthorityNetworkManager();
7767
var destroyGameObject = destroyMode == DestroyMode.DestroyGameObject;
7868

79-
authority.SceneManager.OnSceneEvent += Server_OnSceneEvent;
8069
VerboseDebug("Loading scene");
81-
var status = authority.SceneManager.LoadScene(k_SceneToLoad, LoadSceneMode.Additive);
82-
Assert.IsTrue(status == SceneEventProgressStatus.Started, $"When attempting to load scene {k_SceneToLoad} was returned the following progress status: {status}");
70+
var status = authority.SceneManager.LoadScene(SceneToLoad, LoadSceneMode.Additive);
71+
Assert.IsTrue(status == SceneEventProgressStatus.Started, $"When attempting to load scene {SceneToLoad} was returned the following progress status: {status}");
8372

8473
// This verifies the scene loaded and the in-scene placed NetworkObjects spawned.
85-
yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == TotalClients);
74+
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawnedInSceneObject);
8675
AssertOnTimeout($"Timed out waiting for total spawned in-scene placed NetworkObjects to reach a count of {TotalClients} and is currently {NetworkObjectTestComponent.SpawnedInstances.Count}");
8776

88-
yield return WaitForConditionOrTimeOut(() => m_ServerSideSceneLoaded.IsValid() && m_ServerSideSceneLoaded.isLoaded);
89-
AssertOnTimeout($"Timed out waiting for server to finish loading scene {k_SceneToLoad}!");
77+
yield return WaitForConditionOrTimeOut(HaveAllClientsLoadedScene);
78+
AssertOnTimeout($"Timed out waiting for all clients to finish loading scene {SceneToLoad}!");
9079

9180
// Get the server-side instance of the in-scene NetworkObject
9281
Assert.True(s_GlobalNetworkObjects.ContainsKey(authority.LocalClientId), "Could not find server instance of the test in-scene NetworkObject!");
@@ -96,7 +85,7 @@ private IEnumerator LoadSceneAndDespawnObject(DestroyMode destroyMode)
9685
Assert.IsNotNull(serverObject, "Could not find server-side in-scene placed NetworkObject!");
9786
Assert.IsTrue(serverObject.IsSpawned, $"{serverObject.name} is not spawned!");
9887

99-
VerboseDebug("Doing despawn");
88+
VerboseDebug($"Doing despawn. destroyGameObject: {destroyGameObject}");
10089
// Despawn the in-scene placed NetworkObject
10190
if (m_DespawnMode == DespawnMode.Despawn)
10291
{
@@ -110,10 +99,9 @@ private IEnumerator LoadSceneAndDespawnObject(DestroyMode destroyMode)
11099
yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == 0);
111100
AssertOnTimeout($"Timed out waiting for all in-scene instances to be despawned! Current spawned count: {NetworkObjectTestComponent.SpawnedInstances.Count()}");
112101

113-
114102
foreach (var manager in m_NetworkManagers)
115103
{
116-
Assert.False(manager.SpawnManager.SpawnedObjects.ContainsKey(serverObjectId));
104+
Assert.False(manager.SpawnManager.SpawnedObjects.ContainsKey(serverObjectId), $"Client-{manager.LocalClientId} still has in-scene instance spawned!");
117105
}
118106

119107
foreach (var spawnedObject in spawnedObjects)
@@ -139,7 +127,7 @@ private IEnumerator LoadSceneAndDespawnObject(DestroyMode destroyMode)
139127
[UnityTest]
140128
public IEnumerator InSceneNetworkObjectDespawnSyncAndSpawn()
141129
{
142-
yield return LoadSceneAndDespawnObject(DestroyMode.DontDestroyGameObject);
130+
yield return LoadSceneAndDespawnObject(DestroyMode.DespawnGameObject);
143131

144132
var serverObject = NetworkObjectTestComponent.ServerNetworkObjectInstance;
145133

@@ -163,7 +151,7 @@ public IEnumerator InSceneNetworkObjectDespawnSyncAndSpawn()
163151
// Now test that the despawned in-scene placed NetworkObject can be re-spawned (without having been registered as a NetworkPrefab)
164152
serverObject.Spawn();
165153

166-
yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == TotalClients);
154+
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawnedInSceneObject);
167155
AssertOnTimeout($"Timed out waiting for all in-scene instances to be spawned! Current spawned count: {NetworkObjectTestComponent.SpawnedInstances.Count()} | Expected spawn count: {TotalClients}");
168156

169157
VerboseDebug("Network hiding object on first client");
@@ -181,38 +169,8 @@ public IEnumerator InSceneNetworkObjectDespawnSyncAndSpawn()
181169
// Validate that the first client can spawn the "netcode hidden" in-scene placed NetworkObject
182170
serverObject.NetworkShow(firstClientId);
183171

184-
yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == TotalClients);
172+
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawnedInSceneObject);
185173
AssertOnTimeout($"[NetworkShow] Timed out waiting for Client-{firstClientId} to spawn the in-scene placed NetworkObject! Current spawned count: {NetworkObjectTestComponent.SpawnedInstances.Count()} | Expected spawn count: {TotalClients}");
186-
187-
yield return CleanUpLoadedScene();
188-
}
189-
190-
private NetworkObject m_JoinedClientDespawnedNetworkObject;
191-
192-
private void OnInSceneObjectDespawned(NetworkObject networkObject)
193-
{
194-
m_JoinedClientDespawnedNetworkObject = networkObject;
195-
NetworkObjectTestComponent.OnInSceneObjectDespawned -= OnInSceneObjectDespawned;
196-
}
197-
198-
private void Server_OnSceneEvent(SceneEvent sceneEvent)
199-
{
200-
if (sceneEvent.ClientId == GetAuthorityNetworkManager().LocalClientId && sceneEvent.SceneEventType == SceneEventType.LoadComplete
201-
&& sceneEvent.Scene.IsValid() && sceneEvent.Scene.isLoaded)
202-
{
203-
m_ServerSideSceneLoaded = sceneEvent.Scene;
204-
GetAuthorityNetworkManager().SceneManager.OnSceneEvent -= Server_OnSceneEvent;
205-
}
206-
}
207-
208-
private IEnumerator CleanUpLoadedScene()
209-
{
210-
if (m_ServerSideSceneLoaded.IsValid() && m_ServerSideSceneLoaded.isLoaded)
211-
{
212-
GetAuthorityNetworkManager().SceneManager.UnloadScene(m_ServerSideSceneLoaded);
213-
yield return WaitForConditionOrTimeOut(() => m_ClientNetworkManagers.Any(c => c.IsListening));
214-
AssertOnTimeout($"[CleanUpLoadedScene] Timed out waiting for all in-scene instances to be despawned! Current spawned count: {m_ClientNetworkManagers.Count(c => !c.IsListening)}");
215-
}
216174
}
217175
}
218176
}

0 commit comments

Comments
 (0)