Skip to content

Commit 358e353

Browse files
committed
fix: Ensure NetworkConnectionManager correctly handles multiple disconnect messages being sent
1 parent b78d4eb commit 358e353

File tree

9 files changed

+358
-218
lines changed

9 files changed

+358
-218
lines changed

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

Lines changed: 232 additions & 187 deletions
Large diffs are not rendered by default.

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,18 +1441,13 @@ private void HostServerInitialize()
14411441
}
14421442
}
14431443

1444-
response.Approved = true;
1445-
ConnectionManager.HandleConnectionApproval(ServerClientId, response);
1444+
ConnectionManager.HandleConnectionApproval(ServerClientId, response.CreatePlayerObject, response.PlayerPrefabHash, response.Position, response.Rotation);
14461445
}
14471446
else
14481447
{
1449-
var response = new ConnectionApprovalResponse
1450-
{
1451-
Approved = true,
1452-
// Distributed authority always returns true since the client side handles spawning (whether automatically or manually)
1453-
CreatePlayerObject = DistributedAuthorityMode || NetworkConfig.PlayerPrefab != null,
1454-
};
1455-
ConnectionManager.HandleConnectionApproval(ServerClientId, response);
1448+
// Distributed authority always tries to create the player object since the client side handles spawning (whether automatically or manually)
1449+
var createPlayerObject = DistributedAuthorityMode || NetworkConfig.PlayerPrefab != null;
1450+
ConnectionManager.HandleConnectionApproval(ServerClientId, createPlayerObject);
14561451
}
14571452

14581453
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
@@ -1474,14 +1469,22 @@ private void HostServerInitialize()
14741469
/// </summary>
14751470
/// <param name="clientId">The ClientId to get the TransportId from</param>
14761471
/// <returns>The TransportId associated with the given ClientId</returns>
1477-
public ulong GetTransportIdFromClientId(ulong clientId) => ConnectionManager.ClientIdToTransportId(clientId);
1472+
public ulong GetTransportIdFromClientId(ulong clientId)
1473+
{
1474+
var (id, success) = ConnectionManager.TransportIdToClientId(clientId);
1475+
return success ? id : ulong.MaxValue;
1476+
}
14781477

14791478
/// <summary>
14801479
/// Get the ClientId from the associated TransportId.
14811480
/// </summary>
14821481
/// <param name="transportId">The TransportId to get the ClientId from</param>
14831482
/// <returns>The ClientId from the associated TransportId</returns>
1484-
public ulong GetClientIdFromTransportId(ulong transportId) => ConnectionManager.TransportIdToClientId(transportId);
1483+
public ulong GetClientIdFromTransportId(ulong transportId)
1484+
{
1485+
var (id, success) = ConnectionManager.TransportIdToClientId(transportId);
1486+
return success ? id : ulong.MaxValue;
1487+
}
14851488

14861489
/// <summary>
14871490
/// 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: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Unity.Collections;
2+
using UnityEngine;
23

34
namespace Unity.Netcode
45
{
@@ -210,12 +211,16 @@ public void Handle(ref NetworkContext context)
210211
}
211212
else
212213
{
213-
var response = new NetworkManager.ConnectionApprovalResponse
214+
var createPlayerObject = networkManager.NetworkConfig.PlayerPrefab != null;
215+
216+
// DAHost only:
217+
// Never create the player object on the server if AutoSpawnPlayerPrefabClientSide is set.
218+
if (networkManager.DistributedAuthorityMode && networkManager.AutoSpawnPlayerPrefabClientSide)
214219
{
215-
Approved = true,
216-
CreatePlayerObject = networkManager.DistributedAuthorityMode && networkManager.AutoSpawnPlayerPrefabClientSide ? false : networkManager.NetworkConfig.PlayerPrefab != null
217-
};
218-
networkManager.ConnectionManager.HandleConnectionApproval(senderId, response);
220+
createPlayerObject = false;
221+
}
222+
223+
networkManager.ConnectionManager.HandleConnectionApproval(senderId, createPlayerObject);
219224
}
220225
}
221226
}

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

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

@@ -1321,7 +1325,7 @@ public override ulong GetCurrentRtt(ulong clientId)
13211325

