diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 2c5d220a40..2eb3776c3b 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -10,6 +10,8 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added +- It is now possible to control which port clients will bind to using the `UnityTransport.ConnectionData.ClientBindPort` field. If not set, clients will bind to an ephemeral port (same as before this change). (#3764) + ### Changed diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index 5b71fcadbe..2f3c987ab2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -245,6 +245,12 @@ internal static NetworkEndpoint ParseNetworkEndpoint(string ip, ushort port) return endpoint; } + /// + /// The port the client will bind to. If 0 (the default), an ephemeral port will be used. + /// + [SerializeField] + public ushort ClientBindPort; + /// /// Endpoint (IP address and port) clients will connect to. /// @@ -683,6 +689,20 @@ private bool ClientBindAndConnect() } InitDriver(); + + // Don't bind yet if connecting to a hostname, since we don't know if it will resolve to IPv4 or IPv6. + if (serverEndpoint.Family != NetworkFamily.Invalid && ConnectionData.ClientBindPort != 0) + { + var bindEndpoint = serverEndpoint.Family == NetworkFamily.Ipv6 + ? NetworkEndpoint.AnyIpv6.WithPort(ConnectionData.ClientBindPort) + : NetworkEndpoint.AnyIpv4.WithPort(ConnectionData.ClientBindPort); + if (m_Driver.Bind(bindEndpoint) != 0) + { + Debug.LogError($"Couldn't create socket. Possibly another process is using port {ConnectionData.ClientBindPort}."); + return false; + } + } + Connect(serverEndpoint); return true; @@ -788,16 +808,17 @@ public void SetClientRelayData(string ipAddress, ushort port, byte[] allocationI /// /// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call /// - /// The remote IP address (despite the name, can be an IPv6 address or a domain name) - /// The remote port - /// The local listen address + /// The remote IP address (despite the name, can be an IPv6 address or a domain name). + /// The remote port to connect to. + /// The address the server is going to listen on. public void SetConnectionData(string ipv4Address, ushort port, string listenAddress = null) { ConnectionData = new ConnectionAddressData { Address = ipv4Address, Port = port, - ServerListenAddress = listenAddress ?? ipv4Address + ServerListenAddress = listenAddress ?? ipv4Address, + ClientBindPort = ConnectionData.ClientBindPort }; SetProtocol(ProtocolType.UnityTransport); @@ -806,8 +827,8 @@ public void SetConnectionData(string ipv4Address, ushort port, string listenAddr /// /// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call /// - /// The remote end point - /// The local listen endpoint + /// The remote endpoint the client should connect to. + /// The endpoint the server should listen on. public void SetConnectionData(NetworkEndpoint endPoint, NetworkEndpoint listenEndPoint = default) { string serverAddress = endPoint.Address.Split(':')[0]; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index 7afd0b879d..594467efc2 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -184,6 +184,20 @@ public void UnityTransport_EmptySecurityStringsShouldThrow([Values("", null)] st } } + [Test] + public void UnityTransport_BindClientToSpecificPort() + { + UnityTransport transport = new GameObject().AddComponent(); + transport.Initialize(); + transport.SetConnectionData("127.0.0.1", 4242); + transport.ConnectionData.ClientBindPort = 14242; + + Assert.True(transport.StartClient()); + Assert.AreEqual(14242, transport.GetLocalEndpoint().Port); + + transport.Shutdown(); + } + #if HOSTNAME_RESOLUTION_AVAILABLE private static readonly (string, bool)[] k_HostnameChecks = { diff --git a/com.unity.netcode.gameobjects/package.json b/com.unity.netcode.gameobjects/package.json index 1942f6f429..c0d0083726 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.7.1", + "version": "2.8.0", "unity": "6000.0", "dependencies": { "com.unity.nuget.mono-cecil": "1.11.4",