Skip to content

Commit fae7e8c

Browse files
refactor: decouple PendingSoftSyncObjects from NetworkSpawnManager (#913)
* refactor This removes NetworkSpawnManager.PendingSoftSynchObjects and replaces it with NetworkSceneManager.ScenePlacedObjects. The primary difference between the two is that the ScenePlacedObjects is populated immediately after a level is loaded or when being approved and already within the current scene. * style and refactor Migrating the ScenePlacedObjects dictionary up to the rest of the NetworkkSceneManager's dictionary definitions. Removing the PendingSoftSyncObjects dictionary from NetworkSpawnManager. * refactor Use the ScenePlacedObjects dictionary to generate the client(s) switch scene stream buffer. Refactored PopulateScenePlacedObjects method to not set the IsSceneObject to true and to also check and make sure the NetworkObject instance's NetworkManager reference is the same NetworkManager instance as the NetworkSceneManager's NetworkManager instance in question in order to maintain compatibility with the MultiInstanceHelper class for Mult-Instance unit tests. * refactor Renaming OnSceneUnloadClient and OnSceneUnloadServer to OnClientLoadedScene and OnServerLoadedScene * refactor and fix Fixed some remaining checks (player prefab) as to whether the rigid body should be kinematic or not to prevent the mega spam. During the revert changes of the NetworkTransform changes to better reflect what is in the develop branch, missed the else if. Fixed a minor issue with the slider in the SceneTransitioning manual test to better visually reflect what the value is as well as handle the scenario if it starts at 0 boxes per second it needed to start the coroutine and if set to zero and the coroutine was running it terminates the coroutine. * Fix This is a fix for the NetworkObjectOnSpawnTest where it was spawning a GameObject twice that contained a component that would throw an exception if you spawned a NetworkObject. * fix An unintended project version change * bug test Checking to see if Yamato fails on this... * test This test verifies that "cleaning in-scene placed network objects" within the connection approved method was hiding potential bugs from other unit tests. * fix Removing the unit test duplicated GameObject instance and all of the associated code to verify this was how the bug was missed by TestRunner and Yamato. * fix reverting back to the original values for these 3 files that were mistakenly committed. * Update com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs Co-authored-by: M. Fatih MAR <mfatihmar@gmail.com> Co-authored-by: M. Fatih MAR <mfatihmar@gmail.com>
1 parent 2711225 commit fae7e8c

File tree

9 files changed

+104
-84
lines changed

9 files changed

+104
-84
lines changed

com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ internal void GenerateGlobalObjectIdHash()
5050
return;
5151
}
5252

53-
// do NOT regenerate GlobalObjectIdHash if Editor is transitining into or out of PlayMode
53+
// do NOT regenerate GlobalObjectIdHash if Editor is transitioning into or out of PlayMode
5454
if (!UnityEditor.EditorApplication.isPlaying && UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
5555
{
5656
return;

com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public void HandleConnectionApproved(ulong clientId, Stream stream, float receiv
8686

8787
void DelayedSpawnAction(Stream continuationStream)
8888
{
89+
8990
using (var continuationReader = PooledNetworkReader.Get(continuationStream))
9091
{
9192
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
@@ -94,7 +95,7 @@ void DelayedSpawnAction(Stream continuationStream)
9495
}
9596
else
9697
{
97-
NetworkManager.SpawnManager.ClientCollectSoftSyncSceneObjectSweep(null);
98+
NetworkManager.SceneManager.PopulateScenePlacedObjects();
9899
}
99100

100101
var objectCount = continuationReader.ReadUInt32Packed();
@@ -103,7 +104,6 @@ void DelayedSpawnAction(Stream continuationStream)
103104
NetworkObject.DeserializeSceneObject(continuationStream as NetworkBuffer, continuationReader, m_NetworkManager);
104105
}
105106

106-
NetworkManager.SpawnManager.CleanDiffedSceneObjects();
107107
NetworkManager.IsConnectedClient = true;
108108
NetworkManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId);
109109
}

com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class NetworkSceneManager
7979
internal readonly Dictionary<string, uint> SceneNameToIndex = new Dictionary<string, uint>();
8080
internal readonly Dictionary<uint, string> SceneIndexToString = new Dictionary<uint, string>();
8181
internal readonly Dictionary<Guid, SceneSwitchProgress> SceneSwitchProgresses = new Dictionary<Guid, SceneSwitchProgress>();
82+
internal readonly Dictionary<uint, NetworkObject> ScenePlacedObjects = new Dictionary<uint, NetworkObject>();
8283

8384
private static Scene s_LastScene;
8485
private static string s_NextSceneName;
@@ -273,12 +274,49 @@ internal void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid)
273274
s_IsSwitching = false;
274275
}
275276

