com.unity.netcode.gameobjects@1.1.0
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.1.0] - 2022-10-21 ### Added - Added `NetworkManager.IsApproved` flag that is set to `true` a client has been approved.(#2261) - `UnityTransport` now provides a way to set the Relay server data directly from the `RelayServerData` structure (provided by the Unity Transport package) throuh its `SetRelayServerData` method. This allows making use of the new APIs in UTP 1.3 that simplify integration of the Relay SDK. (#2235) - IPv6 is now supported for direct connections when using `UnityTransport`. (#2232) - Added WebSocket support when using UTP 2.0 with `UseWebSockets` property in the `UnityTransport` component of the `NetworkManager` allowing to pick WebSockets for communication. When building for WebGL, this selection happens automatically. (#2201) - Added position, rotation, and scale to the `ParentSyncMessage` which provides users the ability to specify the final values on the server-side when `OnNetworkObjectParentChanged` is invoked just before the message is created (when the `Transform` values are applied to the message). (#2146) - Added `NetworkObject.TryRemoveParent` method for convenience purposes opposed to having to cast null to either `GameObject` or `NetworkObject`. (#2146) ### Changed - Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.0. (#2231) - The send queues of `UnityTransport` are now dynamically-sized. This means that there shouldn't be any need anymore to tweak the 'Max Send Queue Size' value. In fact, this field is now removed from the inspector and will not be serialized anymore. It is still possible to set it manually using the `MaxSendQueueSize` property, but it is not recommended to do so aside from some specific needs (e.g. limiting the amount of memory used by the send queues in very constrained environments). (#2212) - As a consequence of the above change, the `UnityTransport.InitialMaxSendQueueSize` field is now deprecated. There is no default value anymore since send queues are dynamically-sized. (#2212) - The debug simulator in `UnityTransport` is now non-deterministic. Its random number generator used to be seeded with a constant value, leading to the same pattern of packet drops, delays, and jitter in every run. (#2196) - `NetworkVariable<>` now supports managed `INetworkSerializable` types, as well as other managed types with serialization/deserialization delegates registered to `UserNetworkVariableSerialization<T>.WriteValue` and `UserNetworkVariableSerialization<T>.ReadValue` (#2219) - `NetworkVariable<>` and `BufferSerializer<BufferSerializerReader>` now deserialize `INetworkSerializable` types in-place, rather than constructing new ones. (#2219) ### Fixed - Fixed `NetworkManager.ApprovalTimeout` will not timeout due to slower client synchronization times as it now uses the added `NetworkManager.IsApproved` flag to determined if the client has been approved or not.(#2261) - Fixed issue caused when changing ownership of objects hidden to some clients (#2242) - Fixed issue where an in-scene placed NetworkObject would not invoke NetworkBehaviour.OnNetworkSpawn if the GameObject was disabled when it was despawned. (#2239) - Fixed issue where clients were not rebuilding the `NetworkConfig` hash value for each unique connection request. (#2226) - Fixed the issue where player objects were not taking the `DontDestroyWithOwner` property into consideration when a client disconnected. (#2225) - Fixed issue where `SceneEventProgress` would not complete if a client late joins while it is still in progress. (#2222) - Fixed issue where `SceneEventProgress` would not complete if a client disconnects. (#2222) - Fixed issues with detecting if a `SceneEventProgress` has timed out. (#2222) - Fixed issue #1924 where `UnityTransport` would fail to restart after a first failure (even if what caused the initial failure was addressed). (#2220) - Fixed issue where `NetworkTransform.SetStateServerRpc` and `NetworkTransform.SetStateClientRpc` were not honoring local vs world space settings when applying the position and rotation. (#2203) - Fixed ILPP `TypeLoadException` on WebGL on MacOS Editor and potentially other platforms. (#2199) - Implicit conversion of NetworkObjectReference to GameObject will now return null instead of throwing an exception if the referenced object could not be found (i.e., was already despawned) (#2158) - Fixed warning resulting from a stray NetworkAnimator.meta file (#2153) - Fixed Connection Approval Timeout not working client side. (#2164) - Fixed issue where the `WorldPositionStays` parenting parameter was not being synchronized with clients. (#2146) - Fixed issue where parented in-scene placed `NetworkObject`s would fail for late joining clients. (#2146) - Fixed issue where scale was not being synchronized which caused issues with nested parenting and scale when `WorldPositionStays` was true. (#2146) - Fixed issue with `NetworkTransform.ApplyTransformToNetworkStateWithInfo` where it was not honoring axis sync settings when `NetworkTransformState.IsTeleportingNextFrame` was true. (#2146) - Fixed issue with `NetworkTransform.TryCommitTransformToServer` where it was not honoring the `InLocalSpace` setting. (#2146) - Fixed ClientRpcs always reporting in the profiler view as going to all clients, even when limited to a subset of clients by `ClientRpcParams`. (#2144) - Fixed RPC codegen failing to choose the correct extension methods for `FastBufferReader` and `FastBufferWriter` when the parameters were a generic type (i.e., List<int>) and extensions for multiple instantiations of that type have been defined (i.e., List<int> and List<string>) (#2142) - Fixed the issue where running a server (i.e. not host) the second player would not receive updates (unless a third player joined). (#2127) - Fixed issue where late-joining client transition synchronization could fail when more than one transition was occurring.(#2127) - Fixed throwing an exception in `OnNetworkUpdate` causing other `OnNetworkUpdate` calls to not be executed. (#1739) - Fixed synchronization when Time.timeScale is set to 0. This changes timing update to use unscaled deltatime. Now network updates rate are independent from the local time scale. (#2171) - Fixed not sending all NetworkVariables to all clients when a client connects to a server. (#1987) - Fixed IsOwner/IsOwnedByServer being wrong on the server after calling RemoveOwnership (#2211)
This commit is contained in:
@@ -7,6 +7,7 @@ using UnityEngine.Networking;
|
||||
|
||||
namespace Unity.Netcode.Transports.UNET
|
||||
{
|
||||
[AddComponentMenu("Netcode/UNet Transport")]
|
||||
public class UNetTransport : NetworkTransport
|
||||
{
|
||||
public enum SendMode
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
using Unity.Networking.Transport;
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
#endif
|
||||
|
||||
namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
@@ -25,7 +29,11 @@ namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
fixed (byte* dataPtr = m_Data)
|
||||
{
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
reader.ReadBytesUnsafe(dataPtr, reader.Length);
|
||||
#else
|
||||
reader.ReadBytes(dataPtr, reader.Length);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +70,11 @@ namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
fixed (byte* dataPtr = m_Data)
|
||||
{
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
reader.ReadBytesUnsafe(dataPtr + m_Offset + m_Length, reader.Length);
|
||||
#else
|
||||
reader.ReadBytes(dataPtr + m_Offset + m_Length, reader.Length);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,22 +8,30 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <summary>Queue for batched messages meant to be sent through UTP.</summary>
|
||||
/// <remarks>
|
||||
/// Messages should be pushed on the queue with <see cref="PushMessage"/>. To send batched
|
||||
/// messages, call <see cref="FillWriter"> with the <see cref="DataStreamWriter"/> obtained from
|
||||
/// <see cref="NetworkDriver.BeginSend"/>. This will fill the writer with as many messages as
|
||||
/// possible. If the send is successful, call <see cref="Consume"/> to remove the data from the
|
||||
/// queue.
|
||||
/// messages, call <see cref="FillWriterWithMessages"/> or <see cref="FillWriterWithBytes"/>
|
||||
/// with the <see cref="DataStreamWriter"/> obtained from <see cref="NetworkDriver.BeginSend"/>.
|
||||
/// This will fill the writer with as many messages/bytes as possible. If the send is
|
||||
/// successful, call <see cref="Consume"/> to remove the data from the queue.
|
||||
///
|
||||
/// This is meant as a companion to <see cref="BatchedReceiveQueue"/>, which should be used to
|
||||
/// read messages sent with this queue.
|
||||
/// </remarks>
|
||||
internal struct BatchedSendQueue : IDisposable
|
||||
{
|
||||
private NativeArray<byte> m_Data;
|
||||
// Note that we're using NativeList basically like a growable NativeArray, where the length
|
||||
// of the list is the capacity of our array. (We can't use the capacity of the list as our
|
||||
// queue capacity because NativeList may elect to set it higher than what we'd set it to
|
||||
// with SetCapacity, which breaks the logic of our code.)
|
||||
private NativeList<byte> m_Data;
|
||||
private NativeArray<int> m_HeadTailIndices;
|
||||
private int m_MaximumCapacity;
|
||||
private int m_MinimumCapacity;
|
||||
|
||||
/// <summary>Overhead that is added to each message in the queue.</summary>
|
||||
public const int PerMessageOverhead = sizeof(int);
|
||||
|
||||
internal const int MinimumMinimumCapacity = 4096;
|
||||
|
||||
// Indices into m_HeadTailIndicies.
|
||||
private const int k_HeadInternalIndex = 0;
|
||||
private const int k_TailInternalIndex = 1;
|
||||
@@ -43,18 +51,33 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
public int Length => TailIndex - HeadIndex;
|
||||
|
||||
public int Capacity => m_Data.Length;
|
||||
public bool IsEmpty => HeadIndex == TailIndex;
|
||||
|
||||
public bool IsCreated => m_Data.IsCreated;
|
||||
|
||||
/// <summary>Construct a new empty send queue.</summary>
|
||||
/// <param name="capacity">Maximum capacity of the send queue.</param>
|
||||
public BatchedSendQueue(int capacity)
|
||||
{
|
||||
m_Data = new NativeArray<byte>(capacity, Allocator.Persistent);
|
||||
// Make sure the maximum capacity will be even.
|
||||
m_MaximumCapacity = capacity + (capacity & 1);
|
||||
|
||||
// We pick the minimum capacity such that if we keep doubling it, we'll eventually hit
|
||||
// the maximum capacity exactly. The alternative would be to use capacities that are
|
||||
// powers of 2, but this can lead to over-allocating quite a bit of memory (especially
|
||||
// since we expect maximum capacities to be in the megabytes range). The approach taken
|
||||
// here avoids this issue, at the cost of not having allocations of nice round sizes.
|
||||
m_MinimumCapacity = m_MaximumCapacity;
|
||||
while (m_MinimumCapacity / 2 >= MinimumMinimumCapacity)
|
||||
{
|
||||
m_MinimumCapacity /= 2;
|
||||
}
|
||||
|
||||
m_Data = new NativeList<byte>(m_MinimumCapacity, Allocator.Persistent);
|
||||
m_HeadTailIndices = new NativeArray<int>(2, Allocator.Persistent);
|
||||
|
||||
m_Data.ResizeUninitialized(m_MinimumCapacity);
|
||||
|
||||
HeadIndex = 0;
|
||||
TailIndex = 0;
|
||||
}
|
||||
@@ -68,18 +91,28 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Write a raw buffer to a DataStreamWriter.</summary>
|
||||
private unsafe void WriteBytes(ref DataStreamWriter writer, byte* data, int length)
|
||||
{
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
writer.WriteBytesUnsafe(data, length);
|
||||
#else
|
||||
writer.WriteBytes(data, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Append data at the tail of the queue. No safety checks.</summary>
|
||||
private void AppendDataAtTail(ArraySegment<byte> data)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var writer = new DataStreamWriter((byte*)m_Data.GetUnsafePtr() + TailIndex, m_Data.Length - TailIndex);
|
||||
var writer = new DataStreamWriter((byte*)m_Data.GetUnsafePtr() + TailIndex, Capacity - TailIndex);
|
||||
|
||||
writer.WriteInt(data.Count);
|
||||
|
||||
fixed (byte* dataPtr = data.Array)
|
||||
{
|
||||
writer.WriteBytes(dataPtr + data.Offset, data.Count);
|
||||
WriteBytes(ref writer, dataPtr + data.Offset, data.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,16 +133,16 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
// Check if there's enough room after the current tail index.
|
||||
if (m_Data.Length - TailIndex >= sizeof(int) + message.Count)
|
||||
if (Capacity - TailIndex >= sizeof(int) + message.Count)
|
||||
{
|
||||
AppendDataAtTail(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if there would be enough room if we moved data at the beginning of m_Data.
|
||||
if (m_Data.Length - TailIndex + HeadIndex >= sizeof(int) + message.Count)
|
||||
// Move the data at the beginning of of m_Data. Either it will leave enough space for
|
||||
// the message, or we'll grow m_Data and will want the data at the beginning anyway.
|
||||
if (HeadIndex > 0 && Length > 0)
|
||||
{
|
||||
// Move the data back at the beginning of m_Data.
|
||||
unsafe
|
||||
{
|
||||
UnsafeUtility.MemMove(m_Data.GetUnsafePtr(), (byte*)m_Data.GetUnsafePtr() + HeadIndex, Length);
|
||||
@@ -117,12 +150,38 @@ namespace Unity.Netcode.Transports.UTP
|
||||
|
||||
TailIndex = Length;
|
||||
HeadIndex = 0;
|
||||
}
|
||||
|
||||
// If there's enough space left at the end for the message, now is a good time to trim
|
||||
// the capacity of m_Data if it got very large. We define "very large" here as having
|
||||
// more than 75% of m_Data unused after adding the new message.
|
||||
if (Capacity - TailIndex >= sizeof(int) + message.Count)
|
||||
{
|
||||
AppendDataAtTail(message);
|
||||
|
||||
while (TailIndex < Capacity / 4 && Capacity > m_MinimumCapacity)
|
||||
{
|
||||
m_Data.ResizeUninitialized(Capacity / 2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// If we get here we need to grow m_Data until the data fits (or it's too large).
|
||||
while (Capacity - TailIndex < sizeof(int) + message.Count)
|
||||
{
|
||||
// Can't grow m_Data anymore. Message simply won't fit.
|
||||
if (Capacity * 2 > m_MaximumCapacity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Data.ResizeUninitialized(Capacity * 2);
|
||||
}
|
||||
|
||||
// If we get here we know there's now enough room for the message.
|
||||
AppendDataAtTail(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -149,12 +208,12 @@ namespace Unity.Netcode.Transports.UTP
|
||||
|
||||
unsafe
|
||||
{
|
||||
var reader = new DataStreamReader((byte*)m_Data.GetUnsafePtr() + HeadIndex, Length);
|
||||
var reader = new DataStreamReader(m_Data.AsArray());
|
||||
|
||||
var writerAvailable = writer.Capacity;
|
||||
var readerOffset = 0;
|
||||
var readerOffset = HeadIndex;
|
||||
|
||||
while (readerOffset < Length)
|
||||
while (readerOffset < TailIndex)
|
||||
{
|
||||
reader.SeekSet(readerOffset);
|
||||
var messageLength = reader.ReadInt();
|
||||
@@ -168,7 +227,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
writer.WriteInt(messageLength);
|
||||
|
||||
var messageOffset = HeadIndex + reader.GetBytesRead();
|
||||
writer.WriteBytes((byte*)m_Data.GetUnsafePtr() + messageOffset, messageLength);
|
||||
WriteBytes(ref writer, (byte*)m_Data.GetUnsafePtr() + messageOffset, messageLength);
|
||||
|
||||
writerAvailable -= sizeof(int) + messageLength;
|
||||
readerOffset += sizeof(int) + messageLength;
|
||||
@@ -205,7 +264,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
|
||||
unsafe
|
||||
{
|
||||
writer.WriteBytes((byte*)m_Data.GetUnsafePtr() + HeadIndex, copyLength);
|
||||
WriteBytes(ref writer, (byte*)m_Data.GetUnsafePtr() + HeadIndex, copyLength);
|
||||
}
|
||||
|
||||
return copyLength;
|
||||
@@ -219,10 +278,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <param name="size">Number of bytes to consume from the queue.</param>
|
||||
public void Consume(int size)
|
||||
{
|
||||
// Adjust the head/tail indices such that we consume the given size.
|
||||
if (size >= Length)
|
||||
{
|
||||
HeadIndex = 0;
|
||||
TailIndex = 0;
|
||||
|
||||
// This is a no-op if m_Data is already at minimum capacity.
|
||||
m_Data.ResizeUninitialized(m_MinimumCapacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4,25 +4,24 @@ using AOT;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Networking.Transport;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
[BurstCompile]
|
||||
internal unsafe struct NetworkMetricsPipelineStage : INetworkPipelineStage
|
||||
{
|
||||
static TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate> ReceiveFunction = new TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate>(Receive);
|
||||
static TransportFunctionPointer<NetworkPipelineStage.SendDelegate> SendFunction = new TransportFunctionPointer<NetworkPipelineStage.SendDelegate>(Send);
|
||||
static TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate> InitializeConnectionFunction = new TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate>(InitializeConnection);
|
||||
private static TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate> s_ReceiveFunction = new TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate>(Receive);
|
||||
private static TransportFunctionPointer<NetworkPipelineStage.SendDelegate> s_SendFunction = new TransportFunctionPointer<NetworkPipelineStage.SendDelegate>(Send);
|
||||
private static TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate> s_InitializeConnectionFunction = new TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate>(InitializeConnection);
|
||||
|
||||
public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer,
|
||||
int staticInstanceBufferLength,
|
||||
NetworkSettings settings)
|
||||
{
|
||||
return new NetworkPipelineStage(
|
||||
ReceiveFunction,
|
||||
SendFunction,
|
||||
InitializeConnectionFunction,
|
||||
s_ReceiveFunction,
|
||||
s_SendFunction,
|
||||
s_InitializeConnectionFunction,
|
||||
ReceiveCapacity: 0,
|
||||
SendCapacity: 0,
|
||||
HeaderCapacity: 0,
|
||||
|
||||
188
Runtime/Transports/UTP/SecretsLoaderHelper.cs
Normal file
188
Runtime/Transports/UTP/SecretsLoaderHelper.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
/// <summary>
|
||||
/// Component to add to a NetworkManager if you want the certificates to be loaded from files.
|
||||
/// Mostly helpful to ease development and testing, especially with self-signed certificates
|
||||
///
|
||||
/// Shipping code should make the calls to
|
||||
/// - SetServerSecrets
|
||||
/// - SetClientSecrets
|
||||
/// directly, instead of relying on this.
|
||||
/// </summary>
|
||||
public class SecretsLoaderHelper : MonoBehaviour
|
||||
{
|
||||
internal struct ServerSecrets
|
||||
{
|
||||
public string ServerPrivate;
|
||||
public string ServerCertificate;
|
||||
};
|
||||
|
||||
internal struct ClientSecrets
|
||||
{
|
||||
public string ServerCommonName;
|
||||
public string ClientCertificate;
|
||||
};
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
var serverSecrets = new ServerSecrets();
|
||||
|
||||
try
|
||||
{
|
||||
serverSecrets.ServerCertificate = ServerCertificate;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.Log(exception);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
serverSecrets.ServerPrivate = ServerPrivate;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.Log(exception);
|
||||
}
|
||||
|
||||
var clientSecrets = new ClientSecrets();
|
||||
try
|
||||
{
|
||||
clientSecrets.ClientCertificate = ClientCA;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.Log(exception);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
clientSecrets.ServerCommonName = ServerCommonName;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.Log(exception);
|
||||
}
|
||||
|
||||
var unityTransportComponent = GetComponent<UnityTransport>();
|
||||
|
||||
if (unityTransportComponent == null)
|
||||
{
|
||||
Debug.LogError($"You need to select the UnityTransport protocol, in the NetworkManager, in order for the SecretsLoaderHelper component to be useful.");
|
||||
return;
|
||||
}
|
||||
|
||||
unityTransportComponent.SetServerSecrets(serverSecrets.ServerCertificate, serverSecrets.ServerPrivate);
|
||||
unityTransportComponent.SetClientSecrets(clientSecrets.ServerCommonName, clientSecrets.ClientCertificate);
|
||||
}
|
||||
|
||||
[Tooltip("Hostname")]
|
||||
[SerializeField]
|
||||
private string m_ServerCommonName = "localhost";
|
||||
|
||||
/// <summary>Common name of the server (typically its hostname).</summary>
|
||||
public string ServerCommonName
|
||||
{
|
||||
get => m_ServerCommonName;
|
||||
set => m_ServerCommonName = value;
|
||||
}
|
||||
|
||||
[Tooltip("Client CA filepath. Useful with self-signed certificates")]
|
||||
[SerializeField]
|
||||
private string m_ClientCAFilePath = ""; // "Assets/Secure/myGameClientCA.pem"
|
||||
|
||||
/// <summary>Client CA filepath. Useful with self-signed certificates</summary>
|
||||
public string ClientCAFilePath
|
||||
{
|
||||
get => m_ClientCAFilePath;
|
||||
set => m_ClientCAFilePath = value;
|
||||
}
|
||||
|
||||
[Tooltip("Client CA Override. Only useful for development with self-signed certificates. Certificate content, for platforms that lack file access (WebGL)")]
|
||||
[SerializeField]
|
||||
private string m_ClientCAOverride = "";
|
||||
|
||||
/// <summary>
|
||||
/// Client CA Override. Only useful for development with self-signed certificates.
|
||||
/// Certificate content, for platforms that lack file access (WebGL)
|
||||
/// </summary>
|
||||
public string ClientCAOverride
|
||||
{
|
||||
get => m_ClientCAOverride;
|
||||
set => m_ClientCAOverride = value;
|
||||
}
|
||||
|
||||
[Tooltip("Server Certificate filepath")]
|
||||
[SerializeField]
|
||||
private string m_ServerCertificateFilePath = ""; // "Assets/Secure/myGameServerCertificate.pem"
|
||||
|
||||
/// <summary>Server Certificate filepath</summary>
|
||||
public string ServerCertificateFilePath
|
||||
{
|
||||
get => m_ServerCertificateFilePath;
|
||||
set => m_ServerCertificateFilePath = value;
|
||||
}
|
||||
|
||||
[Tooltip("Server Private Key filepath")]
|
||||
[SerializeField]
|
||||
private string m_ServerPrivateFilePath = ""; // "Assets/Secure/myGameServerPrivate.pem"
|
||||
|
||||
/// <summary>Server Private Key filepath</summary>
|
||||
public string ServerPrivateFilePath
|
||||
{
|
||||
get => m_ServerPrivateFilePath;
|
||||
set => m_ServerPrivate = value;
|
||||
}
|
||||
|
||||
private string m_ClientCA;
|
||||
|
||||
/// <summary>CA certificate used by the client.</summary>
|
||||
public string ClientCA
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ClientCAOverride != "")
|
||||
{
|
||||
return m_ClientCAOverride;
|
||||
}
|
||||
return ReadFile(m_ClientCAFilePath, "Client Certificate");
|
||||
}
|
||||
set => m_ClientCA = value;
|
||||
}
|
||||
|
||||
private string m_ServerCertificate;
|
||||
|
||||
/// <summary>Certificate used by the server.</summary>
|
||||
public string ServerCertificate
|
||||
{
|
||||
get => ReadFile(m_ServerCertificateFilePath, "Server Certificate");
|
||||
set => m_ServerCertificate = value;
|
||||
}
|
||||
|
||||
private string m_ServerPrivate;
|
||||
|
||||
/// <summary>Private key used by the server.</summary>
|
||||
public string ServerPrivate
|
||||
{
|
||||
get => ReadFile(m_ServerPrivateFilePath, "Server Key");
|
||||
set => m_ServerPrivate = value;
|
||||
}
|
||||
|
||||
private static string ReadFile(string path, string label)
|
||||
{
|
||||
if (path == null || path == "")
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var reader = new StreamReader(path);
|
||||
string fileContent = reader.ReadToEnd();
|
||||
Debug.Log((fileContent.Length > 1) ? ("Successfully loaded " + fileContent.Length + " byte(s) from " + label) : ("Could not read " + label + " file"));
|
||||
return fileContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Transports/UTP/SecretsLoaderHelper.cs.meta
Normal file
11
Runtime/Transports/UTP/SecretsLoaderHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc1e7a8dc597cf24c95e4acf92c0edf5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,3 +1,10 @@
|
||||
// NetSim Implementation compilation boilerplate
|
||||
// All references to UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED should be defined in the same way,
|
||||
// as any discrepancies are likely to result in build failures
|
||||
#if UNITY_EDITOR || (DEVELOPMENT_BUILD && !UNITY_MP_TOOLS_NETSIM_DISABLED_IN_DEVELOP) || (!DEVELOPMENT_BUILD && UNITY_MP_TOOLS_NETSIM_ENABLED_IN_RELEASE)
|
||||
#define UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@@ -8,6 +15,13 @@ using Unity.Collections;
|
||||
using Unity.Networking.Transport;
|
||||
using Unity.Networking.Transport.Relay;
|
||||
using Unity.Networking.Transport.Utilities;
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
using Unity.Networking.Transport.TLS;
|
||||
#endif
|
||||
|
||||
#if !UTP_TRANSPORT_2_0_ABOVE
|
||||
using NetworkEndpoint = Unity.Networking.Transport.NetworkEndPoint;
|
||||
#endif
|
||||
|
||||
namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
@@ -88,6 +102,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// The Netcode for GameObjects NetworkTransport for UnityTransport.
|
||||
/// Note: This is highly recommended to use over UNet.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Netcode/Unity Transport")]
|
||||
public partial class UnityTransport : NetworkTransport, INetworkStreamDriverConstructor
|
||||
{
|
||||
/// <summary>
|
||||
@@ -125,8 +140,13 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <summary>
|
||||
/// The default maximum send queue size
|
||||
/// </summary>
|
||||
[Obsolete("MaxSendQueueSize is now determined dynamically (can still be set programmatically using the MaxSendQueueSize property). This initial value is not used anymore.", false)]
|
||||
public const int InitialMaxSendQueueSize = 16 * InitialMaxPayloadSize;
|
||||
|
||||
// Maximum reliable throughput, assuming the full reliable window can be sent on every
|
||||
// frame at 60 FPS. This will be a large over-estimation in any realistic scenario.
|
||||
private const int k_MaxReliableThroughput = (NetworkParameterConstants.MTU * 32 * 60) / 1000; // bytes per millisecond
|
||||
|
||||
private static ConnectionAddressData s_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, ServerListenAddress = string.Empty };
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
@@ -146,6 +166,30 @@ namespace Unity.Netcode.Transports.UTP
|
||||
[SerializeField]
|
||||
private ProtocolType m_ProtocolType;
|
||||
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
[Tooltip("Per default the client/server will communicate over UDP. Set to true to communicate with WebSocket.")]
|
||||
[SerializeField]
|
||||
private bool m_UseWebSockets = false;
|
||||
|
||||
public bool UseWebSockets
|
||||
{
|
||||
get => m_UseWebSockets;
|
||||
set => m_UseWebSockets = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per default the client/server communication will not be encrypted. Select true to enable DTLS for UDP and TLS for Websocket.
|
||||
/// </summary>
|
||||
[Tooltip("Per default the client/server communication will not be encrypted. Select true to enable DTLS for UDP and TLS for Websocket.")]
|
||||
[SerializeField]
|
||||
private bool m_UseEncryption = false;
|
||||
public bool UseEncryption
|
||||
{
|
||||
get => m_UseEncryption;
|
||||
set => m_UseEncryption = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
[Tooltip("The maximum amount of packets that can be in the internal send/receive queues. Basically this is how many packets can be sent/received in a single update/frame.")]
|
||||
[SerializeField]
|
||||
private int m_MaxPacketQueueSize = InitialMaxPacketQueueSize;
|
||||
@@ -169,15 +213,17 @@ namespace Unity.Netcode.Transports.UTP
|
||||
set => m_MaxPayloadSize = value;
|
||||
}
|
||||
|
||||
[Tooltip("The maximum size in bytes of the transport send queue. The send queue accumulates messages for batching and stores messages when other internal send queues are full. If you routinely observe an error about too many in-flight packets, try increasing this.")]
|
||||
[SerializeField]
|
||||
private int m_MaxSendQueueSize = InitialMaxSendQueueSize;
|
||||
private int m_MaxSendQueueSize = 0;
|
||||
|
||||
/// <summary>The maximum size in bytes of the transport send queue.</summary>
|
||||
/// <remarks>
|
||||
/// The send queue accumulates messages for batching and stores messages when other internal
|
||||
/// send queues are full. If you routinely observe an error about too many in-flight packets,
|
||||
/// try increasing this.
|
||||
/// send queues are full. Note that there should not be any need to set this value manually
|
||||
/// since the send queue size is dynamically sized based on need.
|
||||
///
|
||||
/// This value should only be set if you have particular requirements (e.g. if you want to
|
||||
/// limit the memory usage of the send queues). Note however that setting this value too low
|
||||
/// can easily lead to disconnections under heavy traffic.
|
||||
/// </remarks>
|
||||
public int MaxSendQueueSize
|
||||
{
|
||||
@@ -263,12 +309,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
[SerializeField]
|
||||
public string ServerListenAddress;
|
||||
|
||||
private static NetworkEndPoint ParseNetworkEndpoint(string ip, ushort port)
|
||||
private static NetworkEndpoint ParseNetworkEndpoint(string ip, ushort port)
|
||||
{
|
||||
if (!NetworkEndPoint.TryParse(ip, port, out var endpoint))
|
||||
NetworkEndpoint endpoint = default;
|
||||
|
||||
if (!NetworkEndpoint.TryParse(ip, port, out endpoint, NetworkFamily.Ipv4) &&
|
||||
!NetworkEndpoint.TryParse(ip, port, out endpoint, NetworkFamily.Ipv6))
|
||||
{
|
||||
Debug.LogError($"Invalid network endpoint: {ip}:{port}.");
|
||||
return default;
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
@@ -277,12 +325,12 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <summary>
|
||||
/// Endpoint (IP address and port) clients will connect to.
|
||||
/// </summary>
|
||||
public NetworkEndPoint ServerEndPoint => ParseNetworkEndpoint(Address, Port);
|
||||
public NetworkEndpoint ServerEndPoint => ParseNetworkEndpoint(Address, Port);
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint (IP address and port) server will listen/bind on.
|
||||
/// </summary>
|
||||
public NetworkEndPoint ListenEndPoint => ParseNetworkEndpoint((ServerListenAddress == string.Empty) ? Address : ServerListenAddress, Port);
|
||||
public NetworkEndpoint ListenEndPoint => ParseNetworkEndpoint((ServerListenAddress?.Length == 0) ? Address : ServerListenAddress, Port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -326,6 +374,9 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// - packet jitter (variances in latency, see: https://en.wikipedia.org/wiki/Jitter)
|
||||
/// - packet drop rate (packet loss)
|
||||
/// </summary>
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
[Obsolete("DebugSimulator is no longer supported and has no effect. Use Network Simulator from the Multiplayer Tools package.", false)]
|
||||
#endif
|
||||
public SimulatorParameters DebugSimulator = new SimulatorParameters
|
||||
{
|
||||
PacketDelayMS = 0,
|
||||
@@ -333,6 +384,8 @@ namespace Unity.Netcode.Transports.UTP
|
||||
PacketDropRate = 0
|
||||
};
|
||||
|
||||
internal uint? DebugSimulatorRandomSeed { get; set; } = null;
|
||||
|
||||
private struct PacketLossCache
|
||||
{
|
||||
public int PacketsReceived;
|
||||
@@ -340,6 +393,10 @@ namespace Unity.Netcode.Transports.UTP
|
||||
public float PacketLoss;
|
||||
};
|
||||
|
||||
internal static event Action<int, NetworkDriver> TransportInitialized;
|
||||
internal static event Action<int> TransportDisposed;
|
||||
internal NetworkDriver NetworkDriver => m_Driver;
|
||||
|
||||
private PacketLossCache m_PacketLossCache = new PacketLossCache();
|
||||
|
||||
private State m_State = State.Disconnected;
|
||||
@@ -383,6 +440,8 @@ namespace Unity.Netcode.Transports.UTP
|
||||
out m_UnreliableFragmentedPipeline,
|
||||
out m_UnreliableSequencedFragmentedPipeline,
|
||||
out m_ReliableSequencedPipeline);
|
||||
|
||||
TransportInitialized?.Invoke(GetInstanceID(), NetworkDriver);
|
||||
}
|
||||
|
||||
private void DisposeInternals()
|
||||
@@ -400,6 +459,8 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
m_SendQueue.Clear();
|
||||
|
||||
TransportDisposed?.Invoke(GetInstanceID());
|
||||
}
|
||||
|
||||
private NetworkPipeline SelectSendPipeline(NetworkDelivery delivery)
|
||||
@@ -425,7 +486,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
|
||||
private bool ClientBindAndConnect()
|
||||
{
|
||||
var serverEndpoint = default(NetworkEndPoint);
|
||||
var serverEndpoint = default(NetworkEndpoint);
|
||||
|
||||
if (m_ProtocolType == ProtocolType.RelayUnityTransport)
|
||||
{
|
||||
@@ -438,6 +499,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
m_NetworkSettings.WithRelayParameters(ref m_RelayServerData, m_HeartbeatTimeoutMS);
|
||||
serverEndpoint = m_RelayServerData.Endpoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -446,7 +508,8 @@ namespace Unity.Netcode.Transports.UTP
|
||||
|
||||
InitDriver();
|
||||
|
||||
int result = m_Driver.Bind(NetworkEndPoint.AnyIpv4);
|
||||
var bindEndpoint = serverEndpoint.Family == NetworkFamily.Ipv6 ? NetworkEndpoint.AnyIpv6 : NetworkEndpoint.AnyIpv4;
|
||||
int result = m_Driver.Bind(bindEndpoint);
|
||||
if (result != 0)
|
||||
{
|
||||
Debug.LogError("Client failed to bind");
|
||||
@@ -459,7 +522,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ServerBindAndListen(NetworkEndPoint endPoint)
|
||||
private bool ServerBindAndListen(NetworkEndpoint endPoint)
|
||||
{
|
||||
InitDriver();
|
||||
|
||||
@@ -481,51 +544,13 @@ namespace Unity.Netcode.Transports.UTP
|
||||
return true;
|
||||
}
|
||||
|
||||
private static RelayAllocationId ConvertFromAllocationIdBytes(byte[] allocationIdBytes)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = allocationIdBytes)
|
||||
{
|
||||
return RelayAllocationId.FromBytePointer(ptr, allocationIdBytes.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RelayHMACKey ConvertFromHMAC(byte[] hmac)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = hmac)
|
||||
{
|
||||
return RelayHMACKey.FromBytePointer(ptr, RelayHMACKey.k_Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RelayConnectionData ConvertConnectionData(byte[] connectionData)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = connectionData)
|
||||
{
|
||||
return RelayConnectionData.FromBytePointer(ptr, RelayConnectionData.k_Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetMaxPayloadSize(int maxPayloadSize)
|
||||
{
|
||||
m_MaxPayloadSize = maxPayloadSize;
|
||||
}
|
||||
|
||||
private void SetProtocol(ProtocolType inProtocol)
|
||||
{
|
||||
m_ProtocolType = inProtocol;
|
||||
}
|
||||
|
||||
/// <summary>Set the relay server data for the server.</summary>
|
||||
/// <param name="ipv4Address">IP address of the relay server.</param>
|
||||
/// <param name="ipv4Address">IP address or hostname of the relay server.</param>
|
||||
/// <param name="port">UDP port of the relay server.</param>
|
||||
/// <param name="allocationIdBytes">Allocation ID as a byte array.</param>
|
||||
/// <param name="keyBytes">Allocation key as a byte array.</param>
|
||||
@@ -534,39 +559,21 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <param name="isSecure">Whether the connection is secure (uses DTLS).</param>
|
||||
public void SetRelayServerData(string ipv4Address, ushort port, byte[] allocationIdBytes, byte[] keyBytes, byte[] connectionDataBytes, byte[] hostConnectionDataBytes = null, bool isSecure = false)
|
||||
{
|
||||
RelayConnectionData hostConnectionData;
|
||||
|
||||
if (!NetworkEndPoint.TryParse(ipv4Address, port, out var serverEndpoint))
|
||||
{
|
||||
Debug.LogError($"Invalid address {ipv4Address}:{port}");
|
||||
|
||||
// We set this to default to cause other checks to fail to state you need to call this
|
||||
// function again.
|
||||
m_RelayServerData = default;
|
||||
return;
|
||||
}
|
||||
|
||||
var allocationId = ConvertFromAllocationIdBytes(allocationIdBytes);
|
||||
var key = ConvertFromHMAC(keyBytes);
|
||||
var connectionData = ConvertConnectionData(connectionDataBytes);
|
||||
|
||||
if (hostConnectionDataBytes != null)
|
||||
{
|
||||
hostConnectionData = ConvertConnectionData(hostConnectionDataBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
hostConnectionData = connectionData;
|
||||
}
|
||||
|
||||
m_RelayServerData = new RelayServerData(ref serverEndpoint, 0, ref allocationId, ref connectionData, ref hostConnectionData, ref key, isSecure);
|
||||
m_RelayServerData.ComputeNewNonce();
|
||||
var hostConnectionData = hostConnectionDataBytes ?? connectionDataBytes;
|
||||
m_RelayServerData = new RelayServerData(ipv4Address, port, allocationIdBytes, connectionDataBytes, hostConnectionData, keyBytes, isSecure);
|
||||
SetProtocol(ProtocolType.RelayUnityTransport);
|
||||
}
|
||||
|
||||
/// <summary>Set the relay server data (using the lower-level Unity Transport data structure).</summary>
|
||||
/// <param name="serverData">Data for the Relay server to use.</param>
|
||||
public void SetRelayServerData(RelayServerData serverData)
|
||||
{
|
||||
m_RelayServerData = serverData;
|
||||
SetProtocol(ProtocolType.RelayUnityTransport);
|
||||
}
|
||||
|
||||
/// <summary>Set the relay server data for the host.</summary>
|
||||
/// <param name="ipAddress">IP address of the relay server.</param>
|
||||
/// <param name="ipAddress">IP address or hostname of the relay server.</param>
|
||||
/// <param name="port">UDP port of the relay server.</param>
|
||||
/// <param name="allocationId">Allocation ID as a byte array.</param>
|
||||
/// <param name="key">Allocation key as a byte array.</param>
|
||||
@@ -578,7 +585,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
/// <summary>Set the relay server data for the host.</summary>
|
||||
/// <param name="ipAddress">IP address of the relay server.</param>
|
||||
/// <param name="ipAddress">IP address or hostname of the relay server.</param>
|
||||
/// <param name="port">UDP port of the relay server.</param>
|
||||
/// <param name="allocationId">Allocation ID as a byte array.</param>
|
||||
/// <param name="key">Allocation key as a byte array.</param>
|
||||
@@ -593,7 +600,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <summary>
|
||||
/// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call <see cref="SetRelayServerData"/>
|
||||
/// </summary>
|
||||
/// <param name="ipv4Address">The remote IP address</param>
|
||||
/// <param name="ipv4Address">The remote IP address (despite the name, can be an IPv6 address)</param>
|
||||
/// <param name="port">The remote port</param>
|
||||
/// <param name="listenAddress">The local listen address</param>
|
||||
public void SetConnectionData(string ipv4Address, ushort port, string listenAddress = null)
|
||||
@@ -613,7 +620,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// </summary>
|
||||
/// <param name="endPoint">The remote end point</param>
|
||||
/// <param name="listenEndPoint">The local listen endpoint</param>
|
||||
public void SetConnectionData(NetworkEndPoint endPoint, NetworkEndPoint listenEndPoint = default)
|
||||
public void SetConnectionData(NetworkEndpoint endPoint, NetworkEndpoint listenEndPoint = default)
|
||||
{
|
||||
string serverAddress = endPoint.Address.Split(':')[0];
|
||||
|
||||
@@ -634,6 +641,9 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <param name="packetDelay">Packet delay in milliseconds.</param>
|
||||
/// <param name="packetJitter">Packet jitter in milliseconds.</param>
|
||||
/// <param name="dropRate">Packet drop percentage.</param>
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
[Obsolete("SetDebugSimulatorParameters is no longer supported and has no effect. Use Network Simulator from the Multiplayer Tools package.", false)]
|
||||
#endif
|
||||
public void SetDebugSimulatorParameters(int packetDelay, int packetJitter, int dropRate)
|
||||
{
|
||||
if (m_Driver.IsCreated)
|
||||
@@ -662,7 +672,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
else
|
||||
{
|
||||
m_NetworkSettings.WithRelayParameters(ref m_RelayServerData, m_HeartbeatTimeoutMS);
|
||||
return ServerBindAndListen(NetworkEndPoint.AnyIpv4);
|
||||
return ServerBindAndListen(NetworkEndpoint.AnyIpv4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -891,7 +901,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
|
||||
private void ExtractNetworkMetricsForClient(ulong transportClientId)
|
||||
{
|
||||
var networkConnection = ParseClientId(transportClientId);
|
||||
var networkConnection = ParseClientId(transportClientId);
|
||||
ExtractNetworkMetricsFromPipeline(m_UnreliableFragmentedPipeline, networkConnection);
|
||||
ExtractNetworkMetricsFromPipeline(m_UnreliableSequencedFragmentedPipeline, networkConnection);
|
||||
ExtractNetworkMetricsFromPipeline(m_ReliableSequencedPipeline, networkConnection);
|
||||
@@ -907,7 +917,11 @@ namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
//Don't need to dispose of the buffers, they are filled with data pointers.
|
||||
m_Driver.GetPipelineBuffers(pipeline,
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
NetworkPipelineStageId.Get<NetworkMetricsPipelineStage>(),
|
||||
#else
|
||||
NetworkPipelineStageCollection.GetStageId(typeof(NetworkMetricsPipelineStage)),
|
||||
#endif
|
||||
networkConnection,
|
||||
out _,
|
||||
out _,
|
||||
@@ -934,7 +948,11 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
m_Driver.GetPipelineBuffers(m_ReliableSequencedPipeline,
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
NetworkPipelineStageId.Get<ReliableSequencedPipelineStage>(),
|
||||
#else
|
||||
NetworkPipelineStageCollection.GetStageId(typeof(ReliableSequencedPipelineStage)),
|
||||
#endif
|
||||
networkConnection,
|
||||
out _,
|
||||
out _,
|
||||
@@ -956,7 +974,11 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
m_Driver.GetPipelineBuffers(m_ReliableSequencedPipeline,
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
NetworkPipelineStageId.Get<ReliableSequencedPipelineStage>(),
|
||||
#else
|
||||
NetworkPipelineStageCollection.GetStageId(typeof(ReliableSequencedPipelineStage)),
|
||||
#endif
|
||||
networkConnection,
|
||||
out _,
|
||||
out _,
|
||||
@@ -1117,11 +1139,12 @@ namespace Unity.Netcode.Transports.UTP
|
||||
// account for the overhead of its length when we store it in the send queue.
|
||||
var fragmentationCapacity = m_MaxPayloadSize + BatchedSendQueue.PerMessageOverhead;
|
||||
|
||||
m_NetworkSettings
|
||||
.WithFragmentationStageParameters(payloadCapacity: fragmentationCapacity)
|
||||
.WithBaselibNetworkInterfaceParameters(
|
||||
receiveQueueCapacity: m_MaxPacketQueueSize,
|
||||
sendQueueCapacity: m_MaxPacketQueueSize);
|
||||
m_NetworkSettings.WithFragmentationStageParameters(payloadCapacity: fragmentationCapacity);
|
||||
#if !UTP_TRANSPORT_2_0_ABOVE
|
||||
m_NetworkSettings.WithBaselibNetworkInterfaceParameters(
|
||||
receiveQueueCapacity: m_MaxPacketQueueSize,
|
||||
sendQueueCapacity: m_MaxPacketQueueSize);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1159,7 +1182,23 @@ namespace Unity.Netcode.Transports.UTP
|
||||
var sendTarget = new SendTarget(clientId, pipeline);
|
||||
if (!m_SendQueue.TryGetValue(sendTarget, out var queue))
|
||||
{
|
||||
queue = new BatchedSendQueue(Math.Max(m_MaxSendQueueSize, m_MaxPayloadSize));
|
||||
// The maximum size of a send queue is determined according to the disconnection
|
||||
// timeout. The idea being that if the send queue contains enough reliable data that
|
||||
// sending it all out would take longer than the disconnection timeout, then there's
|
||||
// no point storing even more in the queue (it would be like having a ping higher
|
||||
// than the disconnection timeout, which is far into the realm of unplayability).
|
||||
//
|
||||
// The throughput used to determine what consists the maximum send queue size is
|
||||
// the maximum theoritical throughput of the reliable pipeline assuming we only send
|
||||
// on each update at 60 FPS, which turns out to be around 2.688 MB/s.
|
||||
//
|
||||
// Note that we only care about reliable throughput for send queues because that's
|
||||
// the only case where a full send queue causes a connection loss. Full unreliable
|
||||
// send queues are dealt with by flushing it out to the network or simply dropping
|
||||
// new messages if that fails.
|
||||
var maxCapacity = m_MaxSendQueueSize > 0 ? m_MaxSendQueueSize : m_DisconnectTimeoutMS * k_MaxReliableThroughput;
|
||||
|
||||
queue = new BatchedSendQueue(Math.Max(maxCapacity, m_MaxPayloadSize));
|
||||
m_SendQueue.Add(sendTarget, queue);
|
||||
}
|
||||
|
||||
@@ -1173,8 +1212,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
|
||||
var ngoClientId = NetworkManager?.TransportIdToClientId(clientId) ?? clientId;
|
||||
Debug.LogError($"Couldn't add payload of size {payload.Count} to reliable send queue. " +
|
||||
$"Closing connection {ngoClientId} as reliability guarantees can't be maintained. " +
|
||||
$"Perhaps 'Max Send Queue Size' ({m_MaxSendQueueSize}) is too small for workload.");
|
||||
$"Closing connection {ngoClientId} as reliability guarantees can't be maintained.");
|
||||
|
||||
if (clientId == m_ServerClientId)
|
||||
{
|
||||
@@ -1223,9 +1261,9 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
|
||||
var succeeded = ClientBindAndConnect();
|
||||
if (!succeeded)
|
||||
if (!succeeded && m_Driver.IsCreated)
|
||||
{
|
||||
Shutdown();
|
||||
m_Driver.Dispose();
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
@@ -1250,16 +1288,16 @@ namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
case ProtocolType.UnityTransport:
|
||||
succeeded = ServerBindAndListen(ConnectionData.ListenEndPoint);
|
||||
if (!succeeded)
|
||||
if (!succeeded && m_Driver.IsCreated)
|
||||
{
|
||||
Shutdown();
|
||||
m_Driver.Dispose();
|
||||
}
|
||||
return succeeded;
|
||||
case ProtocolType.RelayUnityTransport:
|
||||
succeeded = StartRelayServer();
|
||||
if (!succeeded)
|
||||
if (!succeeded && m_Driver.IsCreated)
|
||||
{
|
||||
Shutdown();
|
||||
m_Driver.Dispose();
|
||||
}
|
||||
return succeeded;
|
||||
default:
|
||||
@@ -1298,16 +1336,65 @@ namespace Unity.Netcode.Transports.UTP
|
||||
m_ServerClientId = 0;
|
||||
}
|
||||
|
||||
private void ConfigureSimulator()
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
private void ConfigureSimulatorForUtp2()
|
||||
{
|
||||
// As DebugSimulator is deprecated, the 'packetDelayMs', 'packetJitterMs' and 'packetDropPercentage'
|
||||
// parameters are set to the default and are supposed to be changed using Network Simulator tool instead.
|
||||
m_NetworkSettings.WithSimulatorStageParameters(
|
||||
maxPacketCount: 300, // TODO Is there any way to compute a better value?
|
||||
maxPacketSize: NetworkParameterConstants.MTU,
|
||||
packetDelayMs: 0,
|
||||
packetJitterMs: 0,
|
||||
packetDropPercentage: 0,
|
||||
randomSeed: DebugSimulatorRandomSeed ?? (uint)System.Diagnostics.Stopwatch.GetTimestamp()
|
||||
, mode: ApplyMode.AllPackets
|
||||
);
|
||||
|
||||
m_NetworkSettings.WithNetworkSimulatorParameters();
|
||||
}
|
||||
#else
|
||||
private void ConfigureSimulatorForUtp1()
|
||||
{
|
||||
m_NetworkSettings.WithSimulatorStageParameters(
|
||||
maxPacketCount: 300, // TODO Is there any way to compute a better value?
|
||||
maxPacketSize: NetworkParameterConstants.MTU,
|
||||
packetDelayMs: DebugSimulator.PacketDelayMS,
|
||||
packetJitterMs: DebugSimulator.PacketJitterMS,
|
||||
packetDropPercentage: DebugSimulator.PacketDropRate
|
||||
packetDropPercentage: DebugSimulator.PacketDropRate,
|
||||
randomSeed: DebugSimulatorRandomSeed ?? (uint)System.Diagnostics.Stopwatch.GetTimestamp()
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
private string m_ServerPrivateKey;
|
||||
private string m_ServerCertificate;
|
||||
|
||||
private string m_ServerCommonName;
|
||||
private string m_ClientCaCertificate;
|
||||
|
||||
/// <summary>Set the server parameters for encryption.</summary>
|
||||
/// <param name="serverCertificate">Public certificate for the server (PEM format).</param>
|
||||
/// <param name="serverPrivateKey">Private key for the server (PEM format).</param>
|
||||
public void SetServerSecrets(string serverCertificate, string serverPrivateKey)
|
||||
{
|
||||
m_ServerPrivateKey = serverPrivateKey;
|
||||
m_ServerCertificate = serverCertificate;
|
||||
}
|
||||
|
||||
/// <summary>Set the client parameters for encryption.</summary>
|
||||
/// <remarks>
|
||||
/// If the CA certificate is not provided, validation will be done against the OS/browser
|
||||
/// certificate store. This is what you'd want if using certificates from a known provider.
|
||||
/// For self-signed certificates, the CA certificate needs to be provided.
|
||||
/// </remarks>
|
||||
/// <param name="serverCommonName">Common name of the server (typically hostname).</param>
|
||||
/// <param name="caCertificate">CA certificate used to validate the server's authenticity.</param>
|
||||
public void SetClientSecrets(string serverCommonName, string caCertificate = null)
|
||||
{
|
||||
m_ServerCommonName = serverCommonName;
|
||||
m_ClientCaCertificate = caCertificate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the internal NetworkDriver
|
||||
@@ -1322,22 +1409,124 @@ namespace Unity.Netcode.Transports.UTP
|
||||
out NetworkPipeline unreliableSequencedFragmentedPipeline,
|
||||
out NetworkPipeline reliableSequencedPipeline)
|
||||
{
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7 && !UTP_TRANSPORT_2_0_ABOVE
|
||||
NetworkPipelineStageCollection.RegisterPipelineStage(new NetworkMetricsPipelineStage());
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
ConfigureSimulator();
|
||||
#if UTP_TRANSPORT_2_0_ABOVE && UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED
|
||||
ConfigureSimulatorForUtp2();
|
||||
#elif !UTP_TRANSPORT_2_0_ABOVE && (UNITY_EDITOR || DEVELOPMENT_BUILD)
|
||||
ConfigureSimulatorForUtp1();
|
||||
#endif
|
||||
|
||||
m_NetworkSettings.WithNetworkConfigParameters(
|
||||
maxConnectAttempts: transport.m_MaxConnectAttempts,
|
||||
connectTimeoutMS: transport.m_ConnectTimeoutMS,
|
||||
disconnectTimeoutMS: transport.m_DisconnectTimeoutMS,
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
sendQueueCapacity: m_MaxPacketQueueSize,
|
||||
receiveQueueCapacity: m_MaxPacketQueueSize,
|
||||
#endif
|
||||
heartbeatTimeoutMS: transport.m_HeartbeatTimeoutMS);
|
||||
|
||||
driver = NetworkDriver.Create(m_NetworkSettings);
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
if (NetworkManager.IsServer)
|
||||
{
|
||||
throw new Exception("WebGL as a server is not supported by Unity Transport, outside the Editor.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
if (m_UseEncryption)
|
||||
{
|
||||
if (m_ProtocolType == ProtocolType.RelayUnityTransport)
|
||||
{
|
||||
if (m_RelayServerData.IsSecure != 0)
|
||||
{
|
||||
// log an error because we have mismatched configuration
|
||||
Debug.LogError("Mismatched security configuration, between Relay and local NetworkManager settings");
|
||||
}
|
||||
|
||||
// No need to to anything else if using Relay because UTP will handle the
|
||||
// configuration of the security parameters on its own.
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (NetworkManager.IsServer)
|
||||
{
|
||||
if (m_ServerCertificate.Length == 0 || m_ServerPrivateKey.Length == 0)
|
||||
{
|
||||
throw new Exception("In order to use encrypted communications, when hosting, you must set the server certificate and key.");
|
||||
}
|
||||
m_NetworkSettings.WithSecureServerParameters(m_ServerCertificate, m_ServerPrivateKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_ServerCommonName.Length == 0)
|
||||
{
|
||||
throw new Exception("In order to use encrypted communications, clients must set the server common name.");
|
||||
}
|
||||
else if (m_ClientCaCertificate == null)
|
||||
{
|
||||
m_NetworkSettings.WithSecureClientParameters(m_ServerCommonName);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NetworkSettings.WithSecureClientParameters(m_ClientCaCertificate, m_ServerCommonName);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Debug.LogException(e, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UTP_TRANSPORT_2_0_ABOVE
|
||||
if (m_UseWebSockets)
|
||||
{
|
||||
driver = NetworkDriver.Create(new WebSocketNetworkInterface(), m_NetworkSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if UNITY_WEBGL
|
||||
Debug.LogWarning($"WebSockets were used even though they're not selected in NetworkManager. You should check {nameof(UseWebSockets)}', on the Unity Transport component, to silence this warning.");
|
||||
driver = NetworkDriver.Create(new WebSocketNetworkInterface(), m_NetworkSettings);
|
||||
#else
|
||||
driver = NetworkDriver.Create(new UDPNetworkInterface(), m_NetworkSettings);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
driver = NetworkDriver.Create(m_NetworkSettings);
|
||||
#endif
|
||||
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7 && UTP_TRANSPORT_2_0_ABOVE
|
||||
driver.RegisterPipelineStage(new NetworkMetricsPipelineStage());
|
||||
#endif
|
||||
|
||||
#if !UTP_TRANSPORT_2_0_ABOVE
|
||||
SetupPipelinesForUtp1(driver,
|
||||
out unreliableFragmentedPipeline,
|
||||
out unreliableSequencedFragmentedPipeline,
|
||||
out reliableSequencedPipeline);
|
||||
#else
|
||||
SetupPipelinesForUtp2(driver,
|
||||
out unreliableFragmentedPipeline,
|
||||
out unreliableSequencedFragmentedPipeline,
|
||||
out reliableSequencedPipeline);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UTP_TRANSPORT_2_0_ABOVE
|
||||
private void SetupPipelinesForUtp1(NetworkDriver driver,
|
||||
out NetworkPipeline unreliableFragmentedPipeline,
|
||||
out NetworkPipeline unreliableSequencedFragmentedPipeline,
|
||||
out NetworkPipeline reliableSequencedPipeline)
|
||||
{
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
if (DebugSimulator.PacketDelayMS > 0 || DebugSimulator.PacketDropRate > 0)
|
||||
{
|
||||
@@ -1355,7 +1544,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
typeof(SimulatorPipelineStage),
|
||||
typeof(SimulatorPipelineStageInSend)
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
,typeof(NetworkMetricsPipelineStage)
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
reliableSequencedPipeline = driver.CreatePipeline(
|
||||
@@ -1363,7 +1552,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
typeof(SimulatorPipelineStage),
|
||||
typeof(SimulatorPipelineStageInSend)
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
,typeof(NetworkMetricsPipelineStage)
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
@@ -1373,25 +1562,63 @@ namespace Unity.Netcode.Transports.UTP
|
||||
unreliableFragmentedPipeline = driver.CreatePipeline(
|
||||
typeof(FragmentationPipelineStage)
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
,typeof(NetworkMetricsPipelineStage)
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
unreliableSequencedFragmentedPipeline = driver.CreatePipeline(
|
||||
typeof(FragmentationPipelineStage),
|
||||
typeof(UnreliableSequencedPipelineStage)
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
,typeof(NetworkMetricsPipelineStage)
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
reliableSequencedPipeline = driver.CreatePipeline(
|
||||
typeof(ReliableSequencedPipelineStage)
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
,typeof(NetworkMetricsPipelineStage)
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
#else
|
||||
private void SetupPipelinesForUtp2(NetworkDriver driver,
|
||||
out NetworkPipeline unreliableFragmentedPipeline,
|
||||
out NetworkPipeline unreliableSequencedFragmentedPipeline,
|
||||
out NetworkPipeline reliableSequencedPipeline)
|
||||
{
|
||||
|
||||
unreliableFragmentedPipeline = driver.CreatePipeline(
|
||||
typeof(FragmentationPipelineStage)
|
||||
#if UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED
|
||||
, typeof(SimulatorPipelineStage)
|
||||
#endif
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
|
||||
unreliableSequencedFragmentedPipeline = driver.CreatePipeline(
|
||||
typeof(FragmentationPipelineStage),
|
||||
typeof(UnreliableSequencedPipelineStage)
|
||||
#if UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED
|
||||
, typeof(SimulatorPipelineStage)
|
||||
#endif
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
|
||||
reliableSequencedPipeline = driver.CreatePipeline(
|
||||
typeof(ReliableSequencedPipelineStage)
|
||||
#if UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED
|
||||
, typeof(SimulatorPipelineStage)
|
||||
#endif
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
, typeof(NetworkMetricsPipelineStage)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif
|
||||
// -------------- Utility Types -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user