diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md
index 30247ba839..3203a873c6 100644
--- a/com.unity.netcode.gameobjects/CHANGELOG.md
+++ b/com.unity.netcode.gameobjects/CHANGELOG.md
@@ -10,6 +10,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Added
+- 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)
### Changed
@@ -24,6 +25,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Fixed
+- 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)
- Fixed an integer overflow that occurred when configuring a large disconnect timeout with Unity Transport. (#3810)
### Security
diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs
index 93203fec03..69fa02c6ed 100644
--- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs
@@ -36,6 +36,8 @@ namespace Unity.Netcode.Transports.UTP
/// var settings = transport.GetDefaultNetworkSettings();
/// driver = NetworkDriver.Create(new IPCNetworkInterface(), settings);
///
+ /// driver.RegisterPipelineStage(new NetworkMetricsPipelineStage());
+ ///
/// transport.GetDefaultPipelineConfigurations(
/// out var unreliableFragmentedPipelineStages,
/// out var unreliableSequencedFragmentedPipelineStages,
diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs
index 30c7dd2691..c602aef739 100644
--- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs
@@ -7,27 +7,32 @@
namespace Unity.Netcode.Transports.UTP
{
+ ///
+ /// A pipeline stage that tracks some internal metrics that are then used by the multiplayer
+ /// tools package. This should only be used when creating a custom
+ /// with if compatibility with the multiplayer
+ /// tools package is desired. In that situation, this stage needs to be registered with the
+ /// constructed driver with .
+ ///
[BurstCompile]
- internal unsafe struct NetworkMetricsPipelineStage : INetworkPipelineStage
+ public unsafe struct NetworkMetricsPipelineStage : INetworkPipelineStage
{
- private static TransportFunctionPointer s_ReceiveFunction = new TransportFunctionPointer(Receive);
- private static TransportFunctionPointer s_SendFunction = new TransportFunctionPointer(Send);
- private static TransportFunctionPointer s_InitializeConnectionFunction = new TransportFunctionPointer(InitializeConnection);
-
+ ///
public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer,
int staticInstanceBufferLength,
NetworkSettings settings)
{
return new NetworkPipelineStage(
- s_ReceiveFunction,
- s_SendFunction,
- s_InitializeConnectionFunction,
+ new TransportFunctionPointer(Receive),
+ new TransportFunctionPointer(Send),
+ new TransportFunctionPointer(InitializeConnection),
ReceiveCapacity: 0,
SendCapacity: 0,
HeaderCapacity: 0,
SharedStateCapacity: UnsafeUtility.SizeOf());
}
+ ///
public int StaticSize => 0;
[BurstCompile(DisableDirectCall = true)]
diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs
index 3f786317ec..812ef7550d 100644
--- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs
@@ -1234,13 +1234,30 @@ private void ExtractNetworkMetricsFromPipeline(NetworkPipeline pipeline, Network
return;
}
- //Don't need to dispose of the buffers, they are filled with data pointers.
- m_Driver.GetPipelineBuffers(pipeline,
- NetworkPipelineStageId.Get(),
- networkConnection,
- out _,
- out _,
- out var sharedBuffer);
+ var sharedBuffer = default(NativeArray);
+
+ try
+ {
+ // Don't need to dispose of the buffers, they are filled with data pointers.
+ m_Driver.GetPipelineBuffers(pipeline,
+ NetworkPipelineStageId.Get(),
+ networkConnection,
+ out _,
+ out _,
+ out sharedBuffer);
+ }
+ catch (InvalidOperationException)
+ {
+ // Can happen if using a custom driver that isn't configured with the metrics stage.
+ return;
+ }
+
+ // That InvalidOperationException above is only thrown in the editor. In runtime builds
+ // we instead get default return values when the pipeline stage is invalid.
+ if (sharedBuffer == default)
+ {
+ return;
+ }
unsafe
{
diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs
index 594467efc2..c54944f3e3 100644
--- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs
@@ -1,5 +1,6 @@
using NUnit.Framework;
using Unity.Netcode.Transports.UTP;
+using Unity.Networking.Transport;
using UnityEngine;
using UnityEngine.TestTools;
@@ -229,5 +230,46 @@ public void UnityTransport_HostnameValidation((string, bool) testCase)
transport.Shutdown();
}
#endif
+
+ private class IPCDriverConstructor : INetworkStreamDriverConstructor
+ {
+ public void CreateDriver(
+ UnityTransport transport,
+ out NetworkDriver driver,
+ out NetworkPipeline unreliableFragmentedPipeline,
+ out NetworkPipeline unreliableSequencedFragmentedPipeline,
+ out NetworkPipeline reliableSequencedPipeline)
+ {
+ var settings = transport.GetDefaultNetworkSettings();
+ driver = NetworkDriver.Create(new IPCNetworkInterface(), settings);
+
+#if MULTIPLAYER_TOOLS
+ driver.RegisterPipelineStage(new NetworkMetricsPipelineStage());
+#endif
+
+ transport.GetDefaultPipelineConfigurations(
+ out var unreliableFragmentedPipelineStages,
+ out var unreliableSequencedFragmentedPipelineStages,
+ out var reliableSequencedPipelineStages);
+
+ unreliableFragmentedPipeline = driver.CreatePipeline(unreliableFragmentedPipelineStages);
+ unreliableSequencedFragmentedPipeline = driver.CreatePipeline(unreliableSequencedFragmentedPipelineStages);
+ reliableSequencedPipeline = driver.CreatePipeline(reliableSequencedPipelineStages);
+ }
+ }
+
+ [Test]
+ public void UnityTransport_CustomDriverConstructorWithDefaultPipelines()
+ {
+ UnityTransport transport = new GameObject().AddComponent();
+ UnityTransport.s_DriverConstructor = new IPCDriverConstructor();
+ transport.Initialize();
+
+ Assert.True(transport.StartServer());
+
+ transport.Shutdown();
+
+ UnityTransport.s_DriverConstructor = null;
+ }
}
}
diff --git a/com.unity.netcode.gameobjects/package.json b/com.unity.netcode.gameobjects/package.json
index d4fc1e4c9d..f833bc8d83 100644
--- a/com.unity.netcode.gameobjects/package.json
+++ b/com.unity.netcode.gameobjects/package.json
@@ -2,7 +2,7 @@
"name": "com.unity.netcode.gameobjects",
"displayName": "Netcode for GameObjects",
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
- "version": "2.8.1",
+ "version": "2.9.0",
"unity": "6000.0",
"dependencies": {
"com.unity.nuget.mono-cecil": "1.11.4",