Skip to content

Commit 4ee4865

Browse files
fix
Session owner now starts and connects ahead of the other clients. This exposed an issue with the connection sequence and the NetworkClient not getting the approved set to true when the client finished synchronizing. FIxed an issue where NetworkManager names were skewed by 1 during the initial start up sequence (which can make things confusing if logging its name). Fixed an issue with invoking the ClientNetworkManagerPostStartInit a 2nd time when connected to a CMB server. Added some additional logging within the NetworkObjectOwnershipTests.TestOwnedObjectCounts. Added a better logical check within the ConnectionApprovedMessage.
1 parent 891ccde commit 4ee4865

File tree

5 files changed

+149
-111
lines changed

5 files changed

+149
-111
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Diagnostics;
23
using Unity.Collections;
34

45
namespace Unity.Netcode
@@ -273,10 +274,10 @@ public void Handle(ref NetworkContext context)
273274
// Stop the client-side approval timeout coroutine since we are approved.
274275
networkManager.ConnectionManager.StopClientApprovalCoroutine();
275276

276-
networkManager.ConnectionManager.ConnectedClientIds.Clear();
277277
foreach (var clientId in ConnectedClientIds)
278278
{
279-
if (!networkManager.ConnectionManager.ConnectedClientIds.Contains(clientId))
279+
// If there is any disconnect between the connection sequence of Ids vs ConnectedClients, then add the client.
280+
if (!networkManager.ConnectionManager.ConnectedClientIds.Contains(clientId) || !networkManager.ConnectionManager.ConnectedClients.ContainsKey(clientId))
280281
{
281282
networkManager.ConnectionManager.AddClient(clientId);
282283
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2604,8 +2604,15 @@ private void HandleSessionOwnerEvent(uint sceneEventId, ulong clientId)
26042604
SceneName = string.Empty,
26052605
ClientId = clientId
26062606
});
2607-
if (NetworkManager.ConnectedClients.ContainsKey(clientId))
2607+
2608+
if (NetworkManager.DistributedAuthorityMode)
26082609
{
2610+
// Make sure we have a NetworkClient for this synchronized client
2611+
if (!NetworkManager.ConnectedClients.ContainsKey(clientId))
2612+
{
2613+
NetworkManager.ConnectionManager.AddClient(clientId);
2614+
}
2615+
// Mark this client as being connected
26092616
NetworkManager.ConnectedClients[clientId].IsConnected = true;
26102617
}
26112618
}
@@ -2619,6 +2626,14 @@ private void HandleSessionOwnerEvent(uint sceneEventId, ulong clientId)
26192626
ClientId = clientId
26202627
});
26212628

2629+
// Make sure we have a NetworkClient for this synchronized client
2630+
if (!NetworkManager.ConnectedClients.ContainsKey(clientId))
2631+
{
2632+
NetworkManager.ConnectionManager.AddClient(clientId);
2633+
}
2634+
// Mark this client as being connected
2635+
NetworkManager.ConnectedClients[clientId].IsConnected = true;
2636+
26222637
// Show any NetworkObjects that are:
26232638
// - Hidden from the session owner
26242639
// - Owned by this client

com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs

