Skip to content

Commit 57b0d76

Browse files
Merge branch 'develop-2.0.0' into fix/da-change-ownership-results-in-invalid-previous-and-current-clientids
2 parents 9412802 + d80340d commit 57b0d76

File tree

7 files changed

+241
-18
lines changed

7 files changed

+241
-18
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
1818

1919
### Fixed
2020

21+
- Fixed issue when using a distributed authority network topology and many clients attempt to connect simultaneously the session owner could max-out the maximum in-flight reliable messages allowed, start dropping packets, and some of the connecting clients would fail to fully synchronize. (#3350)
22+
- Fixed issue when using a distributed authority network topology and scene management was disabled clients would not be able to spawn any new network prefab instances until synchronization was complete. (#3350)
2123
- Fixed issue where an owner that changes ownership, when using a distributed authority network topology, could yield identical previous and current owner identifiers. This could also cause `NetworkTransform` to fail to change ownership which would leave the previous owner still subscribed to network tick events. (#3347)
2224
- Fixed issue where the `MaximumInterpolationTime` could not be modified from within the inspector view or runtime. (#3337)
2325
- Fixed `ChangeOwnership` changing ownership to clients that are not observers. This also happened with automated object distribution. (#3323)

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ClientConnectedMessage.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public void Handle(ref NetworkContext context)
7373
/// DANGO-TODO: Determine if this needs to be removed once the service handles object distribution
7474
networkManager.RedistributeToClients = true;
7575
networkManager.ClientsToRedistribute.Add(ClientId);
76+
77+
// TODO: We need a client synchronized message or something like that here
7678
}
7779
}
7880
}

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,13 @@ public void Handle(ref NetworkContext context)
270270
// Only if scene management is disabled do we handle NetworkObject synchronization at this point
271271
if (!networkManager.NetworkConfig.EnableSceneManagement)
272272
{
273+
/// Mark the client being connected before running through the spawning synchronization so we
274+
/// can assure that if a user attempts to spawn something when an already spawned NetworkObject
275+
/// is spawned (during the initial synchronization just below) it will not error out complaining
276+
/// about the player not being connected.
277+
/// The check for this is done within <see cref="NetworkObject.SpawnInternal(bool, ulong, bool)"/>
278+
networkManager.IsConnectedClient = true;
279+
273280
// DANGO-TODO: This is a temporary fix for no DA CMB scene event handling.
274281
// We will either use this same concept or provide some way for the CMB state plugin to handle it.
275282
if (networkManager.DistributedAuthorityMode && networkManager.LocalClient.IsSessionOwner)
@@ -292,9 +299,6 @@ public void Handle(ref NetworkContext context)
292299
NetworkObject.AddSceneObject(sceneObject, m_ReceivedSceneObjectData, networkManager);
293300
}
294301

295-
// Mark the client being connected
296-
networkManager.IsConnectedClient = true;
297-
298302
if (networkManager.AutoSpawnPlayerPrefabClientSide)
299303
{
300304
networkManager.ConnectionManager.CreateAndSpawnPlayer(OwnerClientId);
@@ -315,14 +319,14 @@ public void Handle(ref NetworkContext context)
315319
if (networkManager.DistributedAuthorityMode && networkManager.CMBServiceConnection && networkManager.LocalClient.IsSessionOwner && networkManager.NetworkConfig.EnableSceneManagement)
316320
{
317321
// Mark the client being connected
318-
networkManager.IsConnectedClient = true;
322+
networkManager.IsConnectedClient = networkManager.ConnectionManager.LocalClient.IsApproved;
319323

320324
networkManager.SceneManager.IsRestoringSession = GetIsSessionRestor();
321325

322326
if (!networkManager.SceneManager.IsRestoringSession)
323327
{
324328
// Synchronize the service with the initial session owner's loaded scenes and spawned objects
325-
networkManager.SceneManager.SynchronizeNetworkObjects(NetworkManager.ServerClientId);
329+
networkManager.SceneManager.SynchronizeNetworkObjects(NetworkManager.ServerClientId, true);
326330

327331
// Spawn any in-scene placed NetworkObjects
328332
networkManager.SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
@@ -334,9 +338,9 @@ public void Handle(ref NetworkContext context)
334338
}
335339

336340
// Synchronize the service with the initial session owner's loaded scenes and spawned objects
337-
networkManager.SceneManager.SynchronizeNetworkObjects(NetworkManager.ServerClientId);
341+
networkManager.SceneManager.SynchronizeNetworkObjects(NetworkManager.ServerClientId, true);
338342

339-
// With scene management enabled and since the session owner doesn't send a Synchronize scene event synchronize itself,
343+
// With scene management enabled and since the session owner doesn't send a scene event synchronize to itself,
340344
// we need to notify the session owner that everything should be synchronized/spawned at this time.
341345
networkManager.SpawnManager.NotifyNetworkObjectsSynchronized();
342346

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

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1986,6 +1986,14 @@ private void OnClientLoadedScene(uint sceneEventId, Scene scene)
19861986
/// </summary>
19871987
internal Func<Scene, bool> ExcludeSceneFromSychronization;
19881988

1989+
/// <summary>
1990+
/// This is used for distributed authority sessions only and assures that
1991+
/// when many clients attempt to connect at the same time they will be
1992+
/// handled sequentially so as to not saturate the session owner's maximum
1993+
/// reliable messages.
1994+
/// </summary>
1995+
internal List<ulong> ClientConnectionQueue = new List<ulong>();
1996+
19891997
/// <summary>
19901998
/// Server Side:
19911999
/// This is used for players that have just had their connection approved and will assure they are synchronized
@@ -1994,8 +2002,30 @@ private void OnClientLoadedScene(uint sceneEventId, Scene scene)
19942002
/// synchronized.
19952003
/// </summary>
19962004
/// <param name="clientId">newly joined client identifier</param>
1997-
internal void SynchronizeNetworkObjects(ulong clientId)
2005+
/// <param name="synchronizingService">true only when invoked on a newly connected and approved client.</param>
2006+
internal void SynchronizeNetworkObjects(ulong clientId, bool synchronizingService = false)
19982007
{
2008+
// If we are connected to a live service hosted session and we are not doing the initial synchronization for the service...
2009+
if (NetworkManager.CMBServiceConnection && !synchronizingService)
2010+
{
2011+
// then as long as this is a newly connecting client add it to the connecting client queue.
2012+
// Otherwise, if this is not a newly connecting client (i.e. it is already in the queue), then go ahead and synchronize
2013+
// that client.
2014+
if (!ClientConnectionQueue.Contains(clientId))
2015+
{
2016+
ClientConnectionQueue.Add(clientId);
2017+
// If we are already synchronizing one or more clients, exit early. This client will be synchronized later.
2018+
if (ClientConnectionQueue.Count > 1)
2019+
{
2020+
if (NetworkManager.LogLevel <= LogLevel.Developer)
2021+
{
2022+
Debug.Log($"Deferring Client-{clientId} synchrnization.");
2023+
}
2024+
return;
2025+
}
2026+
}
2027+
}
2028+
19992029
// Update the clients
20002030
NetworkManager.SpawnManager.UpdateObservedNetworkObjects(clientId);
20012031

@@ -2623,6 +2653,44 @@ private void HandleSessionOwnerEvent(uint sceneEventId, ulong clientId)
26232653
// DANGO-EXP TODO: Remove this once service distributes objects
26242654
NetworkManager.SpawnManager.DistributeNetworkObjects(clientId);
26252655
EndSceneEvent(sceneEventId);
2656+
2657+
// Exit early if not a distributed authority session or this is a DAHost
2658+
// (DAHost has a unique connection per client, so no need to queue synchronization)
2659+
if (!NetworkManager.DistributedAuthorityMode || NetworkManager.DAHost)
2660+
{
2661+
return;
2662+
}
2663+
2664+
// Otherwise, this is a session owner that could have pending clients to synchronize
2665+
if (NetworkManager.DistributedAuthorityMode && NetworkManager.CMBServiceConnection)
2666+
{
2667+
// Remove the client that just synchronized
2668+
ClientConnectionQueue.Remove(clientId);
2669+
2670+
// If we have pending clients to synchronize, then make sure they are still connected
2671+
while (ClientConnectionQueue.Count > 0)
2672+
{
2673+
// If the next client is no longer connected then remove it from the list
2674+
if (!NetworkManager.ConnectedClientsIds.Contains(ClientConnectionQueue[0]))
2675+
{
2676+
ClientConnectionQueue.RemoveAt(0);
2677+
}
2678+
else
2679+
{
2680+
break;
2681+
}
2682+
}
2683+
2684+
// If we still have any pending clients waiting, then synchronize the next one
2685+
if (ClientConnectionQueue.Count > 0)
2686+
{
2687+
if (NetworkManager.LogLevel <= LogLevel.Developer)
2688+
{
2689+
Debug.Log($"Synchronizing Client-{ClientConnectionQueue[0]}...");
2690+
}
2691+
SynchronizeNetworkObjects(ClientConnectionQueue[0]);
2692+
}
2693+
}
26262694
break;
26272695
}
26282696
default:

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

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,7 +1825,7 @@ internal void Shutdown()
18251825
/// </summary>
18261826
/// <param name="objectByTypeAndOwner">the table to populate</param>
18271827
/// <param name="objectTypeCount">the total number of the specific object type to distribute</param>
1828-
internal void GetObjectDistribution(ref Dictionary<uint, Dictionary<ulong, List<NetworkObject>>> objectByTypeAndOwner, ref Dictionary<uint, int> objectTypeCount)
1828+
internal void GetObjectDistribution(ulong clientId, ref Dictionary<uint, Dictionary<ulong, List<NetworkObject>>> objectByTypeAndOwner, ref Dictionary<uint, int> objectTypeCount)
18291829
{
18301830
// DANGO-TODO-MVP: Remove this once the service handles object distribution
18311831
var onlyIncludeOwnedObjects = NetworkManager.CMBServiceConnection;
@@ -1854,6 +1854,11 @@ internal void GetObjectDistribution(ref Dictionary<uint, Dictionary<ulong, List<
18541854
// At this point we only allow things marked with the distributable permission and is not locked to be distributed
18551855
if (networkObject.IsOwnershipDistributable && !networkObject.IsOwnershipLocked)
18561856
{
1857+
// Don't include anything that is not visible to the new client
1858+
if (!networkObject.Observers.Contains(clientId))
1859+
{
1860+
continue;
1861+
}
18571862

18581863
// We have to check if it is an in-scene placed NetworkObject and if it is get the source prefab asset GlobalObjectIdHash value of the in-scene placed instance
18591864
// since all in-scene placed instances use unique GlobalObjectIdHash values.
@@ -1923,7 +1928,7 @@ internal void DistributeNetworkObjects(ulong clientId)
19231928
var objectTypeCount = new Dictionary<uint, int>();
19241929

19251930
// Get all spawned objects by type and then by client owner that are spawned and can be distributed
1926-
GetObjectDistribution(ref distributedNetworkObjects, ref objectTypeCount);
1931+
GetObjectDistribution(clientId, ref distributedNetworkObjects, ref objectTypeCount);
19271932

19281933
var clientCount = NetworkManager.ConnectedClientsIds.Count;
19291934

@@ -1961,7 +1966,6 @@ internal void DistributeNetworkObjects(ulong clientId)
19611966

19621967
var maxDistributeCount = Mathf.Max(ownerList.Value.Count - objPerClient, 1);
19631968
var distributed = 0;
1964-
19651969
// For now when we have more players then distributed NetworkObjects that
19661970
// a specific client owns, just assign half of the NetworkObjects to the new client
19671971
var offsetCount = Mathf.Max((int)Math.Round((float)(ownerList.Value.Count / objPerClient)), 1);
@@ -1974,11 +1978,6 @@ internal void DistributeNetworkObjects(ulong clientId)
19741978
{
19751979
if ((i % offsetCount) == 0)
19761980
{
1977-
while (!ownerList.Value[i].Observers.Contains(clientId))
1978-
{
1979-
i++;
1980-
}
1981-
19821981
var children = ownerList.Value[i].GetComponentsInChildren<NetworkObject>();
19831982
// Since the ownerList.Value[i] has to be distributable, then transfer all child NetworkObjects
19841983
// with the same owner clientId and are marked as distributable also to the same client to keep
@@ -2021,7 +2020,7 @@ internal void DistributeNetworkObjects(ulong clientId)
20212020
var builder = new StringBuilder();
20222021
distributedNetworkObjects.Clear();
20232022
objectTypeCount.Clear();
2024-
GetObjectDistribution(ref distributedNetworkObjects, ref objectTypeCount);
2023+
GetObjectDistribution(clientId, ref distributedNetworkObjects, ref objectTypeCount);
20252024
builder.AppendLine($"Client Relative Distributed Object Count: (distribution follows)");
20262025
// Cycle through each prefab type
20272026
foreach (var objectTypeEntry in distributedNetworkObjects)
@@ -2036,7 +2035,6 @@ internal void DistributeNetworkObjects(ulong clientId)
20362035
}
20372036
Debug.Log(builder.ToString());
20382037
}
2039-
20402038
}
20412039

20422040
internal struct DeferredDespawnObject

0 commit comments

Comments
 (0)