Skip to content

Commit 388a46f

Browse files
Merge branch 'develop-2.0.0' into fix/v2.x/disconnect-event-notifications-2390
2 parents cee5978 + 17a2c04 commit 388a46f

File tree

11 files changed

+393
-216
lines changed

11 files changed

+393
-216
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

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

1414
### Changed
1515

16+
- The `NetworkManager` functions `GetTransportIdFromClientId` and `GetClientIdFromTransportId` will now return `ulong.MaxValue` when the clientId or transportId do not exist. (#3707)
17+
- Improved performance of the NetworkVariable. (#3683)
18+
- Improved performance around the NetworkBehaviour component. (#3687)
1619

1720
### Deprecated
1821

@@ -22,6 +25,9 @@ Additional documentation and release notes are available at [Multiplayer Documen
2225

2326
### Fixed
2427

28+
- Multiple disconnect events from the same transport will no longer disconnect the host. (#3707)
29+
- Distributed authority clients no longer send themselves in the `ClientIds` list when sending a `ChangeOwnershipMessage`. (#3687)
30+
- Made a variety of small performance improvements. (#3683)
2531
- Fixed issue where the disconnect event and provided message was too generic to know why the disconnect occurred. (#3551)
2632

2733
### Security

com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs

Lines changed: 231 additions & 180 deletions
Large diffs are not rendered by default.

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,18 +1451,13 @@ private void HostServerInitialize()
14511451
}
14521452
}
14531453

1454-
response.Approved = true;
1455-
ConnectionManager.HandleConnectionApproval(ServerClientId, response);
1454+
ConnectionManager.HandleConnectionApproval(ServerClientId, response.CreatePlayerObject, response.PlayerPrefabHash, response.Position, response.Rotation);
14561455
}
14571456
else
14581457
{
1459-
var response = new ConnectionApprovalResponse
1460-
{
1461-
Approved = true,
1462-
// Distributed authority always returns true since the client side handles spawning (whether automatically or manually)
1463-
CreatePlayerObject = DistributedAuthorityMode || NetworkConfig.PlayerPrefab != null,
1464-
};
1465-
ConnectionManager.HandleConnectionApproval(ServerClientId, response);
1458+
// Distributed authority always tries to create the player object since the client side handles spawning (whether automatically or manually)
1459+
var createPlayerObject = DistributedAuthorityMode || NetworkConfig.PlayerPrefab != null;
1460+
ConnectionManager.HandleConnectionApproval(ServerClientId, createPlayerObject);
14661461
}
14671462

14681463
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
@@ -1483,15 +1478,27 @@ private void HostServerInitialize()
14831478
/// Get the TransportId from the associated ClientId.
14841479
/// </summary>
14851480
/// <param name="clientId">The ClientId to get the TransportId from</param>
1486-
/// <returns>The TransportId associated with the given ClientId</returns>
1487-
public ulong GetTransportIdFromClientId(ulong clientId) => ConnectionManager.ClientIdToTransportId(clientId);
1481+
/// <returns>
1482+
/// The TransportId associated with the given ClientId if the given clientId is valid; otherwise <see cref="ulong.MaxValue"/>
1483+
/// </returns>
1484+
public ulong GetTransportIdFromClientId(ulong clientId)
1485+
{
1486+
var (id, success) = ConnectionManager.ClientIdToTransportId(clientId);
1487+
return success ? id : ulong.MaxValue;
1488+
}
14881489

14891490
/// <summary>
14901491
/// Get the ClientId from the associated TransportId.
14911492
/// </summary>
14921493
/// <param name="transportId">The TransportId to get the ClientId from</param>
1493-
/// <returns>The ClientId from the associated TransportId</returns>
1494-
public ulong GetClientIdFromTransportId(ulong transportId) => ConnectionManager.TransportIdToClientId(transportId);
1494+
/// <returns>
1495+
/// The ClientId from the associated TransportId if the given transportId is valid; otherwise <see cref="ulong.MaxValue"/>
1496+
/// </returns>
1497+
public ulong GetClientIdFromTransportId(ulong transportId)
1498+
{
1499+
var (id, success) = ConnectionManager.TransportIdToClientId(transportId);
1500+
return success ? id : ulong.MaxValue;
1501+
}
14951502

14961503
/// <summary>
14971504
/// Disconnects the remote client.

com.unity.netcode.gameobjects/Runtime/Messaging/DefaultMessageSender.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,19 @@ public DefaultMessageSender(NetworkManager manager)
1919
public void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData)
2020
{
2121
var sendBuffer = batchData.ToTempByteArray();
22+
var (transportId, clientExists) = m_ConnectionManager.ClientIdToTransportId(clientId);
2223

23-
m_NetworkTransport.Send(m_ConnectionManager.ClientIdToTransportId(clientId), sendBuffer, delivery);
24+
if (!clientExists)
25+
{
26+
if (m_ConnectionManager.NetworkManager.LogLevel <= LogLevel.Error)
27+
{
28+
NetworkLog.LogWarning("Trying to send a message to a client who doesn't have a transport connection");
29+
}
30+
31+
return;
32+
}
33+
34+
m_NetworkTransport.Send(transportId, sendBuffer, delivery);
2435
}
2536
}
2637
}

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,16 @@ public void Handle(ref NetworkContext context)
210210
}
211211
else
212212
{
213-
var response = new NetworkManager.ConnectionApprovalResponse
213+
var createPlayerObject = networkManager.NetworkConfig.PlayerPrefab != null;
214+
215+
// DAHost only:
216+
// Never create the player object on the server if AutoSpawnPlayerPrefabClientSide is set.
217+
if (networkManager.DistributedAuthorityMode && networkManager.AutoSpawnPlayerPrefabClientSide)
214218
{
215-
Approved = true,
216-
CreatePlayerObject = networkManager.DistributedAuthorityMode && networkManager.AutoSpawnPlayerPrefabClientSide ? false : networkManager.NetworkConfig.PlayerPrefab != null
217-
};
218-
networkManager.ConnectionManager.HandleConnectionApproval(senderId, response);
219+
createPlayerObject = false;
220+
}
221+
222+
networkManager.ConnectionManager.HandleConnectionApproval(senderId, createPlayerObject);
219223
}
220224
}
221225
}

