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",