diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index c8f32ffde6..f22ff29349 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -33,10 +33,10 @@ - + - Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this NetworkedMultiplayerENet instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques. + Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this NetworkedMultiplayerENet instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]local_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques. @@ -72,6 +72,13 @@ Returns the channel of the last packet fetched via [method PacketPeer.get_packet]. + + + + + Returns the local port to which this peer is bound. + + diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 1a00cdac9b..1cf77b307d 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -68,7 +68,7 @@ int NetworkedMultiplayerENet::get_last_packet_channel() const { Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) { ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); - ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The port number must be set between 0 and 65535 (inclusive)."); + ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive)."); ERR_FAIL_COND_V_MSG(p_max_clients < 1 || p_max_clients > 4095, ERR_INVALID_PARAMETER, "The number of clients must be set between 1 and 4095 (inclusive)."); ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); @@ -115,46 +115,37 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int connection_status = CONNECTION_CONNECTED; return OK; } - -Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth, int p_client_port) { +Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth, int p_local_port) { ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); - ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The server port number must be set between 0 and 65535 (inclusive)."); - ERR_FAIL_COND_V_MSG(p_client_port < 0 || p_client_port > 65535, ERR_INVALID_PARAMETER, "The client port number must be set between 0 and 65535 (inclusive)."); + ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive)."); + ERR_FAIL_COND_V_MSG(p_local_port < 0 || p_local_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive)."); ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); - if (p_client_port != 0) { - ENetAddress c_client; + ENetAddress c_client; #ifdef GODOT_ENET - if (bind_ip.is_wildcard()) { - c_client.wildcard = 1; - } else { - enet_address_set_ip(&c_client, bind_ip.get_ipv6(), 16); - } + if (bind_ip.is_wildcard()) { + c_client.wildcard = 1; + } else { + enet_address_set_ip(&c_client, bind_ip.get_ipv6(), 16); + } #else - if (bind_ip.is_wildcard()) { - c_client.host = 0; - } else { - ERR_FAIL_COND_V_MSG(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER, "Wildcard IP addresses are only permitted in IPv4, not IPv6."); - c_client.host = *(uint32_t *)bind_ip.get_ipv4(); - } + if (bind_ip.is_wildcard()) { + c_client.host = 0; + } else { + ERR_FAIL_COND_V_MSG(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER, "Wildcard IP addresses are only permitted in IPv4, not IPv6."); + c_client.host = *(uint32_t *)bind_ip.get_ipv4(); + } #endif - c_client.port = p_client_port; + c_client.port = p_local_port; - host = enet_host_create(&c_client /* create a client host */, - 1 /* only allow 1 outgoing connection */, - channel_count /* allow up to channel_count to be used */, - p_in_bandwidth /* limit incoming bandwidth if > 0 */, - p_out_bandwidth /* limit outgoing bandwidth if > 0 */); - } else { - host = enet_host_create(nullptr /* create a client host */, - 1 /* only allow 1 outgoing connection */, - channel_count /* allow up to channel_count to be used */, - p_in_bandwidth /* limit incoming bandwidth if > 0 */, - p_out_bandwidth /* limit outgoing bandwidth if > 0 */); - } + host = enet_host_create(&c_client /* create a client host */, + 1 /* only allow 1 outgoing connection */, + channel_count /* allow up to channel_count to be used */, + p_in_bandwidth /* limit incoming bandwidth if > 0 */, + p_out_bandwidth /* limit outgoing bandwidth if > 0 */); ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create the ENet client host."); #ifdef GODOT_ENET @@ -784,6 +775,11 @@ int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const { #endif } +int NetworkedMultiplayerENet::get_local_port() const { + ERR_FAIL_COND_V_MSG(!active || !host, 0, "The multiplayer instance isn't currently active."); + return host->address.port; +} + void NetworkedMultiplayerENet::set_peer_timeout(int p_peer_id, int p_timeout_limit, int p_timeout_min, int p_timeout_max) { ERR_FAIL_COND_MSG(!peer_map.has(p_peer_id), vformat("Peer ID %d not found in the list of peers.", p_peer_id)); ERR_FAIL_COND_MSG(!is_server() && p_peer_id != 1, "Can't change the timeout of peers other then the server when acting as a client."); @@ -832,7 +828,7 @@ bool NetworkedMultiplayerENet::is_server_relay_enabled() const { void NetworkedMultiplayerENet::_bind_methods() { ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("create_client", "address", "port", "in_bandwidth", "out_bandwidth", "client_port"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("create_client", "address", "port", "in_bandwidth", "out_bandwidth", "local_port"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &NetworkedMultiplayerENet::close_connection, DEFVAL(100)); ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "now"), &NetworkedMultiplayerENet::disconnect_peer, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &NetworkedMultiplayerENet::set_compression_mode); @@ -846,6 +842,7 @@ void NetworkedMultiplayerENet::_bind_methods() { ClassDB::bind_method(D_METHOD("is_dtls_verify_enabled"), &NetworkedMultiplayerENet::is_dtls_verify_enabled); ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &NetworkedMultiplayerENet::get_peer_address); ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &NetworkedMultiplayerENet::get_peer_port); + ClassDB::bind_method(D_METHOD("get_local_port"), &NetworkedMultiplayerENet::get_local_port); ClassDB::bind_method(D_METHOD("set_peer_timeout", "id", "timeout_limit", "timeout_min", "timeout_max"), &NetworkedMultiplayerENet::set_peer_timeout); ClassDB::bind_method(D_METHOD("get_packet_channel"), &NetworkedMultiplayerENet::get_packet_channel); diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index b99b14d218..c589cd9fbf 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -127,10 +127,11 @@ public: virtual IP_Address get_peer_address(int p_peer_id) const; virtual int get_peer_port(int p_peer_id) const; + virtual int get_local_port() const; void set_peer_timeout(int p_peer_id, int p_timeout_limit, int p_timeout_min, int p_timeout_max); Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0); - Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_client_port = 0); + Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_local_port = 0); void close_connection(uint32_t wait_usec = 100); diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp index 140f0093eb..189de6cc1f 100644 --- a/thirdparty/enet/godot.cpp +++ b/thirdparty/enet/godot.cpp @@ -46,6 +46,7 @@ class ENetGodotSocket { public: virtual Error bind(IP_Address p_ip, uint16_t p_port) = 0; + virtual Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) = 0; virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) = 0; virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) = 0; virtual int set_option(ENetSocketOption p_option, int p_value) = 0; @@ -64,8 +65,7 @@ class ENetUDP : public ENetGodotSocket { private: Ref sock; - IP_Address address; - uint16_t port = 0; + IP_Address local_address; bool bound = false; public: @@ -80,10 +80,13 @@ public: } Error bind(IP_Address p_ip, uint16_t p_port) { - address = p_ip; - port = p_port; + local_address = p_ip; bound = true; - return sock->bind(address, port); + return sock->bind(p_ip, p_port); + } + + Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) { + return sock->get_socket_address(r_ip, r_port); } Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) { @@ -142,6 +145,7 @@ public: void close() { sock->close(); + local_address.clear(); } }; @@ -153,6 +157,7 @@ class ENetDTLSClient : public ENetGodotSocket { bool verify = false; String for_hostname; Ref cert; + IP_Address local_address; public: ENetDTLSClient(ENetUDP *p_base, Ref p_cert, bool p_verify, String p_for_hostname) { @@ -161,9 +166,11 @@ public: cert = p_cert; udp.instance(); dtls = Ref(PacketPeerDTLS::create()); - p_base->close(); if (p_base->bound) { - bind(p_base->address, p_base->port); + uint16_t port; + p_base->get_socket_address(&local_address, &port); + p_base->close(); + bind(local_address, port); } } @@ -172,9 +179,19 @@ public: } Error bind(IP_Address p_ip, uint16_t p_port) { + local_address = p_ip; return udp->bind(p_port, p_ip); } + Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) { + if (!udp->is_bound()) { + return ERR_UNCONFIGURED; + } + *r_ip = local_address; + *r_port = udp->get_local_port(); + return OK; + } + Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) { if (!connected) { udp->connect_to_host(p_ip, p_port); @@ -233,13 +250,16 @@ class ENetDTLSServer : public ENetGodotSocket { Ref udp_server; Map> peers; int last_service = 0; + IP_Address local_address; public: ENetDTLSServer(ENetUDP *p_base, Ref p_key, Ref p_cert) { udp_server.instance(); - p_base->close(); if (p_base->bound) { - bind(p_base->address, p_base->port); + uint16_t port; + p_base->get_socket_address(&local_address, &port); + p_base->close(); + bind(local_address, port); } server = Ref(DTLSServer::create()); server->setup(p_key, p_cert); @@ -254,9 +274,19 @@ public: } Error bind(IP_Address p_ip, uint16_t p_port) { + local_address = p_ip; return udp_server->listen(p_port, p_ip); } + Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) { + if (!udp_server->is_listening()) { + return ERR_UNCONFIGURED; + } + *r_ip = local_address; + *r_port = udp_server->get_local_port(); + return OK; + } + Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) { String key = String(p_ip) + ":" + itos(p_port); ERR_FAIL_COND_V(!peers.has(key), ERR_UNAVAILABLE); @@ -341,6 +371,7 @@ public: peers.clear(); udp_server->stop(); server->stop(); + local_address.clear(); } }; @@ -493,15 +524,26 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf return read; } +int enet_socket_get_address (ENetSocket socket, ENetAddress * address) { + IP_Address ip; + uint16_t port; + ENetGodotSocket *sock = (ENetGodotSocket *)socket; + + if (sock->get_socket_address(&ip, &port) != OK) { + return -1; + } + + enet_address_set_ip(address, ip.get_ipv6(), 16); + address->port = port; + + return 0; +} + // Not implemented int enet_socket_wait(ENetSocket socket, enet_uint32 *condition, enet_uint32 timeout) { return 0; // do we need this function? } -int enet_socket_get_address(ENetSocket socket, ENetAddress *address) { - return -1; // do we need this function? -} - int enet_socketset_select(ENetSocket maxSocket, ENetSocketSet *readSet, ENetSocketSet *writeSet, enet_uint32 timeout) { return -1; }