com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,11 @@ private void SendBatchedMessages(SendTarget sendTarget, BatchedSendQueue queue)
910910
var mtu = 0;
911911
if (m_NetworkManager)
912912
{
913-
var ngoClientId = m_NetworkManager.ConnectionManager.TransportIdToClientId(sendTarget.ClientId);
913+
var (ngoClientId, isConnectedClient) = m_NetworkManager.ConnectionManager.TransportIdToClientId(sendTarget.ClientId);
914+
if (!isConnectedClient)
915+
{
916+
return;
917+
}
914918
mtu = m_NetworkManager.GetPeerMTU(ngoClientId);
915919
}
916920

@@ -1328,7 +1332,7 @@ public override ulong GetCurrentRtt(ulong clientId)
13281332

13291333
if (m_NetworkManager != null)
13301334
{
1331-
var transportId = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
1335+
var (transportId, _) = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
13321336

13331337
var rtt = ExtractRtt(ParseClientId(transportId));
13341338
if (rtt > 0)
@@ -1354,13 +1358,14 @@ public NetworkEndpoint GetEndpoint(ulong clientId)
13541358
{
13551359
if (m_Driver.IsCreated && m_NetworkManager != null && m_NetworkManager.IsListening)
13561360
{
1357-
var transportId = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
1361+
var (transportId, connectionExists) = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
13581362
var networkConnection = ParseClientId(transportId);
1359-
if (m_Driver.GetConnectionState(networkConnection) == NetworkConnection.State.Connected)
1363+
if (connectionExists && m_Driver.GetConnectionState(networkConnection) == NetworkConnection.State.Connected)
13601364
{
13611365
return m_Driver.GetRemoteEndpoint(networkConnection);
13621366
}
13631367
}
1368+
13641369
return new NetworkEndpoint();
13651370
}
13661371

@@ -1430,10 +1435,18 @@ public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDel
14301435
// If the message is sent reliably, then we're over capacity and we can't
14311436
// provide any reliability guarantees anymore. Disconnect the client since at
14321437
// this point they're bound to become desynchronized.
1438+
if (m_NetworkManager != null)
1439+
{
1440+
var (ngoClientId, isConnectedClient) = m_NetworkManager.ConnectionManager.TransportIdToClientId(clientId);
1441+
if (isConnectedClient)
1442+
{
1443+
clientId = ngoClientId;
1444+
}
1445+
1446+
}
14331447

1434-
var ngoClientId = m_NetworkManager?.ConnectionManager.TransportIdToClientId(clientId) ?? clientId;
14351448
Debug.LogError($"Couldn't add payload of size {payload.Count} to reliable send queue. " +
1436-
$"Closing connection {ngoClientId} as reliability guarantees can't be maintained.");
1449+
$"Closing connection {clientId} as reliability guarantees can't be maintained.");
14371450

