Skip to content

Commit 8c0f843

Browse files
Merge branch 'develop-2.0.0' into netcode-update-after-2.9.0-release-branch-creation
2 parents 69180ba + 263dd4b commit 8c0f843

24 files changed

+338
-48
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

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

1111
### Added
1212

13+
- The `NetworkMetricsPipelineStage` for Unity Transport is now part of the public API. This allows using it in custom implementations of `INetworkStreamDriverConstructor` that want to maintain compatibility with the multiplayer tools package. (#3853)
1314

1415
### Changed
1516

@@ -22,6 +23,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
2223

2324
### Fixed
2425

26+
- Fixed issue where `NetworkManager` was not cleaning itself up if an exception was thrown while starting. (#3864)
27+
- Prevented a `NullReferenceException` in `UnityTransport` when using a custom `INetworkStreamDriverConstructor` that doesn't use all the default pipelines and the multiplayer tools package is installed. (#3853)
2528

2629
### Security
2730

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

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,17 @@ public bool StartServer()
13231323
}
13241324
ConnectionManager.LocalClient.ClientId = ServerClientId;
13251325

1326-
Initialize(true);
1326+
try
1327+
{
1328+
Initialize(true);
1329+
}
1330+
catch (Exception ex)
1331+
{
1332+
Debug.LogException(ex);
1333+
// Always shutdown to assure everything is cleaned up
1334+
ShutdownInternal();
1335+
return false;
1336+
}
13271337

13281338
try
13291339
{
@@ -1342,11 +1352,12 @@ public bool StartServer()
13421352

13431353
ConnectionManager.TransportFailureEventHandler(true);
13441354
}
1345-
catch (Exception)
1355+
catch (Exception ex)
13461356
{
1347-
ConnectionManager.LocalClient.SetRole(false, false);
1357+
Debug.LogException(ex);
1358+
// Always shutdown to assure everything is cleaned up
1359+
ShutdownInternal();
13481360
IsListening = false;
1349-
throw;
13501361
}
13511362

13521363
return IsListening;
@@ -1373,7 +1384,16 @@ public bool StartClient()
13731384
return false;
13741385
}
13751386

1376-
Initialize(false);
1387+
try
1388+
{
1389+
Initialize(false);
1390+
}
1391+
catch (Exception ex)
1392+
{
1393+
Debug.LogException(ex);
1394+
ShutdownInternal();
1395+
return false;
1396+
}
13771397

13781398
try
13791399
{
@@ -1391,7 +1411,7 @@ public bool StartClient()
13911411
catch (Exception ex)
13921412
{
13931413
Debug.LogException(ex);
1394-
ConnectionManager.LocalClient.SetRole(false, false);
1414+
ShutdownInternal();
13951415
IsListening = false;
13961416
}
13971417

@@ -1419,7 +1439,18 @@ public bool StartHost()
14191439
return false;
14201440
}
14211441