Lines changed: 102 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -180,24 +180,16 @@ protected NetworkManager GetAuthorityNetworkManager()
180180
{
181181
if (m_UseCmbService)
182182
{
183-
// TODO: Because we start the NetworkManagers back-to-back, the service can make a NetworkManager
184-
// (other than the very first one) the session owner. This could lead to test instabilities and
185-
// we need to add additional code during the start process that will start the very 1st instance
186-
// and wait for it to be approved and assigned the session owner before we assume anything.
187-
// Otherwise, any pick from the m_NetworkManagers could result in that specific instance not being
188-
// the first session owner.
189-
// If we haven't even started any NetworkManager, then just return the first
190-
// instance until we resolve the above issue.
183+
// If we haven't even started any NetworkManager, then return the first instance
184+
// since it will be the session owner.
191185
if (!NetcodeIntegrationTestHelpers.IsStarted)
192186
{
193187
return m_NetworkManagers[0];
194188
}
195189

196190
foreach (var client in m_NetworkManagers)
197191
{
198-
// See above notes.
199-
// TODO: Once the above is resolved, just check for client.LocalClient.IsSessionOwner
200-
if (!client.LocalClient.IsApproved || client.LocalClient.IsSessionOwner)
192+
if (client.LocalClient.IsSessionOwner)
201193
{
202194
return client;
203195
}
@@ -584,15 +576,6 @@ private void CreatePlayerPrefab()
584576
VerboseDebug($"Exiting {nameof(CreatePlayerPrefab)}");
585577
}
586578

587-
/// <summary>
588-
/// This is invoked before the server and client(s) are started.
589-
/// Override this method if you want to make any adjustments to their
590-
/// NetworkManager instances.
591-
/// </summary>
592-
protected virtual void OnServerAndClientsCreated()
593-
{
594-
}
595-
596579
private void AddRemoveNetworkManager(NetworkManager networkManager, bool addNetworkManager)
597580
{
598581
var clientNetworkManagersList = new List<NetworkManager>(m_ClientNetworkManagers);
@@ -615,6 +598,78 @@ private void AddRemoveNetworkManager(NetworkManager networkManager, bool addNetw
615598
m_NetworkManagers = clientNetworkManagersList.ToArray();
616599
}
617600

601+
/// <summary>
602+
/// This is invoked before the server and client(s) are started.
603+
/// Override this method if you want to make any adjustments to their
604+
/// NetworkManager instances.
605+
/// </summary>
606+
protected virtual void OnServerAndClientsCreated()
607+
{
608+
}
609+
610+
/// <summary>
611+
/// Will create <see cref="NumberOfClients"/> number of clients.
612+
/// To create a specific number of clients <see cref="CreateServerAndClients(int)"/>
613+
/// </summary>
614+
protected void CreateServerAndClients()
615+
{
616+
CreateServerAndClients(NumberOfClients);
617+
}
618+
619+
/// <summary>
620+
/// Creates the server and clients
621+
/// </summary>
622+
/// <param name="numberOfClients">The number of client instances to create</param>
623+
protected void CreateServerAndClients(int numberOfClients)
624+
{
625+
VerboseDebug($"Entering {nameof(CreateServerAndClients)}");
626+
627+
CreatePlayerPrefab();
628+
629+
if (m_EnableTimeTravel)
630+
{
631+
m_TargetFrameRate = -1;
632+
}
633+
634+
// If we are connecting to a CMB server we add +1 for the session owner
635+
if (m_UseCmbService)
636+
{
637+
numberOfClients++;
638+
}
639+
640+
// Create multiple NetworkManager instances
641+
if (!NetcodeIntegrationTestHelpers.Create(numberOfClients, out NetworkManager server, out NetworkManager[] clients, m_TargetFrameRate, m_CreateServerFirst, m_EnableTimeTravel, m_UseCmbService))
642+
{
643+
Debug.LogError("Failed to create instances");
644+
Assert.Fail("Failed to create instances");
645+
}
646+
m_NumberOfClients = numberOfClients;
647+
m_ClientNetworkManagers = clients;
648+
m_ServerNetworkManager = server;
649+
650+
var managers = clients.ToList();
651+
if (!m_UseCmbService)
652+
{
653+
managers.Insert(0, m_ServerNetworkManager);
654+
}
655+
m_NetworkManagers = managers.ToArray();
656+
657+
s_DefaultWaitForTick = new WaitForSecondsRealtime(1.0f / GetAuthorityNetworkManager().NetworkConfig.TickRate);
658+
659+
// Set the player prefab for the server and clients
660+
foreach (var manager in m_NetworkManagers)
661+
{
662+
manager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
663+
SetDistributedAuthorityProperties(manager);
664+
}
665+
666+
// Provides opportunity to allow child derived classes to
667+
// modify the NetworkManager's configuration before starting.
668+
OnServerAndClientsCreated();
669+
670+
VerboseDebug($"Exiting {nameof(CreateServerAndClients)}");
671+
}
672+
618673
/// <summary>
619674
/// CreateAndStartNewClient Only
620675
/// Invoked when the newly created client has been created
@@ -863,69 +918,6 @@ protected void SetTimeTravelSimulatedLatencyJitter(float jitterSeconds)
863918
}
864919
}
865920

866-
/// <summary>
867-
/// Will create <see cref="NumberOfClients"/> number of clients.
868-
/// To create a specific number of clients <see cref="CreateServerAndClients(int)"/>
869-
/// </summary>
870-
protected void CreateServerAndClients()
871-
{
872-
CreateServerAndClients(NumberOfClients);
873-
}
874-
875-
/// <summary>
876-
/// Creates the server and clients
877-
/// </summary>
878-
/// <param name="numberOfClients">The number of client instances to create</param>
879-
protected void CreateServerAndClients(int numberOfClients)
880-
{
881-
VerboseDebug($"Entering {nameof(CreateServerAndClients)}");
882-
883-
CreatePlayerPrefab();
884-
885-
if (m_EnableTimeTravel)
886-
{
887-
m_TargetFrameRate = -1;
888-
}
889-
890-
// If we are connecting to a CMB server we add +1 for the session owner
891-
if (m_UseCmbService)
892-
{
893-
numberOfClients++;
894-
}
895-
896-
// Create multiple NetworkManager instances
897-
if (!NetcodeIntegrationTestHelpers.Create(numberOfClients, out NetworkManager server, out NetworkManager[] clients, m_TargetFrameRate, m_CreateServerFirst, m_EnableTimeTravel, m_UseCmbService))
898-
{
899-
Debug.LogError("Failed to create instances");
900-
Assert.Fail("Failed to create instances");
901-
}
902-
m_NumberOfClients = numberOfClients;
903-
m_ClientNetworkManagers = clients;
904-
m_ServerNetworkManager = server;
905-
906-
var managers = clients.ToList();
907-
if (!m_UseCmbService)
908-
{
909-
managers.Insert(0, m_ServerNetworkManager);
910-
}
911-
m_NetworkManagers = managers.ToArray();
912-
913-
s_DefaultWaitForTick = new WaitForSecondsRealtime(1.0f / GetAuthorityNetworkManager().NetworkConfig.TickRate);
914-
915-
// Set the player prefab for the server and clients
916-
foreach (var manager in m_NetworkManagers)
917-
{
918-
manager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
919-
SetDistributedAuthorityProperties(manager);
920-
}
921-
922-
// Provides opportunity to allow child derived classes to
923-
// modify the NetworkManager's configuration before starting.
924-
OnServerAndClientsCreated();
925-
926-
VerboseDebug($"Exiting {nameof(CreateServerAndClients)}");
927-
}
928-
929921
/// <summary>
930922
/// Override this method and return false in order to be able
931923
/// to manually control when the server and clients are started.
@@ -1028,11 +1020,7 @@ protected void ClientNetworkManagerPostStartInit()
10281020

10291021
if (m_UseHost)
10301022
{
1031-
#if UNITY_2023_1_OR_NEWER
10321023
var clientSideServerPlayerClones = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.None).Where((c) => c.IsPlayerObject && c.OwnerClientId == NetworkManager.ServerClientId);
1033-
#else
1034-
var clientSideServerPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
1035-
#endif
10361024
foreach (var playerNetworkObject in clientSideServerPlayerClones)
10371025
{
10381026
// When the server is not the host this needs to be done
@@ -1056,6 +1044,19 @@ protected virtual bool ShouldCheckForSpawnedPlayers()
10561044
return true;
10571045
}
10581046

1047+
/// <summary>
1048+
/// Starts the session owner and awaits for it to connect before starting the remaining clients.
1049+
/// </summary>
1050+
private IEnumerator StartSessionOwner()
1051+
{
1052+
VerboseDebug("Starting session owner...");
1053+
NetcodeIntegrationTestHelpers.StartOneClient(m_ClientNetworkManagers[0]);
1054+
yield return WaitForConditionOrTimeOut(() => m_ClientNetworkManagers[0].IsConnectedClient);
1055+
AssertOnTimeout($"Timed out waiting for the session owner to connect to CMB Server!");
1056+
Assert.True(m_ClientNetworkManagers[0].LocalClient.IsSessionOwner, $"Client-{m_ClientNetworkManagers[0].LocalClientId} started session but was not set to be the session owner!");
1057+
VerboseDebug("Session owner connected and approved.");
1058+
}
1059+
10591060
/// <summary>
10601061
/// This starts the server and clients as long as <see cref="CanStartServerAndClients"/>
10611062
/// returns true.
@@ -1066,17 +1067,21 @@ protected IEnumerator StartServerAndClients()
10661067
{
10671068
VerboseDebug($"Entering {nameof(StartServerAndClients)}");
10681069

1070+
if (m_UseCmbService)
1071+
{
1072+
VerboseDebug("Using a distributed authority CMB Server for connection.");
1073+
yield return StartSessionOwner();
1074+
}
1075+
10691076
// Start the instances and pass in our SceneManagerInitialization action that is invoked immediately after host-server
10701077
// is started and after each client is started.
1071-
1072-
VerboseDebug($"Starting with useCmbService: {m_UseCmbService}");
10731078
if (!NetcodeIntegrationTestHelpers.Start(m_UseHost, !m_UseCmbService, m_ServerNetworkManager, m_ClientNetworkManagers))
10741079
{
10751080
Debug.LogError("Failed to start instances");
10761081
Assert.Fail("Failed to start instances");
10771082
}
10781083

1079-
// When using the CMBService, we don't have a server, so get the appropriate authority network manager
1084+
// Get the authority NetworkMananger (Server, Host, or Session Owner)
10801085
var authorityManager = GetAuthorityNetworkManager();
10811086

10821087
// When scene management is enabled, we need to re-apply the scenes populated list since we have overriden the ISceneManagerHandler
@@ -1108,13 +1113,8 @@ protected IEnumerator StartServerAndClients()
11081113

11091114
if (m_UseHost || authorityManager.IsHost)
11101115
{
1111-
#if UNITY_2023_1_OR_NEWER
11121116
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
11131117
var serverPlayerClones = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.None).Where((c) => c.IsPlayerObject && c.OwnerClientId == authorityManager.LocalClientId);
1114-
#else
1115-
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
1116-
var serverPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == authorityManager.LocalClientId);
1117-
#endif
11181118
foreach (var playerNetworkObject in serverPlayerClones)
11191119
{
11201120
if (!m_PlayerNetworkObjects.ContainsKey(playerNetworkObject.NetworkManager.LocalClientId))
@@ -1128,6 +1128,8 @@ protected IEnumerator StartServerAndClients()
11281128
}
11291129
}
11301130
}
1131+
1132+
// With distributed authority, we check that all players have spawned on all NetworkManager instances
11311133
if (m_DistributedAuthority)
11321134
{
11331135
foreach (var networkManager in m_NetworkManagers)
@@ -1137,8 +1139,10 @@ protected IEnumerator StartServerAndClients()
11371139
}
11381140
}
11391141