277+
278+
279+
280+
/// <summary>
281+
/// Should be invoked on both the client and server side after:
282+
/// -- A new scene has been loaded
283+
/// -- Before any "DontDestroyOnLoad" NetworkObjects have been added back into the scene.
284+
/// Added the ability to choose not to clear the scene placed objects for additive scene loading.
285+
/// </summary>
286+
internal void PopulateScenePlacedObjects(bool clearScenePlacedObjects = true)
287+
{
288+
if (clearScenePlacedObjects)
289+
{
290+
ScenePlacedObjects.Clear();
291+
}
292+
293+
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
294+
295+
// Just add every NetworkObject found that isn't already in the list
296+
// If any "non-in-scene placed NetworkObjects" are added to this list it shouldn't matter
297+
// The only thing that matters is making sure each NetworkObject is keyed off of their GlobalObjectIdHash
298+
foreach (var networkObjectInstance in networkObjects)
299+
{
300+
if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash))
301+
{
302+
// We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible
303+
if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager)
304+
{
305+
ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, networkObjectInstance);
306+
}
307+
}
308+
}
309+
}
310+
276311
private void OnSceneLoaded(Guid switchSceneGuid, Stream objectStream)
277312
{
278313
CurrentActiveSceneIndex = SceneNameToIndex[s_NextSceneName];
279314
var nextScene = SceneManager.GetSceneByName(s_NextSceneName);
280315
SceneManager.SetActiveScene(nextScene);
281316

317+
//Get all NetworkObjects loaded by the scene
318+
PopulateScenePlacedObjects();
319+
282320
// Move all objects to the new scene
283321
MoveObjectsToScene(nextScene);
284322

@@ -288,31 +326,22 @@ private void OnSceneLoaded(Guid switchSceneGuid, Stream objectStream)
288326

289327
if (m_NetworkManager.IsServer)
290328
{
291-
OnSceneUnloadServer(switchSceneGuid);
329+
OnServerLoadedScene(switchSceneGuid);
292330
}
293331
else
294332
{
295-
OnSceneUnloadClient(switchSceneGuid, objectStream);
333+
OnClientLoadedScene(switchSceneGuid, objectStream);
296334
}
297335
}
298336

299-
private void OnSceneUnloadServer(Guid switchSceneGuid)
337+
private void OnServerLoadedScene(Guid switchSceneGuid)
300338
{
301-
// Justification: Rare alloc, could(should?) reuse
302-
var newSceneObjects = new List<NetworkObject>();
339+
// Register in-scene placed NetworkObjects with MLAPI
340+
foreach (var keyValuePair in ScenePlacedObjects)
303341
{
304-
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
305-
306-
for (int i = 0; i < networkObjects.Length; i++)
342+
if (!keyValuePair.Value.IsPlayerObject)
307343
{
308-
if (networkObjects[i].NetworkManager == m_NetworkManager)
309-
{
310-
if (networkObjects[i].IsSceneObject == null)
311-
{
312-
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObjects[i], m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, null, false, 0, false, true);
313-
newSceneObjects.Add(networkObjects[i]);
314-
}
315-
}
344+
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePair.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, null, false, 0, false, true);
316345
}
317346
}
318347

@@ -327,25 +356,24 @@ private void OnSceneUnloadServer(Guid switchSceneGuid)
327356
writer.WriteByteArray(switchSceneGuid.ToByteArray());
328357

329358
uint sceneObjectsToSpawn = 0;
330-
for (int i = 0; i < newSceneObjects.Count; i++)
359+
360+
foreach (var keyValuePair in ScenePlacedObjects)
331361
{
332-
if (newSceneObjects[i].Observers.Contains(m_NetworkManager.ConnectedClientsList[j].ClientId))
362+
if (keyValuePair.Value.Observers.Contains(m_NetworkManager.ConnectedClientsList[j].ClientId))
333363
{
334364
sceneObjectsToSpawn++;
335365
}
336366
}
337367

