Skip to content

Commit d220adf

Browse files
fix: ValidatesSceneBeforeLoading halts client synchronization (#1883)
* fix MTT-3285 MTT-3285 This fixes the issue with ValidateSceneBeforeLoading not continuing to synchronize if one scene is skipped over. * update adding some debug information (LogLevel.Developer) so users can track when a client declines to load a scene * fix When a scene is not validated on the client side it should invoke HandleClientSceneEvent not ClientLoadedSynchronization * test MTT-3285 Integration test * style * Update CHANGELOG.md * Update CHANGELOG.md * style removing space in assert message * test fix unload the loaded scenes. * Update CHANGELOG.md
1 parent e4d5166 commit d220adf

File tree

4 files changed

+150
-10
lines changed

4 files changed

+150
-10
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
1414
### Removed
1515

1616
### Fixed
17-
- Fixed client throwing an exception if it has messages in the outbound queue when processing the NetworkEvent.Disconnect event and is using UTP. (#1884)
17+
- Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884)
18+
- Fixed issue during client synchronization if 'ValidateSceneBeforeLoading' returned false it would halt the client synchronization process resulting in a client that was approved but not synchronized or fully connected with the server. (#1883)
1819

1920
## [1.0.0-pre.7] - 2022-04-06
2021

@@ -39,6 +40,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
3940
- Removed `com.unity.collections` dependency from the package (#1849)
4041

4142
### Fixed
43+
4244
- Fixed in-scene placed NetworkObjects not being found/ignored after a client disconnects and then reconnects. (#1850)
4345
- Fixed issue where `UnityTransport` send queues were not flushed when calling `DisconnectLocalClient` or `DisconnectRemoteClient`. (#1847)
4446
- Fixed NetworkBehaviour dependency verification check for an existing NetworkObject not searching from root parent transform relative GameObject. (#1841)

com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,12 +1446,9 @@ private void OnClientBeginSync(uint sceneEventId)
14461446

14471447
var loadSceneMode = sceneHash == sceneEventData.SceneHash ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive;
14481448

1449-
// Always check to see if the scene needs to be validated
1450-
if (!ValidateSceneBeforeLoading(sceneHash, loadSceneMode))
1451-
{
1452-
EndSceneEvent(sceneEventId);
1453-
return;
1454-
}
1449+
// Store the sceneHandle and hash
1450+
sceneEventData.ClientSceneHandle = sceneHandle;
1451+
sceneEventData.ClientSceneHash = sceneHash;
14551452

14561453
// If this is the beginning of the synchronization event, then send client a notification that synchronization has begun
14571454
if (sceneHash == sceneEventData.SceneHash)
@@ -1468,9 +1465,16 @@ private void OnClientBeginSync(uint sceneEventId)
14681465
ScenePlacedObjects.Clear();
14691466
}
14701467

1471-
// Store the sceneHandle and hash
1472-
sceneEventData.ClientSceneHandle = sceneHandle;
1473-
sceneEventData.ClientSceneHash = sceneHash;
1468+
// Always check to see if the scene needs to be validated
1469+
if (!ValidateSceneBeforeLoading(sceneHash, loadSceneMode))
1470+
{
1471+
HandleClientSceneEvent(sceneEventId);
1472+
if (m_NetworkManager.LogLevel == LogLevel.Developer)
1473+
{
1474+
NetworkLog.LogInfo($"Client declined to load the scene {sceneName}, continuing with synchronization.");
1475+
}
1476+
return;
1477+
}
14741478

14751479
var shouldPassThrough = false;
14761480
var sceneLoad = (AsyncOperation)null;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using NUnit.Framework;
4+
using UnityEngine;
5+
using UnityEngine.SceneManagement;
6+
using UnityEngine.TestTools;
7+
using Unity.Netcode;
8+
using Unity.Netcode.TestHelpers.Runtime;
9+
10+
namespace TestProject.RuntimeTests
11+
{
12+
public class ClientSynchronizationValidationTest : NetcodeIntegrationTest
13+
{
14+
protected override int NumberOfClients => 1;
15+
private const string k_FirstSceneToLoad = "UnitTestBaseScene";
16+
private const string k_SecondSceneToSkip = "InSceneNetworkObject";
17+
private const string k_ThirdSceneToLoad = "EmptyScene";
18+
private List<ClientSceneVerificationHandler> m_ClientSceneVerifiers = new List<ClientSceneVerificationHandler>();
19+
20+
protected override void OnOneTimeSetup()
21+
{
22+
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
23+
// Pre-load some scenes (i.e. server will tell clients to synchronize to these scenes)
24+
SceneManager.LoadSceneAsync(k_FirstSceneToLoad, LoadSceneMode.Additive);
25+
SceneManager.LoadSceneAsync(k_SecondSceneToSkip, LoadSceneMode.Additive);
26+
SceneManager.LoadSceneAsync(k_ThirdSceneToLoad, LoadSceneMode.Additive);
27+
base.OnOneTimeSetup();
28+
}
29+
private List<Scene> m_ScenesLoaded = new List<Scene>();
30+
private void SceneManager_sceneLoaded(Scene scene, LoadSceneMode mode)
31+
{
32+
m_ScenesLoaded.Add(scene);
33+
}
34+
35+
protected override IEnumerator OnStartedServerAndClients()
36+
{
37+
// Create ClientSceneVerificationHandlers for each client
38+
foreach (var client in m_ClientNetworkManagers)
39+
{
40+
m_ClientSceneVerifiers.Add(new ClientSceneVerificationHandler(client));
41+
}
42+
return base.OnStartedServerAndClients();
43+
}
44+
45+
[UnityTest]
46+
public IEnumerator ClientVerifySceneBeforeLoading()
47+
{
48+
// If we made it here it means that all clients finished synchronizing
49+
// Now check to make sure only the two scenes were loaded and one
50+
// completely skipped.
51+
foreach (var clientSceneVerifier in m_ClientSceneVerifiers)
52+
{
53+
clientSceneVerifier.ValidateScenesLoaded();
54+
}
55+
yield return null;
56+
}
57+
58+
protected override IEnumerator OnTearDown()
59+
{
60+
foreach (var scene in m_ScenesLoaded)
61+
{
62+
if (scene.isLoaded)
63+
{
64+
SceneManager.UnloadSceneAsync(scene);
65+
}
66+
}
67+
m_ScenesLoaded.Clear();
68+
return base.OnTearDown();
69+
}
70+
71+
/// <summary>
72+
/// Determines if all clients loaded only two of the 3 scenes
73+
/// </summary>
74+
private class ClientSceneVerificationHandler
75+
{
76+
private NetworkManager m_NetworkManager;
77+
private Dictionary<string, int> m_ValidSceneEventCount = new Dictionary<string, int>();
78+
public ClientSceneVerificationHandler(NetworkManager networkManager)
79+
{
80+
m_NetworkManager = networkManager;
81+
m_NetworkManager.SceneManager.DisableValidationWarnings(true);
82+
m_NetworkManager.SceneManager.VerifySceneBeforeLoading = VerifySceneBeforeLoading;
83+
m_NetworkManager.SceneManager.OnLoad += ClientSceneManager_OnLoad;
84+
m_NetworkManager.SceneManager.OnLoadComplete += ClientSceneManager_OnLoadComplete;
85+
m_ValidSceneEventCount.Add(k_FirstSceneToLoad, 0);
86+
m_ValidSceneEventCount.Add(k_SecondSceneToSkip, 0);
87+
m_ValidSceneEventCount.Add(k_ThirdSceneToLoad, 0);
88+
}
89+
90+
public void ValidateScenesLoaded()
91+
{
92+
Assert.IsFalse(m_ValidSceneEventCount[k_SecondSceneToSkip] > 0, $"Client still loaded the invalidated scene {k_SecondSceneToSkip}!");
93+
Assert.IsTrue(m_ValidSceneEventCount[k_FirstSceneToLoad] == 2, $"Client did not load and process the validated scene {k_FirstSceneToLoad}!");
94+
Assert.IsTrue(m_ValidSceneEventCount[k_ThirdSceneToLoad] == 2, $"Client did not load and process the validated scene {k_ThirdSceneToLoad}!");
95+
}
96+
97+
private void ClientSceneManager_OnLoadComplete(ulong clientId, string sceneName, LoadSceneMode loadSceneMode)
98+
{
99+
if (m_ValidSceneEventCount.ContainsKey(sceneName))
100+
{
101+
m_ValidSceneEventCount[sceneName]++;
102+
}
103+
}
104+
105+
private void ClientSceneManager_OnLoad(ulong clientId, string sceneName, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation)
106+
{
107+
if (m_ValidSceneEventCount.ContainsKey(sceneName))
108+
{
109+
m_ValidSceneEventCount[sceneName]++;
110+
}
111+
}
112+
113+
private bool VerifySceneBeforeLoading(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
114+
{
115+
if (sceneName == k_SecondSceneToSkip)
116+
{
117+
return false;
118+
}
119+
return true;
120+
}
121+
}
122+
}
123+
}

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

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

0 commit comments

Comments
 (0)