1140-
if (ShouldCheckForSpawnedPlayers())
1142+
// Client-Server or DAHost
1143+
if (ShouldCheckForSpawnedPlayers() && !m_UseCmbService)
11411144
{
1145+
// Check for players being spawned on server instance
11421146
ClientNetworkManagerPostStartInit();
11431147
}
11441148

@@ -1698,13 +1702,12 @@ private bool CheckClientsConnected(NetworkManager[] clientsToCheck)
16981702
}
16991703

17001704
var manager = GetAuthorityNetworkManager();
1701-
var expectedCount = manager.IsHost ? clientsToCheck.Length + 1 : clientsToCheck.Length;
17021705
var currentCount = manager.ConnectedClients.Count;
17031706

1704-
if (currentCount != expectedCount)
1707+
if (currentCount != TotalClients)
17051708
{
17061709
allClientsConnected = false;
1707-
m_InternalErrorLog.AppendLine($"[Server-Side] Expected {expectedCount} clients to connect but only {currentCount} connected!");
1710+
m_InternalErrorLog.AppendLine($"[Server-Side] Expected {TotalClients} clients to connect but only {currentCount} connected!");
17081711
}
17091712

17101713
return allClientsConnected;

com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTestHelpers.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,13 @@ internal static NetworkManager CreateNewClient(int identifier, bool mockTranspor
327327
public static bool CreateNewClients(int clientCount, out NetworkManager[] clients, bool useMockTransport = false, bool useCmbService = false)
328328
{
329329
clients = new NetworkManager[clientCount];
330+
// Pre-identify NetworkManager identifiers based on network topology type
331+
var startCount = useCmbService ? 1 : 0;
330332
for (int i = 0; i < clientCount; i++)
331333
{
332334
// Create networkManager component
333-
clients[i] = CreateNewClient(i, useMockTransport, useCmbService);
335+
clients[i] = CreateNewClient(startCount, useMockTransport, useCmbService);
336+
startCount++;
334337
}
335338

336339
NetworkManagerInstances.AddRange(clients);
@@ -546,6 +549,18 @@ public static bool Start(bool host, NetworkManager server, NetworkManager[] clie
546549

547550
foreach (var client in clients)
548551
{
552+
if (client.IsConnectedClient)
553+
{
554+
// Skip starting the session owner
555+
if (client.DistributedAuthorityMode && client.CMBServiceConnection && client.LocalClient.IsSessionOwner)
556+
{
557+
continue;
558+
}
559+
else
560+
{
561+
throw new Exception("Client NetworkManager is already connected when starting clients!");
562+
}
563+
}
549564
client.StartClient();
550565
hooks = new MultiInstanceHooks();
551566
client.ConnectionManager.MessageManager.Hook(hooks);

0 commit comments

Comments
 (0)