From 0149ea32ca9a07417bde0032fb8c0fe8a70af1ce Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 22 Jan 2026 10:53:49 -0500 Subject: [PATCH 1/7] feat: Make NetworkMetricsPipelineStage public --- com.unity.netcode.gameobjects/CHANGELOG.md | 1 + .../UTP/INetworkStreamDriverConstructor.cs | 2 + .../UTP/NetworkMetricsPipelineStage.cs | 21 ++++++---- .../Runtime/Transports/UTP/UnityTransport.cs | 29 ++++++++++---- .../Editor/Transports/UnityTransportTests.cs | 39 +++++++++++++++++++ 5 files changed, 77 insertions(+), 15 deletions(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 30247ba839..4b11464fe2 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. ### Changed diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs index 93203fec03..42a6e01696 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs @@ -35,6 +35,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, 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..0ab77bbc53 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -1234,13 +1234,28 @@ 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); + 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 var 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..5ed1c8ebbd 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -229,5 +229,44 @@ 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); + + driver.RegisterPipelineStage(new NetworkMetricsPipelineStage()); + + 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; + } } } From 2534c5cd0a99cd4f76cea57460c939b01bcc3d44 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 22 Jan 2026 11:01:55 -0500 Subject: [PATCH 2/7] Add PR number to CHANGELOG entries --- com.unity.netcode.gameobjects/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 4b11464fe2..3203a873c6 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -10,7 +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. +- 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 @@ -25,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 From 5bd6cb3db75d52cb4dbe5fab61c75025d201754c Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 22 Jan 2026 11:10:50 -0500 Subject: [PATCH 3/7] Bump package version to 2.9.0 --- com.unity.netcode.gameobjects/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From 8832981c78fc01e5fa6dcdb1e26f21e5504b093c Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 22 Jan 2026 12:25:12 -0500 Subject: [PATCH 4/7] Fix missing using statement --- .../Tests/Editor/Transports/UnityTransportTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index 5ed1c8ebbd..ac63ab5dc1 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; From 9f2c535e77b205607110417e1eb043a5e3170842 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 22 Jan 2026 12:37:34 -0500 Subject: [PATCH 5/7] Fix scoping error --- .../Runtime/Transports/UTP/UnityTransport.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index 0ab77bbc53..812ef7550d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -1234,6 +1234,8 @@ private void ExtractNetworkMetricsFromPipeline(NetworkPipeline pipeline, Network return; } + var sharedBuffer = default(NativeArray); + try { // Don't need to dispose of the buffers, they are filled with data pointers. @@ -1242,7 +1244,7 @@ private void ExtractNetworkMetricsFromPipeline(NetworkPipeline pipeline, Network networkConnection, out _, out _, - out var sharedBuffer); + out sharedBuffer); } catch (InvalidOperationException) { From 9c52c2a865cdec44f82dc72f5442be61f1da1b80 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 22 Jan 2026 14:36:02 -0500 Subject: [PATCH 6/7] Fix compilation of tests when MP tools package is not present --- .../Tests/Editor/Transports/UnityTransportTests.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index ac63ab5dc1..c54944f3e3 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -243,17 +243,19 @@ public void CreateDriver( 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); - } + unreliableFragmentedPipeline = driver.CreatePipeline(unreliableFragmentedPipelineStages); + unreliableSequencedFragmentedPipeline = driver.CreatePipeline(unreliableSequencedFragmentedPipelineStages); + reliableSequencedPipeline = driver.CreatePipeline(reliableSequencedPipelineStages); + } } [Test] From 3c30d6f2588259f2645aef4c4251380e197be13c Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 22 Jan 2026 15:22:49 -0500 Subject: [PATCH 7/7] Remove space at the end of a line --- .../Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs index 42a6e01696..69fa02c6ed 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs @@ -35,7 +35,7 @@ namespace Unity.Netcode.Transports.UTP /// { /// var settings = transport.GetDefaultNetworkSettings(); /// driver = NetworkDriver.Create(new IPCNetworkInterface(), settings); - /// + /// /// driver.RegisterPipelineStage(new NetworkMetricsPipelineStage()); /// /// transport.GetDefaultPipelineConfigurations(