Skip to content

Commit 07dd61a

Browse files
authored
fix: Ensure transport connection events don't act if duplicated (#3863)
* fix: Ensure transport connection events don't act if duplicated * Update CHANGELOG * Fix the expected error message in tests * reset MockTransport on shutdown
1 parent 3491f14 commit 07dd61a

File tree

6 files changed

+70
-13
lines changed

6 files changed

+70
-13
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2828

2929
### Fixed
3030

31+
- Duplicate transport connection events for the same connection will now do nothing. (#3863)
3132
- Fixed memory leak in `NetworkAnimator` on clients where `RpcTarget` groups were not being properly disposed due to incorrect type casting of `ProxyRpcTargetGroup` to `RpcTargetGroup`.
3233
- Fixed issue when using a client-server topology where a `NetworkList` with owner write permissions was resetting sent time and dirty flags after having been spawned on owning clients that were not the spawn authority. (#3850)
3334
- Fixed an integer overflow that occurred when configuring a large disconnect timeout with Unity Transport. (#3810)

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

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -494,10 +494,12 @@ internal void HandleNetworkEvent(NetworkEvent networkEvent, ulong transportClien
494494

495495
internal ulong LocalClientTransportId => m_LocalClientTransportId;
496496

497+
private bool m_IsTransportConnected = false;
498+
497499
/// <summary>
498500
/// Handles a <see cref="NetworkEvent.Connect"/> event.
499501
/// </summary>
500-
internal void ConnectEventHandler(ulong transportClientId)
502+
internal void ConnectEventHandler(ulong transportId)
501503
{
502504
#if DEVELOPMENT_BUILD || UNITY_EDITOR
503505
s_TransportConnect.Begin();
@@ -507,20 +509,45 @@ internal void ConnectEventHandler(ulong transportClientId)
507509
// - When client receives one, it *must be* the server
508510
// Client's can't connect to or talk to other clients.
509511
// Server is a sentinel so only one exists, if we are server, we can't be connecting to it.
510-
var clientId = transportClientId;
512+
513+
ulong clientId;
514+
515+
// If we're the server, then this connect event is firing for a new incoming client connection
511516
if (LocalClient.IsServer)
512517
{
518+
var (_, alreadyConnected) = TransportIdToClientId(transportId);
519+
if (alreadyConnected)
520+
{
521+
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
522+
{
523+
NetworkLog.LogError($"[TransportApproval][Server] TransportId {transportId} is already connected to this server!");
524+
}
525+
return;
526+
}
527+
513528
clientId = m_NextClientId++;
514529
}
530+
// Otherwise this connect event is an approved connection from the server
515531
else
516532
{
533+
if (m_IsTransportConnected)
534+
{
535+
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
536+
{
537+
NetworkLog.LogError("[TransportApproval][Client] Client received a transport connection event after already connecting!");
538+
}
539+
return;
540+
}
541+
542+
m_IsTransportConnected = true;
543+
517544
// Cache the local client's transport id.
518-
m_LocalClientTransportId = transportClientId;
545+
m_LocalClientTransportId = transportId;
519546
clientId = NetworkManager.ServerClientId;
520547
}
521548

522-
ClientIdToTransportIdMap[clientId] = transportClientId;
523-
TransportIdToClientIdMap[transportClientId] = clientId;
549+
ClientIdToTransportIdMap[clientId] = transportId;
550+
TransportIdToClientIdMap[transportId] = clientId;
524551
MessageManager.ClientConnected(clientId);
525552

526553
if (LocalClient.IsServer)
@@ -1540,6 +1567,7 @@ internal void Initialize(NetworkManager networkManager)
15401567
ConnectedClientIds.Clear();
15411568
ClientIdToTransportIdMap.Clear();
15421569
TransportIdToClientIdMap.Clear();
1570+
m_IsTransportConnected = false;
15431571
ClientsToApprove.Clear();
15441572
NetworkObject.OrphanChildren.Clear();
15451573
m_DisconnectReason = string.Empty;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReade
104104
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
105105
{
106106
var transportErrorMsg = GetTransportErrorMessage(messageContent, m_NetworkManager);
107-
NetworkLog.LogError($"A {nameof(ConnectionApprovedMessage)} was received from the server when the connection has already been established. {transportErrorMsg}");
107+
NetworkLog.LogError($"A {nameof(ConnectionApprovedMessage)} was received from the server when a connection has already been established. {transportErrorMsg}");
108108
}
109109

110110
return false;
@@ -118,11 +118,11 @@ private static string GetTransportErrorMessage(FastBufferReader messageContent,
118118
{
119119
if (networkManager.NetworkConfig.NetworkTransport is not UnityTransport)
120120
{
121-
return $"NetworkTransport: {networkManager.NetworkConfig.NetworkTransport.GetType()}. Please report this to the maintainer of transport layer.";
121+
return $"NetworkTransport: {networkManager.NetworkConfig.NetworkTransport.GetType()}. Please report this to the maintainer of the transport layer.";
122122
}
123123

124124
var transportVersion = GetTransportVersion(networkManager);
125-
return $"{transportVersion}. This should not happen. Please report this to the Netcode for GameObjects team at https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues and include the following data: Message Size: {messageContent.Length}. Message Content: {NetworkMessageManager.ByteArrayToString(messageContent.ToArray(), 0, messageContent.Length)}";
125+
return $"{transportVersion}. This should not happen. Further information: Message Size: {messageContent.Length}. Message Content: {NetworkMessageManager.ByteArrayToString(messageContent.ToArray(), 0, messageContent.Length)}";
126126
}
127127

128128
private static string GetTransportVersion(NetworkManager networkManager)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public IEnumerator WhenSendingConnectionApprovedToAlreadyConnectedClient_Connect
9696

9797
m_ClientNetworkManagers[0].ConnectionManager.MessageManager.Hook(new Hooks<ConnectionApprovedMessage>());
9898

99-
LogAssert.Expect(LogType.Error, new Regex($"A {nameof(ConnectionApprovedMessage)} was received from the server when the connection has already been established\\. NetworkTransport: Unity.Netcode.Transports.UTP.UnityTransport UnityTransportProtocol: UnityTransport. This should not happen\\."));
99+
LogAssert.Expect(LogType.Error, new Regex($"A {nameof(ConnectionApprovedMessage)} was received from the server when a connection has already been established\\. NetworkTransport: Unity.Netcode.Transports.UTP.UnityTransport UnityTransportProtocol: UnityTransport. This should not happen\\."));
100100

101101
yield return WaitForMessageReceived<UnnamedMessage>(m_ClientNetworkManagers.ToList());
102102
}

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ private struct MessageData
1919
public override ulong ServerClientId { get; } = 0;
2020

2121
public static ulong HighTransportId = 0;
22-
public ulong TransportId = 0;
22+
public ulong TransportId = ulong.MaxValue;
2323
public float SimulatedLatencySeconds;
2424
public float PacketDropRate;
2525
public float LatencyJitter;
@@ -75,15 +75,23 @@ public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte
7575

7676
public override bool StartClient()
7777
{
78-
TransportId = ++HighTransportId;
79-
s_MessageQueue[TransportId] = new Queue<MessageData>();
78+
// TransportId will be the max if it hasn't already been set.
79+
if (TransportId == ulong.MaxValue)
80+
{
81+
TransportId = ++HighTransportId;
82+
s_MessageQueue[TransportId] = new Queue<MessageData>();
83+
}
8084
s_MessageQueue[ServerClientId].Enqueue(new MessageData { Event = NetworkEvent.Connect, FromClientId = TransportId, Payload = new ArraySegment<byte>() });
8185
return true;
8286
}
8387

8488
public override bool StartServer()
8589
{
86-
s_MessageQueue[ServerClientId] = new Queue<MessageData>();
90+
if (TransportId == ulong.MaxValue)
91+
{
92+
TransportId = ServerClientId;
93+
s_MessageQueue[ServerClientId] = new Queue<MessageData>();
94+
}
8795
return true;
8896
}
8997

@@ -104,6 +112,7 @@ public override ulong GetCurrentRtt(ulong clientId)
104112

105113
public override void Shutdown()
106114
{
115+
TransportId = ulong.MaxValue;
107116
}
108117

109118
protected override NetworkTopologyTypes OnCurrentTopology()

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,27 @@ public IEnumerator MultipleDisconnectEventsNoop()
4040
var newExpectedClients = m_UseHost ? NumberOfClients + 1 : NumberOfClients;
4141
yield return WaitForConditionOrTimeOut(() => otherClient.ConnectedClientsIds.Count == newExpectedClients);
4242
AssertOnTimeout($"Incorrect number of connected clients. Expected: {newExpectedClients}, have: {otherClient.ConnectedClientsIds.Count}");
43+
}
44+
45+
[UnityTest]
46+
public IEnumerator MultipleConnectMessagesNoop()
47+
{
48+
var clientToConnect = GetNonAuthorityNetworkManager(0);
49+
var clientTransport = clientToConnect.NetworkConfig.NetworkTransport;
50+
51+
var otherClient = GetNonAuthorityNetworkManager(1);
4352

53+
var currentConnectedClients = m_UseHost ? NumberOfClients + 1 : NumberOfClients;
4454

55+
clientTransport.StartClient();
56+
clientTransport.StartClient();
57+
58+
// Start a new client to ensure everything is still working
59+
yield return CreateAndStartNewClient();
60+
61+
var newExpectedClients = currentConnectedClients + 1;
62+
yield return WaitForConditionOrTimeOut(() => otherClient.ConnectedClientsIds.Count == newExpectedClients);
63+
AssertOnTimeout($"Incorrect number of connected clients. Expected: {newExpectedClients}, have: {otherClient.ConnectedClientsIds.Count}");
4564
}
4665
}
4766
}

0 commit comments

Comments
 (0)