14381451
if (clientId == m_ServerClientId)
14391452
{

com.unity.netcode.gameobjects/Tests/Runtime/DisconnectTests.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,24 @@ private void OnConnectionEvent(NetworkManager networkManager, ConnectionEventDat
108108
/// </summary>
109109
private bool TransportIdCleanedUp()
110110
{
111-
if (m_ServerNetworkManager.ConnectionManager.TransportIdToClientId(m_TransportClientId) == m_ClientId)
111+
var (clientId, isConnected) = m_ServerNetworkManager.ConnectionManager.TransportIdToClientId(m_TransportClientId);
112+
if (isConnected)
112113
{
113114
return false;
114115
}
115116

116-
if (m_ServerNetworkManager.ConnectionManager.ClientIdToTransportId(m_ClientId) == m_TransportClientId)
117+
if (clientId == m_ClientId)
117118
{
118119
return false;
119120
}
120-
return true;
121+
122+
var (transportId, connectionExists) = m_ServerNetworkManager.ConnectionManager.ClientIdToTransportId(m_ClientId);
123+
if (connectionExists)
124+
{
125+
return false;
126+
}
127+
128+
return transportId != m_TransportClientId;
121129
}
122130

123131
/// <summary>
@@ -145,7 +153,9 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client
145153

146154
var serverSideClientPlayer = m_ServerNetworkManager.ConnectionManager.ConnectedClients[m_ClientId].PlayerObject;
147155

148-
m_TransportClientId = m_ServerNetworkManager.ConnectionManager.ClientIdToTransportId(m_ClientId);
156+
bool connectionExists;
157+
(m_TransportClientId, connectionExists) = m_ServerNetworkManager.ConnectionManager.ClientIdToTransportId(m_ClientId);
158+
Assert.IsTrue(connectionExists);
149159

150160
if (clientDisconnectType == ClientDisconnectType.ServerDisconnectsClient)
151161
{

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,17 @@ protected void SetDistributedAuthorityProperties(NetworkManager networkManager)
484484
/// </summary>
485485
protected virtual bool m_EnableTimeTravel => false;
486486

487+
/// <summary>
488+
/// When true, <see cref="CreateServerAndClients()"/> and <see cref="CreateNewClient"/> will use a <see cref="MockTransport"/>
489+
/// as the <see cref="NetworkConfig.NetworkTransport"/> on the created server and/or clients.
490+
/// When false, a <see cref="UnityTransport"/> is used.
491+
/// </summary>
492+
/// <remarks>
493+
/// This defaults to, and is required to be true when <see cref="m_EnableTimeTravel"/> is true.
494+
/// <see cref="m_EnableTimeTravel"/> will not work with the <see cref="UnityTransport"/> component.
495+
/// </remarks>
496+
protected virtual bool m_UseMockTransport => m_EnableTimeTravel;
497+
487498
/// <summary>
488499
/// If this is false, SetUp will call OnInlineSetUp instead of OnSetUp.
489500
/// This is a performance advantage when not using the coroutine functionality, as a coroutine that
@@ -638,7 +649,7 @@ public IEnumerator SetUp()
638649
VerboseDebugLog.Clear();
639650
VerboseDebug($"Entering {nameof(SetUp)}");
640651
NetcodeLogAssert = new NetcodeLogAssert();
641-
if (m_EnableTimeTravel)
652+
if (m_UseMockTransport)
642653
{
643654
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests)
644655
{
@@ -648,8 +659,11 @@ public IEnumerator SetUp()
648659
{
649660
MockTransport.Reset();
650661
}
662+
}
651663

652-
// Setup the frames per tick for time travel advance to next tick
664+
// Setup the frames per tick for time travel advance to next tick
665+
if (m_EnableTimeTravel)
666+
{
653667
ConfigureFramesPerTick();
654668
}
655669

@@ -781,7 +795,7 @@ protected void CreateServerAndClients(int numberOfClients)
781795
}
782796

783797
// Create multiple NetworkManager instances
784-
if (!NetcodeIntegrationTestHelpers.Create(numberOfClients, out NetworkManager server, out NetworkManager[] clients, m_TargetFrameRate, m_CreateServerFirst, m_EnableTimeTravel, m_UseCmbService))
798+
if (!NetcodeIntegrationTestHelpers.Create(numberOfClients, out NetworkManager server, out NetworkManager[] clients, m_TargetFrameRate, m_CreateServerFirst, m_UseMockTransport, m_UseCmbService))
785799
{
786800
Debug.LogError("Failed to create instances");
787801
Assert.Fail("Failed to create instances");
@@ -872,7 +886,7 @@ protected virtual bool ShouldWaitForNewClientToConnect(NetworkManager networkMan
872886
/// <returns>The newly created <see cref="NetworkManager"/>.</returns>
873887
protected NetworkManager CreateNewClient()
874888
{
875-
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_EnableTimeTravel, m_UseCmbService);
889+
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_UseMockTransport, m_UseCmbService);
876890
networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
877891
SetDistributedAuthorityProperties(networkManager);
878892

@@ -981,7 +995,7 @@ private bool AllPlayerObjectClonesSpawned(NetworkManager joinedClient)
981995
/// </summary>
982996
protected void CreateAndStartNewClientWithTimeTravel()
983997
{
984-
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_EnableTimeTravel);
998+
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_UseMockTransport);
985999
networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
9861000
SetDistributedAuthorityProperties(networkManager);
9871001

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,18 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ
327327
return true;
328328
}
329329

330-
internal static NetworkManager CreateNewClient(int identifier, bool mockTransport = false, bool useCmbService = false)
330+
/// <summary>
331+
/// Creates a new <see cref="NetworkManager"/> and configures it for use in a multi instance setting.
332+
/// </summary>
333+
/// <param name="identifier">The ClientId representation that is used in the name of the NetworkManager</param>
334+
/// <param name="mockTransport">
335+
/// When true, the client is created with a <see cref="MockTransport"/>; otherwise a <see cref="UnityTransport"/> is added
336+
/// </param>
337+
/// <param name="useCmbService">
338+
/// Whether to configure the client to run against a hosted build of the CMB Service. Only applies if mockTransport is set to false.
339+
/// </param>
340+
/// <returns>The newly created <see cref="NetworkManager"/> component.</returns>
341+
public static NetworkManager CreateNewClient(int identifier, bool mockTransport = false, bool useCmbService = false)
331342
{
332343
// Create gameObject
333344
var go = new GameObject("NetworkManager - Client - " + identifier);
@@ -351,7 +362,7 @@ internal static NetworkManager CreateNewClient(int identifier, bool mockTranspor
351362
/// <param name="clients">Output array containing the created NetworkManager instances</param>
352363
/// <param name="useMockTransport">When true, uses mock transport for testing, otherwise uses real transport. Default value is false</param>
353364
/// <param name="useCmbService">If true, each client will be created with transport configured to connect to a locally hosted da service</param>
354-
/// <returns> Returns <see cref="true"/> if the clients were successfully created and configured, otherwise <see cref="false"/>.</returns>
365+
/// <returns> Returns true if the clients were successfully created and configured, otherwise false.</returns>
355366
public static bool CreateNewClients(int clientCount, out NetworkManager[] clients, bool useMockTransport = false, bool useCmbService = false)
356367
{
357368
clients = new NetworkManager[clientCount];
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using Unity.Netcode.TestHelpers.Runtime;
4+
using UnityEngine.TestTools;
5+
6+
namespace Unity.Netcode.RuntimeTests
7+
{
8+
[TestFixture(HostOrServer.Server)]
9+
[TestFixture(HostOrServer.Host)]
10+
internal class TransportTests : NetcodeIntegrationTest
11+
{
12+
protected override int NumberOfClients => 2;
13+
14+
protected override bool m_UseMockTransport => true;
15+
16+
public TransportTests(HostOrServer hostOrServer) : base(hostOrServer) { }
17+
18+
[UnityTest]
19+
public IEnumerator MultipleDisconnectEventsNoop()
20+
{
21+
var clientToDisconnect = GetNonAuthorityNetworkManager(0);
22+
var clientTransport = clientToDisconnect.NetworkConfig.NetworkTransport;
23+
24+
var otherClient = GetNonAuthorityNetworkManager(1);
25+
26+
// Send multiple disconnect events
27+
clientTransport.DisconnectLocalClient();
28+
clientTransport.DisconnectLocalClient();
29+
30+
// completely stop and clean up the client
31+
yield return StopOneClient(clientToDisconnect);
32+
33+
var expectedConnectedClients = m_UseHost ? NumberOfClients : NumberOfClients - 1;
34+
yield return WaitForConditionOrTimeOut(() => otherClient.ConnectedClientsIds.Count == expectedConnectedClients);
35+
AssertOnTimeout($"Incorrect number of connected clients. Expected: {expectedConnectedClients}, have: {otherClient.ConnectedClientsIds.Count}");
36+
37+
// Start a new client to ensure everything is still working
38+
yield return CreateAndStartNewClient();
39+
40+
var newExpectedClients = m_UseHost ? NumberOfClients + 1 : NumberOfClients;
41+
yield return WaitForConditionOrTimeOut(() => otherClient.ConnectedClientsIds.Count == newExpectedClients);
42+
AssertOnTimeout($"Incorrect number of connected clients. Expected: {newExpectedClients}, have: {otherClient.ConnectedClientsIds.Count}");
43+
44+
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)