338368
// Write number of scene objects to spawn
339369
writer.WriteUInt32Packed(sceneObjectsToSpawn);
340-
341-
for (int i = 0; i < newSceneObjects.Count; i++)
370+
foreach (var keyValuePair in ScenePlacedObjects)
342371
{
343-
if (newSceneObjects[i].Observers.Contains(m_NetworkManager.ConnectedClientsList[j].ClientId))
372+
if (keyValuePair.Value.Observers.Contains(m_NetworkManager.ConnectedClientsList[j].ClientId))
344373
{
345-
newSceneObjects[i].SerializeSceneObject(writer, m_NetworkManager.ConnectedClientsList[j].ClientId);
374+
keyValuePair.Value.SerializeSceneObject(writer, m_NetworkManager.ConnectedClientsList[j].ClientId);
346375
}
347376
}
348-
349377
m_NetworkManager.MessageSender.Send(m_NetworkManager.ConnectedClientsList[j].ClientId, NetworkConstants.SWITCH_SCENE, NetworkChannel.Internal, buffer);
350378
}
351379
}
@@ -362,12 +390,10 @@ private void OnSceneUnloadServer(Guid switchSceneGuid)
362390
OnSceneSwitched?.Invoke();
363391
}
364392

365-
private void OnSceneUnloadClient(Guid switchSceneGuid, Stream objectStream)
393+
private void OnClientLoadedScene(Guid switchSceneGuid, Stream objectStream)
366394
{
367395
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
368396

369-
m_NetworkManager.SpawnManager.ClientCollectSoftSyncSceneObjectSweep(networkObjects);
370-
371397
using (var reader = PooledNetworkReader.Get(objectStream))
372398
{
373399
var newObjectsCount = reader.ReadUInt32Packed();

com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs

Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ public class NetworkSpawnManager
2525
/// </summary>
2626
public readonly Dictionary<ulong, NetworkObject> SpawnedObjects = new Dictionary<ulong, NetworkObject>();
2727

28-
// Pending SoftSync objects
29-
internal readonly Dictionary<ulong, NetworkObject> PendingSoftSyncObjects = new Dictionary<ulong, NetworkObject>();
30-
3128
/// <summary>
3229
/// A list of the spawned objects
3330
/// </summary>
@@ -246,17 +243,19 @@ internal NetworkObject CreateLocalNetworkObject(bool isSceneObject, uint globalO
246243
}
247244
else
248245
{
249-
// SoftSync them by mapping
250-
if (!PendingSoftSyncObjects.TryGetValue(globalObjectIdHash, out NetworkObject networkObject))
246+
if (!NetworkManager.SceneManager.ScenePlacedObjects.TryGetValue(globalObjectIdHash, out NetworkObject networkObject))
251247
{
252248
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
253249
{
254250
NetworkLog.LogError($"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure for Hash: {globalObjectIdHash}!");
255251
}
252+
256253
return null;
257254
}
258-
259-
PendingSoftSyncObjects.Remove(globalObjectIdHash);
255+
else
256+
{
257+
NetworkManager.SceneManager.ScenePlacedObjects.Remove(globalObjectIdHash);
258+
}
260259

261260
if (parentNetworkObject != null)
262261
{
@@ -557,20 +556,6 @@ internal void DestroySceneObjects()
557556
}
558557
}
559558

560-
internal void CleanDiffedSceneObjects()
561-
{
562-
// Clean up any in-scene objects that had been destroyed
563-
if (PendingSoftSyncObjects.Count > 0)
564-
{
565-
foreach (var pair in PendingSoftSyncObjects)
566-
{
567-
UnityEngine.Object.Destroy(pair.Value.gameObject);
568-
}
569-
570-
// Make sure to clear this once done destroying all remaining NetworkObjects
571-
PendingSoftSyncObjects.Clear();
572-
}
573-
}
574559

575560
internal void ServerSpawnSceneObjectsOnStartSweep()
576561
{
@@ -588,25 +573,6 @@ internal void ServerSpawnSceneObjectsOnStartSweep()
588573
}
589574
}
590575

591-
internal void ClientCollectSoftSyncSceneObjectSweep(NetworkObject[] networkObjects)
592-
{
593-
if (networkObjects == null)
594-
{
595-
networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
596-
}
597-
598-
for (int i = 0; i < networkObjects.Length; i++)
599-
{
600-
if (networkObjects[i].NetworkManager == NetworkManager)
601-
{
602-
if (networkObjects[i].IsSceneObject == null)
603-
{
604-
PendingSoftSyncObjects.Add(networkObjects[i].GlobalObjectIdHash, networkObjects[i]);
605-
}
606-
}
607-
}
608-
}
609-
610576
internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObject)
611577
{
612578
if (NetworkManager == null)

com.unity.multiplayer.mlapi/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,9 @@ public IEnumerator InstantiateDestroySpawnNotCalled()
2626

2727
yield return null;
2828

29-
// instantiate
30-
var instance = Object.Instantiate(gameObject);
31-
yield return null;
32-
3329
// destroy
34-
Object.Destroy(instance);
30+
Object.Destroy(gameObject);
31+
3532
yield return null;
3633
}
3734