13221326
if (m_NetworkManager != null)
13231327
{
1324-
var transportId = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
1328+
var (transportId, _) = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
13251329

13261330
var rtt = ExtractRtt(ParseClientId(transportId));
13271331
if (rtt > 0)
@@ -1347,13 +1351,14 @@ public NetworkEndpoint GetEndpoint(ulong clientId)
13471351
{
13481352
if (m_Driver.IsCreated && m_NetworkManager != null && m_NetworkManager.IsListening)
13491353
{
1350-
var transportId = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
1354+
var (transportId, connectionExists) = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
13511355
var networkConnection = ParseClientId(transportId);
1352-
if (m_Driver.GetConnectionState(networkConnection) == NetworkConnection.State.Connected)
1356+
if (connectionExists && m_Driver.GetConnectionState(networkConnection) == NetworkConnection.State.Connected)
13531357
{
13541358
return m_Driver.GetRemoteEndpoint(networkConnection);
13551359
}
13561360
}
1361+
13571362
return new NetworkEndpoint();
13581363
}
13591364

@@ -1447,10 +1452,18 @@ public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDel
14471452
// If the message is sent reliably, then we're over capacity and we can't
14481453
// provide any reliability guarantees anymore. Disconnect the client since at
14491454
// this point they're bound to become desynchronized.
1455+
if (m_NetworkManager != null)
1456+
{
1457+
var (ngoClientId, isConnectedClient) = m_NetworkManager.ConnectionManager.TransportIdToClientId(clientId);
1458+
if (isConnectedClient)
1459+
{
1460+
clientId = ngoClientId;
1461+
}
1462+
1463+
}
14501464

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

14551468
if (clientId == m_ServerClientId)
14561469
{

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: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@ protected void SetDistributedAuthorityProperties(NetworkManager networkManager)
484484
/// </summary>
485485
protected virtual bool m_EnableTimeTravel => false;
486486

487+
protected virtual bool m_UseMockTransport => m_EnableTimeTravel;
488+
487489
/// <summary>
488490
/// If this is false, SetUp will call OnInlineSetUp instead of OnSetUp.
489491
/// This is a performance advantage when not using the coroutine functionality, as a coroutine that
@@ -637,7 +639,7 @@ public IEnumerator SetUp()
637639
VerboseDebugLog.Clear();
638640
VerboseDebug($"Entering {nameof(SetUp)}");
639641
NetcodeLogAssert = new NetcodeLogAssert();
640-
if (m_EnableTimeTravel)
642+
if (m_UseMockTransport)
641643
{
642644
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests)
643645
{
@@ -780,7 +782,7 @@ protected void CreateServerAndClients(int numberOfClients)
780782
}
781783

782784
// Create multiple NetworkManager instances
783-
if (!NetcodeIntegrationTestHelpers.Create(numberOfClients, out NetworkManager server, out NetworkManager[] clients, m_TargetFrameRate, m_CreateServerFirst, m_EnableTimeTravel, m_UseCmbService))
785+
if (!NetcodeIntegrationTestHelpers.Create(numberOfClients, out NetworkManager server, out NetworkManager[] clients, m_TargetFrameRate, m_CreateServerFirst, m_UseMockTransport, m_UseCmbService))
784786
{
785787
Debug.LogError("Failed to create instances");
786788
Assert.Fail("Failed to create instances");
@@ -871,7 +873,7 @@ protected virtual bool ShouldWaitForNewClientToConnect(NetworkManager networkMan
871873
/// <returns>The newly created <see cref="NetworkManager"/>.</returns>
872874
protected NetworkManager CreateNewClient()
873875
{
874-
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_EnableTimeTravel, m_UseCmbService);
876+
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_UseMockTransport, m_UseCmbService);
875877
networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
876878
SetDistributedAuthorityProperties(networkManager);
877879

@@ -980,7 +982,7 @@ private bool AllPlayerObjectClonesSpawned(NetworkManager joinedClient)
980982
/// </summary>
981983
protected void CreateAndStartNewClientWithTimeTravel()
982984
{
983-
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_EnableTimeTravel);
985+
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length, m_UseMockTransport);
984986
networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
985987
SetDistributedAuthorityProperties(networkManager);
986988

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System.Collections;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
using Unity.Netcode.TestHelpers.Runtime;
5+
using UnityEngine.TestTools;
6+
7+
namespace Unity.Netcode.RuntimeTests
8+
{
9+
[TestFixture(HostOrServer.Server)]
10+
[TestFixture(HostOrServer.Host)]
11+
internal class TransportTests : NetcodeIntegrationTest
12+
{
13+
protected override int NumberOfClients => 2;
14+
15+
protected override bool m_UseMockTransport => true;
16+
17+
public TransportTests(HostOrServer hostOrServer) : base(hostOrServer) { }
18+
19+
[UnityTest]
20+
public IEnumerator MultipleDisconnectEventsNoop()
21+
{
22+
var clientToDisconnect = GetNonAuthorityNetworkManager(0);
23+
var clientTransport = clientToDisconnect.NetworkConfig.NetworkTransport;
24+
25+
var otherClient = GetNonAuthorityNetworkManager(1);
26+
27+
// Send multiple disconnect events
28+
clientTransport.DisconnectLocalClient();
29+
clientTransport.DisconnectLocalClient();
30+
31+
// completely stop and clean up the client
32+
yield return StopOneClient(clientToDisconnect);
33+
34+
var expectedConnectedClients = m_UseHost ? NumberOfClients : NumberOfClients - 1;
35+
yield return WaitForConditionOrTimeOut(() => otherClient.ConnectedClientsIds.Count == expectedConnectedClients);
36+
AssertOnTimeout($"Incorrect number of connected clients. Expected: {expectedConnectedClients}, have: {otherClient.ConnectedClientsIds.Count}");
37+
38+
// Start a new client to ensure everything is still working
39+
yield return CreateAndStartNewClient();
40+
41+
var newExpectedClients = m_UseHost ? NumberOfClients + 1 : NumberOfClients;
42+
yield return WaitForConditionOrTimeOut(() => otherClient.ConnectedClientsIds.Count == newExpectedClients);
43+
AssertOnTimeout($"Incorrect number of connected clients. Expected: {newExpectedClients}, have: {otherClient.ConnectedClientsIds.Count}");
44+
45+
46+
}
47+
}
48+
}

com.unity.netcode.gameobjects/Tests/Runtime/Transports/TransportTests.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)