Skip to content

Commit cd50a00

Browse files
fix: NetworksSenemanager order of operations and scene migration preprocess (#2532)
* fix Change the order of operations where clients match the server's currently active scene and then spawn and synchronize NetworkObjects locally on the server. Preprocess the scenes containing NetworkObjects to send scene migration notifications for in the event the NetworkObjects were despawned in the same frame. * fix Change the logic to detect if setting the synchronization mode is occurring when clients are already connected so that it is checking against whether it is a host vs server. * Style fixing white space issues. * test Added test to verify that late joining clients synchronize properly when a NetworkObject is migrated into a new scene and then despawn and destroyed during a late joining client's initial synchronization. * update Updating changelog
1 parent 9246d94 commit cd50a00

File tree

4 files changed

+79
-7
lines changed

4 files changed

+79
-7
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1515
- Fixed issue where some temporary debug console logging was left in a merged PR. (#2562)
1616
- Fixed the "Generate Default Network Prefabs List" setting not loading correctly and always reverting to being checked. (#2545)
1717
- Fixed missing value on `NetworkListEvent` for `EventType.RemoveAt` events. (#2542,#2543)
18+
- Fixed issue where a server would include scene migrated and then despawned NetworkObjects to a client that was being synchronized. (#2532)
1819
- Fixed the inspector throwing exceptions when attempting to render `NetworkVariable`s of enum types. (#2529)
1920
- Making a `NetworkVariable` with an `INetworkSerializable` type that doesn't meet the `new()` constraint will now create a compile-time error instead of an editor crash (#2528)
2021
- Fixed Multiplayer Tools package installation docs page link on the NetworkManager popup. (#2526)
@@ -26,10 +27,10 @@ Additional documentation and release notes are available at [Multiplayer Documen
2627

2728
## Changed
2829

30+
- Connecting clients being synchronized now switch to the server's active scene before spawning and synchronizing NetworkObjects. (#2532)
2931
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.4. (#2533)
3032
- Improved performance of NetworkBehaviour initialization by replacing reflection when initializing NetworkVariables with compile-time code generation, which should help reduce hitching during additive scene loads. (#2522)
3133

32-
3334
## [1.4.0] - 2023-04-10
3435

3536
### Added

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ public void SetClientSynchronizationMode(ref NetworkManager networkManager, Load
337337
return;
338338
}
339339
else // Warn users if they are changing this after there are clients already connected and synchronized
340-
if (networkManager.ConnectedClientsIds.Count > (networkManager.IsServer ? 0 : 1) && sceneManager.ClientSynchronizationMode != mode)
340+
if (networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode)
341341
{
342342
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
343343
{

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

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,8 +2026,6 @@ private void HandleClientSceneEvent(uint sceneEventId)
20262026
{
20272027
// Include anything in the DDOL scene
20282028
PopulateScenePlacedObjects(DontDestroyOnLoadScene, false);
2029-
// Synchronize the NetworkObjects for this scene
2030-
sceneEventData.SynchronizeSceneNetworkObjects(NetworkManager);
20312029

20322030
// If needed, set the currently active scene
20332031
if (HashToBuildIndex.ContainsKey(sceneEventData.ActiveSceneHash))
@@ -2039,7 +2037,10 @@ private void HandleClientSceneEvent(uint sceneEventId)
20392037
}
20402038
}
20412039

2042-
// If needed, migrate dynamically spawned NetworkObjects to the same scene as on the server side
2040+
// Spawn and Synchronize all NetworkObjects
2041+
sceneEventData.SynchronizeSceneNetworkObjects(NetworkManager);
2042+
2043+
// If needed, migrate dynamically spawned NetworkObjects to the same scene as they are on the server
20432044
SynchronizeNetworkObjectScene();
20442045

20452046
sceneEventData.SceneEventType = SceneEventType.SynchronizeComplete;
@@ -2468,6 +2469,9 @@ internal void MigrateNetworkObjectsIntoScenes()
24682469
ObjectsMigratedIntoNewScene.Clear();
24692470
}
24702471

2472+
2473+
private List<int> m_ScenesToRemoveFromObjectMigration = new List<int>();
2474+
24712475
/// <summary>
24722476
/// Should be invoked during PostLateUpdate just prior to the NetworkMessageManager processes its outbound message queue.
24732477
/// </summary>
@@ -2479,6 +2483,40 @@ internal void CheckForAndSendNetworkObjectSceneChanged()
24792483
return;
24802484
}
24812485

2486+
// Double check that the NetworkObjects to migrate still exist
2487+
m_ScenesToRemoveFromObjectMigration.Clear();
2488+
foreach (var sceneEntry in ObjectsMigratedIntoNewScene)
2489+
{
2490+
for (int i = sceneEntry.Value.Count - 1; i >= 0; i--)
2491+
{
2492+
// Remove NetworkObjects that are no longer spawned
2493+
if (!sceneEntry.Value[i].IsSpawned)
2494+
{
2495+
sceneEntry.Value.RemoveAt(i);
2496+
}
2497+
}
2498+
// If the scene entry no longer has any NetworkObjects to migrate
2499+
// then add it to the list of scenes to be removed from the table
2500+
// of scenes containing NetworkObjects to migrate.
2501+
if (sceneEntry.Value.Count == 0)
2502+
{
2503+
m_ScenesToRemoveFromObjectMigration.Add(sceneEntry.Key);
2504+
}
2505+
}
2506+
2507+
// Remove sceneHandle entries that no longer have any NetworkObjects remaining
2508+
foreach (var sceneHandle in m_ScenesToRemoveFromObjectMigration)
2509+
{
2510+
ObjectsMigratedIntoNewScene.Remove(sceneHandle);
2511+
}
2512+
2513+
// If there is nothing to send a migration notification for then exit
2514+
if (ObjectsMigratedIntoNewScene.Count == 0)
2515+
{
2516+
return;
2517+
}
2518+
2519+
// Some NetworkObjects still exist, send the message
24822520
var sceneEvent = BeginSceneEvent();
24832521
sceneEvent.SceneEventType = SceneEventType.ObjectSceneChanged;
24842522
SendSceneEventData(sceneEvent.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());

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

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,21 +207,51 @@ public IEnumerator MigrateIntoNewSceneTest()
207207

208208
// Register for the server-side client synchronization so we can send an object scene migration event at the same time
209209
// the new client begins to synchronize
210-
m_ServerNetworkManager.SceneManager.OnSynchronize += SceneManager_OnSynchronize;
210+
m_ServerNetworkManager.SceneManager.OnSynchronize += MigrateObjects_OnSynchronize;
211211

212212
// Verify that a late joining client synchronizes properly even while new scene migrations occur
213213
// during its synchronization
214214
yield return CreateAndStartNewClient();
215215
yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated);
216216

217217
AssertOnTimeout($"[Late Joined Client] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!");
218+
219+
// Verify that a late joining client synchronizes properly even if we migrate
220+
// during its synchronization and despawn some of the NetworkObjects migrated.
221+
m_ServerNetworkManager.SceneManager.OnSynchronize += MigrateAndDespawnObjects_OnSynchronize;
222+
yield return CreateAndStartNewClient();
223+
yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated);
224+
225+
AssertOnTimeout($"[Late Joined Client] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!");
226+
}
227+
228+
/// <summary>
229+
/// Part of NetworkObject scene migration tests to verify that a NetworkObject
230+
/// migrated to a scene and then despawned will be handled properly for clients
231+
/// in the middle of synchronization.
232+
/// </summary>
233+
private void MigrateAndDespawnObjects_OnSynchronize(ulong clientId)
234+
{
235+
var objectCount = 0;
236+
// Migrate the NetworkObjects into different scenes than they originally were migrated into
237+
for (int i = m_ServerSpawnedPrefabInstances.Count - 1; i >= 0; i--)
238+
{
239+
var scene = m_ScenesLoaded[i % m_ScenesLoaded.Count];
240+
SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[i].gameObject, scene);
241+
// De-spawn every-other object
242+
if (i % 2 == 0)
243+
{
244+
m_ServerSpawnedPrefabInstances[objectCount + i].Despawn();
245+
m_ServerSpawnedPrefabInstances.RemoveAt(i);
246+
}
247+
}
218248
}
219249

220250
/// <summary>
221251
/// Migrate objects into other scenes when a client begins synchronization
222252
/// </summary>
223253
/// <param name="clientId"></param>
224-
private void SceneManager_OnSynchronize(ulong clientId)
254+
private void MigrateObjects_OnSynchronize(ulong clientId)
225255
{
226256
var objectCount = k_MaxObjectsToSpawn - 1;
227257

@@ -233,6 +263,9 @@ private void SceneManager_OnSynchronize(ulong clientId)
233263
SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[objectCount - 2].gameObject, scene);
234264
objectCount -= 3;
235265
}
266+
267+
// Unsubscribe to this event for this part of the test
268+
m_ServerNetworkManager.SceneManager.OnSynchronize -= MigrateObjects_OnSynchronize;
236269
}
237270

238271
/// <summary>

0 commit comments

Comments
 (0)