Skip to content

Commit f1c61d3

Browse files
fix: key not found exception when client never connects (#1821)
* fix This fixes the issue where if a client fails to connect after a specific period of time it will throw an exception because it never populated the m_TransportIdToClientIdMap. * update this is a better way of handling this * test The test for this fix where when a client cannot reach the server (i.e. times out) it would throw an exception complaining about a key not being found. This test validates the fix for both UNet and UTP. * Update CHANGELOG.md * update changing the default return to be zero when a client fails to connect.
1 parent 8d211cf commit f1c61d3

File tree

4 files changed

+118
-7
lines changed

4 files changed

+118
-7
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ Additional documentation and release notes are available at [Multiplayer Documen
1919
- Removed `com.unity.modules.animation`, `com.unity.modules.physics` and `com.unity.modules.physics2d` dependencies from the package (#1812)
2020

2121
### Fixed
22+
- Fixed client throws a key not found exception when it times out using UNet or UTP. (#1821)
23+
- Fixed network variable updates are no longer limited to 32,768 bytes when NetworkConfig.EnsureNetworkVariableLengthSafety is enabled. The limits are now determined by what the transport can send in a message. (#1811)
2224
- Fixed in-scene NetworkObjects get destroyed if a client fails to connect and shuts down the NetworkManager. (#1809)
2325
- Fixed user never being notified in the editor that a NetworkBehaviour requires a NetworkObject to function properly. (#1808)
2426
- Fixed PlayerObjects and dynamically spawned NetworkObjects not being added to the NetworkClient's OwnedObjects (#1801)
2527
- Fixed issue where NetworkManager would continue starting even if the NetworkTransport selected failed. (#1780)
2628
- Fixed issue when spawning new player if an already existing player exists it does not remove IsPlayer from the previous player (#1779)
2729
- Fixed lack of notification that NetworkManager and NetworkObject cannot be added to the same GameObject with in-editor notifications (#1777)
28-
- Network variable updates are no longer limited to 32,768 bytes when NetworkConfig.EnsureNetworkVariableLengthSafety is enabled. The limits are now determined by what the transport can send in a message. (#1811)
2930

3031
## [1.0.0-pre.6] - 2022-03-02
3132

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

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#endif
1313
using Unity.Profiling;
1414
using UnityEngine.SceneManagement;
15+
using System.Runtime.CompilerServices;
1516
using Debug = UnityEngine.Debug;
1617

1718
namespace Unity.Netcode
@@ -1436,18 +1437,15 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A
14361437
#if DEVELOPMENT_BUILD || UNITY_EDITOR
14371438
s_TransportDisconnect.Begin();
14381439
#endif
1439-
clientId = TransportIdToClientId(clientId);
1440-
1441-
OnClientDisconnectCallback?.Invoke(clientId);
1442-
1443-
m_TransportIdToClientIdMap.Remove(transportId);
1444-
m_ClientIdToTransportIdMap.Remove(clientId);
1440+
clientId = TransportIdCleanUp(clientId, transportId);
14451441

14461442
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
14471443
{
14481444
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
14491445
}
14501446

1447+
OnClientDisconnectCallback?.Invoke(clientId);
1448+
14511449
if (IsServer)
14521450
{
14531451
OnClientDisconnectFromServer(clientId);
@@ -1463,6 +1461,31 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A
14631461
}
14641462
}
14651463

1464+
/// <summary>
1465+
/// Handles cleaning up the transport id/client id tables after
1466+
/// receiving a disconnect event from transport
1467+
/// </summary>
1468+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1469+
private ulong TransportIdCleanUp(ulong clientId, ulong transportId)
1470+
{
1471+
// This check is for clients that attempted to connect but failed.
1472+
// When this happens, the client will not have an entry within the
1473+
// m_TransportIdToClientIdMap or m_ClientIdToTransportIdMap lookup
1474+
// tables so we exit early and just return 0 to be used for the
1475+
// disconnect event.
1476+
if (!IsServer && !m_TransportIdToClientIdMap.ContainsKey(clientId))
1477+
{
1478+
return 0;
1479+
}
1480+
1481+
clientId = TransportIdToClientId(clientId);
1482+
1483+
m_TransportIdToClientIdMap.Remove(transportId);
1484+
m_ClientIdToTransportIdMap.Remove(clientId);
1485+
1486+
return clientId;
1487+
}
1488+
14661489
internal unsafe int SendMessage<TMessageType, TClientIdListType>(ref TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
14671490
where TMessageType : INetworkMessage
14681491
where TClientIdListType : IReadOnlyList<ulong>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using UnityEngine;
4+
using UnityEngine.TestTools;
5+
using Unity.Netcode.TestHelpers.Runtime;
6+
#if UNITY_UNET_PRESENT
7+
using Unity.Netcode.Transports.UNET;
8+
#endif
9+
10+
namespace Unity.Netcode.RuntimeTests
11+
{
12+
public class ClientOnlyConnectionTests
13+
{
14+
private NetworkManager m_ClientNetworkManager;
15+
private GameObject m_NetworkManagerGameObject;
16+
private WaitForSeconds m_DefaultWaitForTick = new WaitForSeconds(1.0f / 30);
17+
private bool m_WasDisconnected;
18+
private TimeoutHelper m_TimeoutHelper;
19+
20+
[SetUp]
21+
public void Setup()
22+
{
23+
m_WasDisconnected = false;
24+
m_NetworkManagerGameObject = new GameObject();
25+
m_ClientNetworkManager = m_NetworkManagerGameObject.AddComponent<NetworkManager>();
26+
m_ClientNetworkManager.NetworkConfig = new NetworkConfig();
27+
#if UNITY_UNET_PRESENT
28+
m_TimeoutHelper = new TimeoutHelper(30);
29+
m_ClientNetworkManager.NetworkConfig.NetworkTransport = m_NetworkManagerGameObject.AddComponent<UNetTransport>();
30+
#else
31+
// Default is 1000ms per connection attempt and 60 connection attempts (60s)
32+
// Currently there is no easy way to set these values other than in-editor
33+
m_TimeoutHelper = new TimeoutHelper(70);
34+
m_ClientNetworkManager.NetworkConfig.NetworkTransport = m_NetworkManagerGameObject.AddComponent<UnityTransport>();
35+
#endif
36+
}
37+
38+
[UnityTest]
39+
public IEnumerator ClientFailsToConnect()
40+
{
41+
// Wait for the disconnected event
42+
m_ClientNetworkManager.OnClientDisconnectCallback += M_ClientNetworkManager_OnClientDisconnectCallback;
43+
44+
// Only start the client (so it will timeout)
45+
m_ClientNetworkManager.StartClient();
46+
47+
#if !UNITY_UNET_PRESENT
48+
// Unity Transport throws an error when it times out
49+
LogAssert.Expect(LogType.Error, "Failed to connect to server.");
50+
#endif
51+
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => m_WasDisconnected, m_TimeoutHelper);
52+
Assert.False(m_TimeoutHelper.TimedOut, "Timed out waiting for client to timeout waiting to connect!");
53+
54+
// Shutdown the client
55+
m_ClientNetworkManager.Shutdown();
56+
57+
// Wait for a tick
58+
yield return m_DefaultWaitForTick;
59+
}
60+
61+
private void M_ClientNetworkManager_OnClientDisconnectCallback(ulong obj)
62+
{
63+
m_WasDisconnected = true;
64+
}
65+
66+
[TearDown]
67+
public void TearDown()
68+
{
69+
if (m_NetworkManagerGameObject != null)
70+
{
71+
Object.DestroyImmediate(m_NetworkManagerGameObject);
72+
}
73+
}
74+
}
75+
}
76+

com.unity.netcode.gameobjects/Tests/Runtime/ClientFailsToConnectTest.cs.meta

Lines changed: 11 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)