com.unity.multiplayer.mlapi/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ public void NetworkObjectSceneSerializationFailure()
8484
// Serialize the valid NetworkObject
8585
networkObject.SerializeSceneObject(writer, 0);
8686

87-
// Add this valid NetworkObject into the PendinigSoftSyncObjects list
88-
NetworkManagerHelper.NetworkManagerObject.SpawnManager.PendingSoftSyncObjects.Add(networkObject.GlobalObjectIdHash, networkObject);
87+
// Add this valid NetworkObject into the ScenePlacedObjects list
88+
NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(networkObject.GlobalObjectIdHash, networkObject);
8989
}
9090
}
9191

testproject/Assets/Prefabs/Player.prefab

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,16 @@ MonoBehaviour:
5252
m_Name:
5353
m_EditorClassIdentifier:
5454
TransformAuthority: 1
55-
FixedSendsPerSecond: 5
55+
FixedSendsPerSecond: 15
5656
InterpolatePosition: 1
5757
SnapDistance: 10
5858
InterpolateServer: 1
59-
MinMeters: 0.16
59+
MinMeters: 0.15
6060
MinDegrees: 1.5
6161
MinSize: 0.15
62-
Channel: 0
62+
Channel: 2
63+
m_UseLocal:
64+
m_InternalValue: 0
6365
--- !u!114 &-3775814466963834669
6466
MonoBehaviour:
6567
m_ObjectHideFlags: 0
@@ -73,6 +75,7 @@ MonoBehaviour:
7375
m_Name:
7476
m_EditorClassIdentifier:
7577
GlobalObjectIdHash: 951099334
78+
m_MarkedAsSceneObject: 0
7679
AlwaysReplicateAsRoot: 0
7780
DontDestroyWithOwner: 0
7881
--- !u!33 &4079352819444256610

testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class NetworkPrefabPool : NetworkBehaviour
2121
[Tooltip("When enabled, this will utilize the NetworkPrefabHandler to register a custom INetworkPrefabInstanceHandler")]
2222
public bool EnableHandler;
2323

24-
[Tooltip("When enabled, this will register register a custom INetworkPrefabInstanceHandler using a NetworkObject reference")]
24+
[Tooltip("When enabled, this will register a custom INetworkPrefabInstanceHandler using a NetworkObject reference")]
2525
public bool RegisterUsingNetworkObject;
2626

2727
[Tooltip("What is going to be spawned on the server side from the pool.")]
@@ -167,6 +167,9 @@ public override void OnNetworkSpawn()
167167
{
168168
m_DelaySpawning = Time.realtimeSinceStartup + InitialSpawnDelay;
169169
StartSpawningBoxes();
170+
171+
//Make sure our slider reflects the current spawn rate
172+
UpdateSpawnsPerSecond();
170173
}
171174
}
172175
}
@@ -233,7 +236,6 @@ private GameObject AddNewInstance()
233236
/// </summary>
234237
private void StartSpawningBoxes()
235238
{
236-
237239
if (NetworkManager.IsHost && SpawnSlider != null)
238240
{
239241
SpawnSlider.gameObject.SetActive(true);
@@ -246,14 +248,26 @@ private void StartSpawningBoxes()
246248
}
247249

248250
/// <summary>
249-
/// Checks to detemrine if we need to update our spawns per second calculations
251+
/// Checks to determine if we need to update our spawns per second calculations
250252
/// </summary>
251253
public void UpdateSpawnsPerSecond()
252254
{
253255
if (SpawnSlider != null)
254256
{
255257
SpawnsPerSecond = (int)SpawnSlider.value;
256258
SpawnSliderValueText.text = SpawnsPerSecond.ToString();
259+
260+
// Handle case where the initial value is set to zero and so coroutine needs to be started
261+
if(SpawnsPerSecond > 0 && !m_IsSpawningObjects)
262+
{
263+
StartSpawningBoxes();
264+
}
265+
else //Handle case where spawning coroutine is running but we set our spawn rate to zero
266+
if (SpawnsPerSecond == 0 && m_IsSpawningObjects)
267+
{
268+
m_IsSpawningObjects = false;
269+
StopCoroutine(SpawnObjects());
270+
}
257271
}
258272
}
259273

0 commit comments

Comments
 (0)