1422-
Initialize(true);
1442+
try
1443+
{
1444+
Initialize(true);
1445+
}
1446+
catch (Exception ex)
1447+
{
1448+
Debug.LogException(ex);
1449+
// Always shutdown to assure everything is cleaned up
1450+
ShutdownInternal();
1451+
return false;
1452+
}
1453+
14231454
try
14241455
{
14251456
IsListening = NetworkConfig.NetworkTransport.StartServer();
@@ -1437,7 +1468,8 @@ public bool StartHost()
14371468
catch (Exception ex)
14381469
{
14391470
Debug.LogException(ex);
1440-
ConnectionManager.LocalClient.SetRole(false, false);
1471+
// Always shutdown to assure everything is cleaned up
1472+
ShutdownInternal();
14411473
IsListening = false;
14421474
}
14431475

com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,15 @@ internal NetworkObject HandleNetworkPrefabSpawn(uint networkPrefabAssetHash, ulo
287287
{
288288
if (m_PrefabAssetToPrefabHandlerWithData.TryGetValue(networkPrefabAssetHash, out var prefabInstanceHandler))
289289
{
290-
networkObjectInstance = prefabInstanceHandler.Instantiate(ownerClientId, position, rotation, instantiationData);
290+
try
291+
{
292+
networkObjectInstance = prefabInstanceHandler.Instantiate(ownerClientId, position, rotation, instantiationData);
293+
}
294+
catch (Exception ex)
295+
{
296+
Debug.LogException(ex);
297+
return null;
298+
}
291299
}
292300
else
293301
{
@@ -297,7 +305,15 @@ internal NetworkObject HandleNetworkPrefabSpawn(uint networkPrefabAssetHash, ulo
297305
}
298306
else if (m_PrefabAssetToPrefabHandler.TryGetValue(networkPrefabAssetHash, out var prefabInstanceHandler))
299307
{
300-
networkObjectInstance = prefabInstanceHandler.Instantiate(ownerClientId, position, rotation);
308+
try
309+
{
310+
networkObjectInstance = prefabInstanceHandler.Instantiate(ownerClientId, position, rotation);
311+
}
312+
catch (Exception ex)
313+
{
314+
Debug.LogException(ex);
315+
return null;
316+
}
301317
}
302318

303319
// Now we must make sure this alternate PrefabAsset spawned in place of the prefab asset with the networkPrefabAssetHash (GlobalObjectIdHash)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ namespace Unity.Netcode.Transports.UTP
3636
/// var settings = transport.GetDefaultNetworkSettings();
3737
/// driver = NetworkDriver.Create(new IPCNetworkInterface(), settings);
3838
///
39+
/// driver.RegisterPipelineStage(new NetworkMetricsPipelineStage());
40+
///
3941
/// transport.GetDefaultPipelineConfigurations(
4042
/// out var unreliableFragmentedPipelineStages,
4143
/// out var unreliableSequencedFragmentedPipelineStages,

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,32 @@
77

88
namespace Unity.Netcode.Transports.UTP
99
{
10+
/// <summary>
11+
/// A pipeline stage that tracks some internal metrics that are then used by the multiplayer
12+
/// tools package. This should only be used when creating a custom <see cref="NetworkDriver"/>
13+
/// with <see cref="INetworkStreamDriverConstructor"/> if compatibility with the multiplayer
14+
/// tools package is desired. In that situation, this stage needs to be registered with the
15+
/// constructed driver with <see cref="NetworkDriver.RegisterPipelineStage{T}"/>.
16+
/// </summary>
1017
[BurstCompile]
11-
internal unsafe struct NetworkMetricsPipelineStage : INetworkPipelineStage
18+
public unsafe struct NetworkMetricsPipelineStage : INetworkPipelineStage
1219
{
13-
private static TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate> s_ReceiveFunction = new TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate>(Receive);
14-
private static TransportFunctionPointer<NetworkPipelineStage.SendDelegate> s_SendFunction = new TransportFunctionPointer<NetworkPipelineStage.SendDelegate>(Send);
15-
private static TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate> s_InitializeConnectionFunction = new TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate>(InitializeConnection);
16-
20+
/// <inheritdoc/>
1721
public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer,
1822
int staticInstanceBufferLength,
1923
NetworkSettings settings)
2024
{
2125
return new NetworkPipelineStage(
22-
s_ReceiveFunction,
23-
s_SendFunction,
24-
s_InitializeConnectionFunction,
26+
new TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate>(Receive),
27+
new TransportFunctionPointer<NetworkPipelineStage.SendDelegate>(Send),
28+
new TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate>(InitializeConnection),
2529
ReceiveCapacity: 0,
2630
SendCapacity: 0,
2731
HeaderCapacity: 0,
2832
SharedStateCapacity: UnsafeUtility.SizeOf<NetworkMetricsContext>());
2933
}
3034

35+
/// <inheritdoc/>
3136
public int StaticSize => 0;
3237

3338
[BurstCompile(DisableDirectCall = true)]

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,13 +1234,30 @@ private void ExtractNetworkMetricsFromPipeline(NetworkPipeline pipeline, Network
12341234
return;
12351235
}
12361236

1237-
//Don't need to dispose of the buffers, they are filled with data pointers.
1238-
m_Driver.GetPipelineBuffers(pipeline,
1239-
NetworkPipelineStageId.Get<NetworkMetricsPipelineStage>(),
1240-
networkConnection,
1241-
out _,
1242-
out _,
1243-
out var sharedBuffer);
1237+
var sharedBuffer = default(NativeArray<byte>);
1238+
1239+
try
1240+
{
1241+
// Don't need to dispose of the buffers, they are filled with data pointers.
1242+
m_Driver.GetPipelineBuffers(pipeline,
1243+
NetworkPipelineStageId.Get<NetworkMetricsPipelineStage>(),
1244+
networkConnection,
1245+
out _,
1246+
out _,
1247+
out sharedBuffer);
1248+
}
1249+
catch (InvalidOperationException)
1250+
{
1251+
// Can happen if using a custom driver that isn't configured with the metrics stage.
1252+
return;
1253+
}
1254+
1255+
// That InvalidOperationException above is only thrown in the editor. In runtime builds
1256+
// we instead get default return values when the pipeline stage is invalid.
1257+
if (sharedBuffer == default)
1258+
{
1259+
return;
1260+
}
12441261

12451262
unsafe
12461263
{

com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using NUnit.Framework;
22
using Unity.Netcode.Transports.UTP;
3+
using Unity.Networking.Transport;
34
using UnityEngine;
45
using UnityEngine.TestTools;
56

@@ -154,21 +155,21 @@ public void UnityTransport_StartServerWithoutAddresses()
154155
[Test]
155156
public void UnityTransport_EmptySecurityStringsShouldThrow([Values("", null)] string cert, [Values("", null)] string secret)
156157
{
157-
var supportingGO = new GameObject();
158+
var supportingGo = new GameObject();
158159
try
159160
{
160-
var networkManager = supportingGO.AddComponent<NetworkManager>(); // NM is required for UTP to work with certificates.
161+
var networkManager = supportingGo.AddComponent<NetworkManager>(); // NM is required for UTP to work with certificates.
161162
networkManager.NetworkConfig = new NetworkConfig();
162-
UnityTransport transport = supportingGO.AddComponent<UnityTransport>();
163+
UnityTransport transport = supportingGo.AddComponent<UnityTransport>();
163164
networkManager.NetworkConfig.NetworkTransport = transport;
164-
transport.Initialize();
165+
transport.Initialize(networkManager);
165166
transport.SetServerSecrets(serverCertificate: cert, serverPrivateKey: secret);
166167

167168
// Use encryption, but don't set certificate and check for exception
168169
transport.UseEncryption = true;
169170
Assert.Throws<System.Exception>(() =>
170171
{
171-
networkManager.StartServer();
172+
transport.StartServer();
172173
});
173174
// Make sure StartServer failed
174175
Assert.False(transport.GetNetworkDriver().IsCreated);
@@ -177,9 +178,9 @@ public void UnityTransport_EmptySecurityStringsShouldThrow([Values("", null)] st
177178
}
178179
finally
179180
{
180-
if (supportingGO != null)
181+
if (supportingGo != null)
181182
{
182-
Object.DestroyImmediate(supportingGO);
183+
Object.DestroyImmediate(supportingGo);
183184
}
184185
}
185186
}
@@ -229,5 +230,46 @@ public void UnityTransport_HostnameValidation((string, bool) testCase)
229230
transport.Shutdown();
230231
}
231232
#endif
233+
234+
private class IPCDriverConstructor : INetworkStreamDriverConstructor
235+
{
236+
public void CreateDriver(
237+
UnityTransport transport,
238+
out NetworkDriver driver,
239+
out NetworkPipeline unreliableFragmentedPipeline,
240+
out NetworkPipeline unreliableSequencedFragmentedPipeline,
241+
out NetworkPipeline reliableSequencedPipeline)
242+
{
243+
var settings = transport.GetDefaultNetworkSettings();
244+
driver = NetworkDriver.Create(new IPCNetworkInterface(), settings);
245+
246+
#if MULTIPLAYER_TOOLS
247+
driver.RegisterPipelineStage(new NetworkMetricsPipelineStage());
248+
#endif
249+
250+
transport.GetDefaultPipelineConfigurations(
251+
out var unreliableFragmentedPipelineStages,
252+
out var unreliableSequencedFragmentedPipelineStages,
253+
out var reliableSequencedPipelineStages);
254+
255+
unreliableFragmentedPipeline = driver.CreatePipeline(unreliableFragmentedPipelineStages);
256+
unreliableSequencedFragmentedPipeline = driver.CreatePipeline(unreliableSequencedFragmentedPipelineStages);
257+
reliableSequencedPipeline = driver.CreatePipeline(reliableSequencedPipelineStages);
258+
}
259+
}
260+
261+
[Test]
262+
public void UnityTransport_CustomDriverConstructorWithDefaultPipelines()
263+
{
264+
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
265+
UnityTransport.s_DriverConstructor = new IPCDriverConstructor();
266+
transport.Initialize();
267+
268+
Assert.True(transport.StartServer());
269+
270+
transport.Shutdown();
271+
272+
UnityTransport.s_DriverConstructor = null;
273+
}
232274
}
233275
}

com.unity.netcode.gameobjects/Tests/Runtime/ClientApprovalDenied.cs renamed to com.unity.netcode.gameobjects/Tests/Runtime/Connection/ClientApprovalDenied.cs

File renamed without changes.

com.unity.netcode.gameobjects/Tests/Runtime/ClientApprovalDenied.cs.meta renamed to com.unity.netcode.gameobjects/Tests/Runtime/Connection/ClientApprovalDenied.cs.meta

File renamed without changes.

com.unity.netcode.gameobjects/Tests/Runtime/ClientOnlyConnectionTests.cs renamed to com.unity.netcode.gameobjects/Tests/Runtime/Connection/ClientOnlyConnectionTests.cs

File renamed without changes.

0 commit comments

Comments
 (0)