Skip to content

fix: server scene exclusion during synchronization for runtime generated scenes #2550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

- Fixed issue where some temporary debug console logging was left in a merged PR. (#2562)
- Fixed the "Generate Default Network Prefabs List" setting not loading correctly and always reverting to being checked. (#2545)
- Fixed issue where users could not use NetworkSceneManager.VerifySceneBeforeLoading to exclude runtime generated scenes from client synchronization. (#2550)
- Fixed missing value on `NetworkListEvent` for `EventType.RemoveAt` events. (#2542,#2543)
- Fixed issue where parenting a NetworkTransform under a transform with a scale other than Vector3.one would result in incorrect values on non-authoritative instances. (#2538)
- Fixed issue where a server would include scene migrated and then despawned NetworkObjects to a client that was being synchronized. (#2532)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -786,9 +786,18 @@ private void SceneManager_ActiveSceneChanged(Scene current, Scene next)
/// <returns>true (Valid) or false (Invalid)</returns>
internal bool ValidateSceneBeforeLoading(uint sceneHash, LoadSceneMode loadSceneMode)
{
var validated = true;
var sceneName = SceneNameFromHash(sceneHash);
var sceneIndex = SceneUtility.GetBuildIndexByScenePath(sceneName);
return ValidateSceneBeforeLoading(sceneIndex, sceneName, loadSceneMode);
}

/// <summary>
/// Overloaded version that is invoked by <see cref="ValidateSceneBeforeLoading"/> and <see cref="SynchronizeNetworkObjects"/>.
/// This specifically is to allow runtime generated scenes to be excluded by the server during synchronization.
/// </summary>
internal bool ValidateSceneBeforeLoading(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
{
var validated = true;
if (VerifySceneBeforeLoading != null)
{
validated = VerifySceneBeforeLoading.Invoke(sceneIndex, sceneName, loadSceneMode);
Expand Down Expand Up @@ -1744,24 +1753,22 @@ internal void SynchronizeNetworkObjects(ulong clientId)
continue;
}

var sceneHash = SceneHashFromNameOrPath(scene.path);

// This would depend upon whether we are additive or not
// If we are the base scene, then we set the root scene index;
if (activeScene == scene)
{
if (!ValidateSceneBeforeLoading(sceneHash, sceneEventData.LoadSceneMode))
if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, sceneEventData.LoadSceneMode))
{
continue;
}
sceneEventData.SceneHash = sceneHash;
sceneEventData.SceneHash = SceneHashFromNameOrPath(scene.path);
sceneEventData.SceneHandle = scene.handle;
}
else if (!ValidateSceneBeforeLoading(sceneHash, LoadSceneMode.Additive))
else if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, LoadSceneMode.Additive))
{
continue;
}
sceneEventData.AddSceneToSynchronize(sceneHash, scene.handle);
sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), scene.handle);
}

sceneEventData.AddSpawnedNetworkObjects();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,69 @@ public class ClientSynchronizationValidationTest : NetcodeIntegrationTest
private const string k_FirstSceneToLoad = "UnitTestBaseScene";
private const string k_SecondSceneToLoad = "InSceneNetworkObject";
private const string k_ThirdSceneToSkip = "EmptyScene";
private Scene m_RuntimeGeneratedScene;
private bool m_IncludeSceneVerificationHandler;
private bool m_RuntimeSceneWasExcludedFromSynch;

private List<ClientSceneVerificationHandler> m_ClientSceneVerifiers = new List<ClientSceneVerificationHandler>();

protected override void OnNewClientStarted(NetworkManager networkManager)
{
m_ClientSceneVerifiers.Add(new ClientSceneVerificationHandler(networkManager));
if (m_IncludeSceneVerificationHandler)
{
m_ClientSceneVerifiers.Add(new ClientSceneVerificationHandler(networkManager));
}
base.OnNewClientStarted(networkManager);
}

/// <summary>
/// Handle excluding runtime scene from synchronization
/// </summary>
private bool OnServerVerifySceneBeforeLoading(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
{
// exclude test runner scene
if (sceneName.StartsWith(NetcodeIntegrationTestHelpers.FirstPartOfTestRunnerSceneName))
{
return false;
}

// Exclude the runtime generated scene
if (sceneIndex == m_RuntimeGeneratedScene.buildIndex && m_RuntimeGeneratedScene.name == sceneName)
{
m_RuntimeSceneWasExcludedFromSynch = true;
return false;
}

return true;
}

/// <summary>
/// Test that validates users can exclude runtime generated scenes from the initial client synchronization
/// process using <see cref="NetworkSceneManager.VerifySceneBeforeLoading"/>
/// </summary>
[UnityTest]
public IEnumerator ClientSynchWithServerSideRuntimeGeneratedScene()
{
m_IncludeSceneVerificationHandler = false;
m_ServerNetworkManager.SceneManager.VerifySceneBeforeLoading = OnServerVerifySceneBeforeLoading;
m_ServerNetworkManager.SceneManager.DisableValidationWarnings(true);
// For this test we want to disable the check for scenes in build list
m_ServerNetworkManager.SceneManager.ExcludeSceneFromSychronization = null;
// Create a runtime scene in the server side
m_RuntimeGeneratedScene = SceneManager.CreateScene("RuntimeGeneratedScene");
yield return s_DefaultWaitForTick;
yield return CreateAndStartNewClient();

Assert.True(m_RuntimeSceneWasExcludedFromSynch, $"Server did not exclude the runtime generated scene when creating synchronization message data!");
}

/// <summary>
/// Validates that connecting clients will exclude scenes using <see cref="NetworkSceneManager.VerifySceneBeforeLoading"/>
/// </summary>
[UnityTest]
public IEnumerator ClientVerifySceneBeforeLoading()
{
m_IncludeSceneVerificationHandler = true;
var scenesToLoad = new List<string>() { k_FirstSceneToLoad, k_SecondSceneToLoad, k_ThirdSceneToSkip };
m_ServerNetworkManager.SceneManager.OnLoadComplete += OnLoadComplete;
foreach (var sceneToLoad in scenesToLoad)
Expand Down