Implement NetworkedMultiplayerENet.get_local_port

Allows retrieving the local port to which the peer is bound.
This commit is contained in:
Fabio Alessandrelli 2021-03-29 18:14:50 +02:00
parent 288f484d0a
commit cd22a2be2f
4 changed files with 95 additions and 48 deletions

View File

@ -33,10 +33,10 @@
</argument>
<argument index="3" name="out_bandwidth" type="int" default="0">
</argument>
<argument index="4" name="client_port" type="int" default="0">
<argument index="4" name="local_port" type="int" default="0">
</argument>
<description>
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.
</description>
</method>
<method name="create_server">
@ -72,6 +72,13 @@
Returns the channel of the last packet fetched via [method PacketPeer.get_packet].
</description>
</method>
<method name="get_local_port" qualifiers="const">
<return type="int">
</return>
<description>
Returns the local port to which this peer is bound.
</description>
</method>
<method name="get_packet_channel" qualifiers="const">
<return type="int">
</return>

View File

@ -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);

View File

@ -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);

View File

@ -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<NetSocket> 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<X509Certificate> cert;
IP_Address local_address;
public:
ENetDTLSClient(ENetUDP *p_base, Ref<X509Certificate> p_cert, bool p_verify, String p_for_hostname) {
@ -161,9 +166,11 @@ public:
cert = p_cert;
udp.instance();
dtls = Ref<PacketPeerDTLS>(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<UDPServer> udp_server;
Map<String, Ref<PacketPeerDTLS>> peers;
int last_service = 0;
IP_Address local_address;
public:
ENetDTLSServer(ENetUDP *p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> 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>(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;
}