diff --git a/com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs b/com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs index 742fee37df..381327941c 100644 --- a/com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs +++ b/com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs @@ -19,6 +19,49 @@ public interface INetworkStreamDriverConstructor void CreateDriver(UnityTransport transport, out NetworkDriver driver, out NetworkPipeline unreliableSequencedPipeline, out NetworkPipeline reliableSequencedPipeline, out NetworkPipeline reliableSequencedFragmentedPipeline); } + public static class ErrorUtilities + { + private const string k_NetworkSuccess = "Success"; + private const string k_NetworkIdMismatch = "NetworkId is invalid, likely caused by stale connection {0}."; + private const string k_NetworkVersionMismatch = "NetworkVersion is invalid, likely caused by stale connection {0}."; + private const string k_NetworkStateMismatch = "Sending data while connecting on connectionId{0} is now allowed"; + private const string k_NetworkPacketOverflow = "Unable to allocate packet due to buffer overflow."; + private const string k_NetworkSendQueueFull = "Currently unable to queue packet as there is too many inflight packets."; + private const string k_NetworkHeaderInvalid = "Invalid Unity Transport Protocol header."; + private const string k_NetworkDriverParallelForErr = "The parallel network driver needs to process a single unique connection per job, processing a single connection multiple times in a parallel for is not supported."; + private const string k_NetworkSendHandleInvalid = "Invalid NetworkInterface Send Handle. Likely caused by pipeline send data corruption."; + private const string k_NetworkArgumentMismatch = "Invalid NetworkEndpoint Arguments."; + + public static string ErrorToString(Networking.Transport.Error.StatusCode error, ulong connectionId) + { + switch (error) + { + case Networking.Transport.Error.StatusCode.Success: + return k_NetworkSuccess; + case Networking.Transport.Error.StatusCode.NetworkIdMismatch: + return string.Format(k_NetworkIdMismatch, connectionId); + case Networking.Transport.Error.StatusCode.NetworkVersionMismatch: + return string.Format(k_NetworkVersionMismatch, connectionId); + case Networking.Transport.Error.StatusCode.NetworkStateMismatch: + return string.Format(k_NetworkStateMismatch, connectionId); + case Networking.Transport.Error.StatusCode.NetworkPacketOverflow: + return k_NetworkPacketOverflow; + case Networking.Transport.Error.StatusCode.NetworkSendQueueFull: + return k_NetworkSendQueueFull; + case Networking.Transport.Error.StatusCode.NetworkHeaderInvalid: + return k_NetworkHeaderInvalid; + case Networking.Transport.Error.StatusCode.NetworkDriverParallelForErr: + return k_NetworkDriverParallelForErr; + case Networking.Transport.Error.StatusCode.NetworkSendHandleInvalid: + return k_NetworkSendHandleInvalid; + case Networking.Transport.Error.StatusCode.NetworkArgumentMismatch: + return k_NetworkArgumentMismatch; + } + + return $"Unknown ErrorCode {Enum.GetName(typeof(Networking.Transport.Error.StatusCode), error)}"; + } + } + public class UnityTransport : NetworkTransport, INetworkStreamDriverConstructor { public enum ProtocolType @@ -34,23 +77,43 @@ private enum State Connected, } - public const int MaximumMessageLength = 6 * 1024; + public const int InitialBatchQueueSize = 6 * 1024; + public const int InitialMaxPacketSize = NetworkParameterConstants.MTU; + + private static ConnectionAddressData s_DefaultConnectionAddressData = new ConnectionAddressData() + { Address = "127.0.0.1", Port = 7777 }; #pragma warning disable IDE1006 // Naming Styles public static INetworkStreamDriverConstructor s_DriverConstructor; #pragma warning restore IDE1006 // Naming Styles public INetworkStreamDriverConstructor DriverConstructor => s_DriverConstructor != null ? s_DriverConstructor : this; + [Tooltip("Which protocol should be selected Relay/Non-Relay")] [SerializeField] private ProtocolType m_ProtocolType; - [SerializeField] private int m_MessageBufferSize = MaximumMessageLength; - [SerializeField] private int m_ReciveQueueSize = 128; - [SerializeField] private int m_SendQueueSize = 128; - [Tooltip("The maximum size of the send queue for batching Netcode events")] - [SerializeField] private int m_SendQueueBatchSize = 4096; + [Tooltip("Maximum size in bytes for a given packet")] + [SerializeField] private int m_MaximumPacketSize = InitialMaxPacketSize; + + [Tooltip("The maximum amount of packets that can be in the send/recv queues")] + [SerializeField] private int m_MaxPacketQueueSize = 128; + + [Tooltip("The maximum size in bytes of the send queue for batching Netcode events")] + [SerializeField] private int m_SendQueueBatchSize = InitialBatchQueueSize; - [SerializeField] private string m_ServerAddress = "127.0.0.1"; - [SerializeField] private ushort m_ServerPort = 7777; + [Serializable] + public struct ConnectionAddressData + { + [SerializeField] public string Address; + [SerializeField] public int Port; + + public static implicit operator NetworkEndPoint(ConnectionAddressData d) => + NetworkEndPoint.Parse(d.Address, (ushort)d.Port); + + public static implicit operator ConnectionAddressData(NetworkEndPoint d) => + new ConnectionAddressData() { Address = d.Address.Split(':')[0], Port = d.Port }; + } + + public ConnectionAddressData ConnectionData = s_DefaultConnectionAddressData; private State m_State = State.Disconnected; private NetworkDriver m_Driver; @@ -171,7 +234,7 @@ private bool ClientBindAndConnect() } else { - serverEndpoint = NetworkEndPoint.Parse(m_ServerAddress, m_ServerPort); + serverEndpoint = ConnectionData; } InitDriver(); @@ -273,8 +336,16 @@ public void SetRelayServerData(string ipv4Address, ushort port, byte[] allocatio /// public void SetConnectionData(string ipv4Address, ushort port) { - m_ServerAddress = ipv4Address; - m_ServerPort = port; + ConnectionData.Address = ipv4Address; + ConnectionData.Port = port; + } + + /// + /// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call + /// + public void SetConnectionData(NetworkEndPoint endPoint) + { + ConnectionData = endPoint; } private bool StartRelayServer() @@ -372,25 +443,23 @@ private bool ProcessEvent() private unsafe void ReadData(int size, ref DataStreamReader reader, ref NetworkConnection networkConnection) { - if (size > m_MessageBufferSize) + if (size > m_SendQueueBatchSize) { - Debug.LogError("The received message does not fit into the message buffer"); + Debug.LogError($"The received message does not fit into the message buffer: {size} {m_SendQueueBatchSize}"); } else { unsafe { - fixed (byte* buffer = &m_MessageBuffer[0]) - { - reader.ReadBytes(buffer, size); - } + using var data = new NativeArray(size, Allocator.Temp); + reader.ReadBytes(data); + + InvokeOnTransportEvent(NetcodeNetworkEvent.Data, + ParseClientId(networkConnection), + new ArraySegment(data.ToArray(), 0, size), + Time.realtimeSinceStartup + ); } - - InvokeOnTransportEvent(NetcodeNetworkEvent.Data, - ParseClientId(networkConnection), - new ArraySegment(m_MessageBuffer, 0, size), - Time.realtimeSinceStartup - ); } } @@ -438,7 +507,6 @@ public override void DisconnectLocalClient() m_State = State.Disconnected; - // If we successfully disconnect we dispatch a local disconnect message // this how uNET and other transports worked and so this is just keeping with the old behavior // should be also noted on the client this will call shutdown on the NetworkManager and the Transport @@ -491,23 +559,20 @@ public override void Initialize() { Debug.Assert(sizeof(ulong) == UnsafeUtility.SizeOf(), "Netcode connection id size does not match UTP connection id size"); - Debug.Assert(m_MessageBufferSize > 5, "Message buffer size must be greater than 5"); + Debug.Assert(m_MaximumPacketSize > 5, "Message buffer size must be greater than 5"); m_NetworkParameters = new List(); // If we want to be able to actually handle messages MaximumMessageLength bytes in // size, we need to allow a bit more than that in FragmentationUtility since this needs // to account for headers and such. 128 bytes is plenty enough for such overhead. - var maxFragmentationCapacity = MaximumMessageLength + 128; - m_NetworkParameters.Add(new FragmentationUtility.Parameters() { PayloadCapacity = maxFragmentationCapacity }); + m_NetworkParameters.Add(new FragmentationUtility.Parameters() { PayloadCapacity = m_SendQueueBatchSize }); m_NetworkParameters.Add(new BaselibNetworkParameter() { - maximumPayloadSize = (uint)m_MessageBufferSize, - receiveQueueCapacity = m_ReciveQueueSize, - sendQueueCapacity = m_SendQueueSize + maximumPayloadSize = (uint)m_MaximumPacketSize, + receiveQueueCapacity = m_MaxPacketQueueSize, + sendQueueCapacity = m_MaxPacketQueueSize }); - - m_MessageBuffer = new byte[m_MessageBufferSize]; } public override NetcodeNetworkEvent PollEvent(out ulong clientId, out ArraySegment payload, out float receiveTime) @@ -556,7 +621,6 @@ private unsafe void SendBatchedMessage(ulong clientId, ref NativeArray dat { var payloadSize = data.Length + 1; // One extra byte to mark whether this message is batched or not var result = m_Driver.BeginSend(pipeline, ParseClientId(clientId), out var writer, payloadSize); - if (result == 0) { if (data.IsCreated) @@ -573,13 +637,14 @@ private unsafe void SendBatchedMessage(ulong clientId, ref NativeArray dat } } - Debug.LogError($"Error sending the message {result}"); + Debug.LogError($"Error sending the message: {ErrorUtilities.ErrorToString((Networking.Transport.Error.StatusCode)result, clientId)}"); } private unsafe void SendMessageInstantly(ulong clientId, ArraySegment data, NetworkPipeline pipeline) { var payloadSize = data.Count + 1 + 4; // 1 byte to indicate if the message is batched and 4 for the payload size var result = m_Driver.BeginSend(pipeline, ParseClientId(clientId), out var writer, payloadSize); + if (result == 0) { if (data.Array != null) @@ -604,7 +669,7 @@ private unsafe void SendMessageInstantly(ulong clientId, ArraySegment data } } - Debug.LogError($"Error sending the message {result}"); + Debug.LogError($"Error sending the message: {ErrorUtilities.ErrorToString((Networking.Transport.Error.StatusCode)result, clientId)}"); } /// @@ -657,7 +722,7 @@ public override bool StartServer() switch (m_ProtocolType) { case ProtocolType.UnityTransport: - return ServerBindAndListen(NetworkEndPoint.Parse(m_ServerAddress, m_ServerPort)); + return ServerBindAndListen(ConnectionData); case ProtocolType.RelayUnityTransport: return StartRelayServer(); default: @@ -743,7 +808,6 @@ public void CreateDriver(UnityTransport transport, out NetworkDriver driver, out } } - // -------------- Utility Types ------------------------------------------------------------------------------- /// diff --git a/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/DriverClient.cs b/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/DriverClient.cs index 8f577cb61b..79d6c67afc 100644 --- a/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/DriverClient.cs +++ b/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/DriverClient.cs @@ -35,7 +35,7 @@ public class DriverClient : MonoBehaviour private void Awake() { - var maxCap = UnityTransport.MaximumMessageLength + 128; + var maxCap = UnityTransport.InitialBatchQueueSize + 128; var fragParams = new FragmentationUtility.Parameters() { PayloadCapacity = maxCap }; m_Driver = NetworkDriver.Create(fragParams);