diff options
Diffstat (limited to 'modules/enet')
-rw-r--r-- | modules/enet/config.py | 2 | ||||
-rw-r--r-- | modules/enet/doc_classes/NetworkedMultiplayerENet.xml | 76 | ||||
-rw-r--r-- | modules/enet/networked_multiplayer_enet.cpp | 323 | ||||
-rw-r--r-- | modules/enet/networked_multiplayer_enet.h | 22 |
4 files changed, 344 insertions, 79 deletions
diff --git a/modules/enet/config.py b/modules/enet/config.py index 8031fbb4b6..3e30bbe778 100644 --- a/modules/enet/config.py +++ b/modules/enet/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index 55a7a1ac77..fab4b05da9 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -1,12 +1,14 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1-dev"> +<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1"> <brief_description> PacketPeer implementation using the ENet library. </brief_description> <description> - A connection (or a listening server) that should be passed to [method SceneTree.set_network_peer]. Socket events can be handled by connecting to [SceneTree] signals. + A PacketPeer implementation that should be passed to [method SceneTree.set_network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals. </description> <tutorials> + <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link> + <link>http://enet.bespin.org/usergroup0.html</link> </tutorials> <demos> </demos> @@ -14,13 +16,16 @@ <method name="close_connection"> <return type="void"> </return> + <argument index="0" name="wait_usec" type="int" default="100"> + </argument> <description> + Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server. </description> </method> <method name="create_client"> <return type="int" enum="Error"> </return> - <argument index="0" name="ip" type="String"> + <argument index="0" name="address" type="String"> </argument> <argument index="1" name="port" type="int"> </argument> @@ -28,8 +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> <description> - Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code]. + 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 nome (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 [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] 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 in some NAT traveral technique. </description> </method> <method name="create_server"> @@ -44,7 +51,50 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> - Create server that listens to connections via [code]port[/code]. + Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]*[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [code]OK[/code] if a server was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the server could not be created. + </description> + </method> + <method name="disconnect_peer"> + <return type="void"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="now" type="bool" default="false"> + </argument> + <description> + Disconnect the given peer. If "now" is set to true, the connection will be closed immediately without flushing queued messages. + </description> + </method> + <method name="get_last_packet_channel" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the channel of the last packet fetched via [method PacketPeer.get_packet] + </description> + </method> + <method name="get_packet_channel" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the channel of the next packet that will be retrieved via [method PacketPeer.get_packet_peer] + </description> + </method> + <method name="get_peer_address" qualifiers="const"> + <return type="String"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <description> + Returns the IP address of the given peer. + </description> + </method> + <method name="get_peer_port" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <description> + Returns the remote port of the given peer. </description> </method> <method name="set_bind_ip"> @@ -53,23 +103,39 @@ <argument index="0" name="ip" type="String"> </argument> <description> + The IP used when creating a server. This is set to the wildcard [code]*[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code]. </description> </method> </methods> <members> + <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered"> + Always use [code]TRANSFER_MODE_ORDERED[/code] in place of [code]TRANSFER_MODE_UNRELIABLE[/code]. This is the only way to use ordering with the RPC system. + </member> + <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count"> + The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In realiable or ordered mode, for example, the packet delivery order is ensured on a per channel basis. + </member> <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode"> + The compression method used for network packets. Default is no compression. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. + </member> + <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel"> + Set the default channel to be used to transfer data. By default this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. </member> </members> <constants> <constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> + No compression. </constant> <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode"> + ENet's buildin range encoding. </constant> <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode"> + FastLZ compression. </constant> <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode"> + zlib compression. </constant> <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode"> + ZStandard compression. </constant> </constants> </class> diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index f3f4acd768..25b7f2472d 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "networked_multiplayer_enet.h" +#include "io/ip.h" #include "io/marshalls.h" #include "os/os.h" @@ -54,9 +55,29 @@ int NetworkedMultiplayerENet::get_packet_peer() const { return incoming_packets.front()->get().from; } +int NetworkedMultiplayerENet::get_packet_channel() const { + + ERR_FAIL_COND_V(!active, -1); + ERR_FAIL_COND_V(incoming_packets.size() == 0, -1); + + return incoming_packets.front()->get().channel; +} + +int NetworkedMultiplayerENet::get_last_packet_channel() const { + + ERR_FAIL_COND_V(!active, -1); + ERR_FAIL_COND_V(!current_packet.packet, -1); + + return current_packet.channel; +} + Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) { ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_max_clients < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER); ENetAddress address; @@ -78,9 +99,9 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int host = enet_host_create(&address /* the address to bind the server host to */, p_max_clients /* allow up to 32 clients and/or outgoing connections */, - SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, - p_in_bandwidth /* assume any amount of incoming bandwidth */, - p_out_bandwidth /* assume any amount of outgoing bandwidth */); + channel_count /* allow up to channel_count to be used */, + p_in_bandwidth /* limit incoming bandwith if > 0 */, + p_out_bandwidth /* limit outgoing bandwith if > 0 */); ERR_FAIL_COND_V(!host, ERR_CANT_CREATE); @@ -92,43 +113,84 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int connection_status = CONNECTION_CONNECTED; return OK; } -Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth) { +Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth, int p_client_port) { ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_client_port < 0 || p_client_port > 65535, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER); + + if (p_client_port != 0) { + 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); + } +#else + if (bind_ip.is_wildcard()) { + c_client.host = 0; + } else { + ERR_FAIL_COND_V(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER); + c_client.host = *(uint32_t *)bind_ip.get_ipv4(); + } +#endif + + c_client.port = p_client_port; - host = enet_host_create(NULL /* create a client host */, - 1 /* only allow 1 outgoing connection */, - SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, - p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */, - p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */); + 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 bandwith if > 0 */, + p_out_bandwidth /* limit outgoing bandwith if > 0 */); + } else { + host = enet_host_create(NULL /* 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 bandwith if > 0 */, + p_out_bandwidth /* limit outgoing bandwith if > 0 */); + } ERR_FAIL_COND_V(!host, ERR_CANT_CREATE); _setup_compressor(); + IP_Address ip; + if (p_address.is_valid_ip_address()) { + ip = p_address; + } else { +#ifdef GODOT_ENET + ip = IP::get_singleton()->resolve_hostname(p_address); +#else + ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4); +#endif + + ERR_FAIL_COND_V(!ip.is_valid(), ERR_CANT_RESOLVE); + } + ENetAddress address; #ifdef GODOT_ENET - enet_address_set_ip(&address, p_ip.get_ipv6(), 16); + enet_address_set_ip(&address, ip.get_ipv6(), 16); #else - ERR_FAIL_COND_V(!p_ip.is_ipv4(), ERR_INVALID_PARAMETER); - address.host = *(uint32_t *)p_ip.get_ipv4(); + ERR_FAIL_COND_V(!ip.is_ipv4(), ERR_INVALID_PARAMETER); + address.host = *(uint32_t *)ip.get_ipv4(); #endif address.port = p_port; - //enet_address_set_host (& address, "localhost"); - //address.port = p_port; - unique_id = _gen_unique_id(); - /* Initiate the connection, allocating the enough channels */ - ENetPeer *peer = enet_host_connect(host, &address, SYSCH_MAX, unique_id); + // Initiate connection, allocating enough channels + ENetPeer *peer = enet_host_connect(host, &address, channel_count, unique_id); if (peer == NULL) { enet_host_destroy(host); ERR_FAIL_COND_V(!peer, ERR_CANT_CREATE); } - //technically safe to ignore the peer or anything else. + // Technically safe to ignore the peer or anything else. connection_status = CONNECTION_CONNECTING; active = true; @@ -148,13 +210,13 @@ void NetworkedMultiplayerENet::poll() { /* Wait up to 1000 milliseconds for an event. */ while (true) { - if (!host || !active) //might have been disconnected while emitting a notification + if (!host || !active) // Might have been disconnected while emitting a notification return; int ret = enet_host_service(host, &event, 1); if (ret < 0) { - //error, do something? + // Error, do something? break; } else if (ret == 0) { break; @@ -162,7 +224,7 @@ void NetworkedMultiplayerENet::poll() { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: { - /* Store any relevant client information here. */ + // Store any relevant client information here. if (server && refuse_connections) { enet_peer_reset(event.peer); @@ -172,7 +234,7 @@ void NetworkedMultiplayerENet::poll() { int *new_id = memnew(int); *new_id = event.data; - if (*new_id == 0) { //data zero is sent by server (enet won't let you configure this). Server is always 1 + if (*new_id == 0) { // Data zero is sent by server (enet won't let you configure this). Server is always 1. *new_id = 1; } @@ -180,22 +242,22 @@ void NetworkedMultiplayerENet::poll() { peer_map[*new_id] = event.peer; - connection_status = CONNECTION_CONNECTED; //if connecting, this means it connected t something! + connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something! emit_signal("peer_connected", *new_id); if (server) { - //someone connected, let it know of all the peers available + // Someone connected, notify all the peers available for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { if (E->key() == *new_id) continue; - //send existing peers to new peer + // Send existing peers to new peer ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); encode_uint32(E->key(), &packet->data[4]); enet_peer_send(event.peer, SYSCH_CONFIG, packet); - //send the new peer to existing peers + // Send the new peer to existing peers packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); encode_uint32(*new_id, &packet->data[4]); @@ -209,7 +271,7 @@ void NetworkedMultiplayerENet::poll() { } break; case ENET_EVENT_TYPE_DISCONNECT: { - /* Reset the peer's client information. */ + // Reset the peer's client information. int *id = (int *)event.peer->data; @@ -220,12 +282,12 @@ void NetworkedMultiplayerENet::poll() { } else { if (server) { - //someone disconnected, let it know to everyone else + // Someone disconnected, notify everyone else for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { if (E->key() == *id) continue; - //send the new peer to existing peers + ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); encode_uint32(*id, &packet->data[4]); @@ -246,7 +308,7 @@ void NetworkedMultiplayerENet::poll() { case ENET_EVENT_TYPE_RECEIVE: { if (event.channelID == SYSCH_CONFIG) { - //some config message + // Some config message ERR_CONTINUE(event.packet->dataLength < 8); // Only server can send config messages @@ -270,7 +332,7 @@ void NetworkedMultiplayerENet::poll() { } enet_packet_destroy(event.packet); - } else if (event.channelID < SYSCH_MAX) { + } else if (event.channelID < channel_count) { Packet packet; packet.packet = event.packet; @@ -284,6 +346,7 @@ void NetworkedMultiplayerENet::poll() { uint32_t flags = decode_uint32(&event.packet->data[8]); packet.from = source; + packet.channel = event.channelID; if (server) { // Someone is cheating and trying to fake the source! @@ -292,13 +355,13 @@ void NetworkedMultiplayerENet::poll() { packet.from = *id; if (target == 0) { - //re-send the everyone but sender :| + // Re-send to everyone but sender :| incoming_packets.push_back(packet); - //and make copies for sending + // And make copies for sending for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (uint32_t(E->key()) == source) //do not resend to self + if (uint32_t(E->key()) == source) // Do not resend to self continue; ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); @@ -307,12 +370,12 @@ void NetworkedMultiplayerENet::poll() { } } else if (target < 0) { - //to all but one + // To all but one - //and make copies for sending + // And make copies for sending for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (uint32_t(E->key()) == source || E->key() == -target) //do not resend to self, also do not send to excluded + if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded continue; ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); @@ -321,18 +384,18 @@ void NetworkedMultiplayerENet::poll() { } if (-target != 1) { - //server is not excluded + // Server is not excluded incoming_packets.push_back(packet); } else { - //server is excluded, erase packet + // Server is excluded, erase packet enet_packet_destroy(packet.packet); } } else if (target == 1) { - //to myself and only myself + // To myself and only myself incoming_packets.push_back(packet); } else { - //to someone else, specifically + // To someone else, specifically ERR_CONTINUE(!peer_map.has(target)); enet_peer_send(peer_map[target], event.channelID, packet.packet); } @@ -341,14 +404,14 @@ void NetworkedMultiplayerENet::poll() { incoming_packets.push_back(packet); } - //destroy packet later.. + // Destroy packet later } else { ERR_CONTINUE(true); } } break; case ENET_EVENT_TYPE_NONE: { - //do nothing + // Do nothing } break; } } @@ -360,10 +423,10 @@ bool NetworkedMultiplayerENet::is_server() const { return server; } -void NetworkedMultiplayerENet::close_connection() { +void NetworkedMultiplayerENet::close_connection(uint32_t wait_usec) { - if (!active) - return; + ERR_FAIL_COND(!active); + ERR_FAIL_COND(wait_usec < 0); _pop_current_packet(); @@ -377,20 +440,54 @@ void NetworkedMultiplayerENet::close_connection() { if (peers_disconnected) { enet_host_flush(host); - OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send + + if (wait_usec > 0) { + OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send + } } enet_host_destroy(host); active = false; incoming_packets.clear(); - unique_id = 1; //server is 1 + unique_id = 1; // Server is 1 connection_status = CONNECTION_DISCONNECTED; } +void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) { + + ERR_FAIL_COND(!active); + ERR_FAIL_COND(!is_server()); + ERR_FAIL_COND(!peer_map.has(p_peer)) + + if (now) { + enet_peer_disconnect_now(peer_map[p_peer], 0); + + // enet_peer_disconnect_now doesn't generate ENET_EVENT_TYPE_DISCONNECT, + // notify everyone else, send disconnect signal & remove from peer_map like in poll() + + for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { + + if (E->key() == p_peer) + continue; + + ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); + encode_uint32(p_peer, &packet->data[4]); + enet_peer_send(E->get(), SYSCH_CONFIG, packet); + } + + emit_signal("peer_disconnected", p_peer); + peer_map.erase(p_peer); + } else { + enet_peer_disconnect_later(peer_map[p_peer], 0); + } +} + int NetworkedMultiplayerENet::get_available_packet_count() const { return incoming_packets.size(); } + Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { ERR_FAIL_COND_V(incoming_packets.size() == 0, ERR_UNAVAILABLE); @@ -405,6 +502,7 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buff return OK; } + Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!active, ERR_UNCONFIGURED); @@ -415,7 +513,10 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer switch (transfer_mode) { case TRANSFER_MODE_UNRELIABLE: { - packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; + if (always_ordered) + packet_flags = 0; + else + packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; channel = SYSCH_UNRELIABLE; } break; case TRANSFER_MODE_UNRELIABLE_ORDERED: { @@ -428,6 +529,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer } break; } + if (transfer_channel > SYSCH_CONFIG) + channel = transfer_channel; + Map<int, ENetPeer *>::Element *E = NULL; if (target_peer != 0) { @@ -440,9 +544,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer } ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags); - encode_uint32(unique_id, &packet->data[0]); //source ID - encode_uint32(target_peer, &packet->data[4]); //dest ID - encode_uint32(packet_flags, &packet->data[8]); //dest ID + encode_uint32(unique_id, &packet->data[0]); // Source ID + encode_uint32(target_peer, &packet->data[4]); // Dest ID + encode_uint32(packet_flags, &packet->data[8]); // Dest ID copymem(&packet->data[12], p_buffer, p_buffer_size); if (server) { @@ -450,14 +554,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer if (target_peer == 0) { enet_host_broadcast(host, channel, packet); } else if (target_peer < 0) { - //send to all but one - //and make copies for sending + // Send to all but one + // and make copies for sending int exclude = -target_peer; for (Map<int, ENetPeer *>::Element *F = peer_map.front(); F; F = F->next()) { - if (F->key() == exclude) // exclude packet + if (F->key() == exclude) // Exclude packet continue; ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags); @@ -465,14 +569,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer enet_peer_send(F->get(), channel, packet2); } - enet_packet_destroy(packet); //original packet no longer needed + enet_packet_destroy(packet); // Original packet no longer needed } else { enet_peer_send(E->get(), channel, packet); } } else { ERR_FAIL_COND_V(!peer_map.has(1), ERR_BUG); - enet_peer_send(peer_map[1], channel, packet); //send to server for broadcast.. + enet_peer_send(peer_map[1], channel, packet); // Send to server for broadcast } enet_host_flush(host); @@ -482,7 +586,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer int NetworkedMultiplayerENet::get_max_packet_size() const { - return 1 << 24; //anything is good + return 1 << 24; // Anything is good } void NetworkedMultiplayerENet::_pop_current_packet() { @@ -491,6 +595,7 @@ void NetworkedMultiplayerENet::_pop_current_packet() { enet_packet_destroy(current_packet.packet); current_packet.packet = NULL; current_packet.from = 0; + current_packet.channel = -1; } } @@ -511,16 +616,12 @@ uint32_t NetworkedMultiplayerENet::_gen_unique_id() const { (uint32_t)OS::get_singleton()->get_unix_time(), hash); hash = hash_djb2_one_32( (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash); - /* hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_unique_id().hash64(), hash ); - */ + (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap hash = hash_djb2_one_32( - (uint32_t)((uint64_t)this), hash); //rely on aslr heap - hash = hash_djb2_one_32( - (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack + (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack - hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion + hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion } return hash; @@ -565,7 +666,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer * while (total) { for (size_t i = 0; i < inBufferCount; i++) { int to_copy = MIN(total, int(inBuffers[i].dataLength)); - copymem(&enet->src_compressor_mem[ofs], inBuffers[i].data, to_copy); + copymem(&enet->src_compressor_mem.write[ofs], inBuffers[i].data, to_copy); ofs += to_copy; total -= to_copy; } @@ -596,7 +697,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer * return 0; if (ret > int(outLimit)) - return 0; //do not bother + return 0; // Do not bother copymem(outData, enet->dst_compressor_mem.ptr(), ret); @@ -651,19 +752,96 @@ void NetworkedMultiplayerENet::_setup_compressor() { void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) { - //do none + // Nothing to do +} + +IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const { + + ERR_FAIL_COND_V(!peer_map.has(p_peer_id), IP_Address()); + ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, IP_Address()); + ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, IP_Address()); + + IP_Address out; +#ifdef GODOT_ENET + out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host)); +#else + out.set_ipv4((uint8_t *)&(peer_map[p_peer_id]->address.host)); +#endif + + return out; +} + +int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const { + + ERR_FAIL_COND_V(!peer_map.has(p_peer_id), 0); + ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, 0); + ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, 0); +#ifdef GODOT_ENET + return peer_map[p_peer_id]->address.port; +#else + return peer_map[p_peer_id]->address.port; +#endif +} + +void NetworkedMultiplayerENet::set_transfer_channel(int p_channel) { + + ERR_FAIL_COND(p_channel < -1 || p_channel >= channel_count); + + if (p_channel == SYSCH_CONFIG) { + ERR_EXPLAIN("Channel " + itos(SYSCH_CONFIG) + " is reserved"); + ERR_FAIL(); + } + transfer_channel = p_channel; +} + +int NetworkedMultiplayerENet::get_transfer_channel() const { + return transfer_channel; +} + +void NetworkedMultiplayerENet::set_channel_count(int p_channel) { + + ERR_FAIL_COND(active); + ERR_FAIL_COND(p_channel < SYSCH_MAX); + channel_count = p_channel; +} + +int NetworkedMultiplayerENet::get_channel_count() const { + return channel_count; +} + +void NetworkedMultiplayerENet::set_always_ordered(bool p_ordered) { + always_ordered = p_ordered; +} + +bool NetworkedMultiplayerENet::is_always_ordered() const { + return always_ordered; } 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", "ip", "port", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("close_connection"), &NetworkedMultiplayerENet::close_connection); + 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("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); ClassDB::bind_method(D_METHOD("get_compression_mode"), &NetworkedMultiplayerENet::get_compression_mode); ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &NetworkedMultiplayerENet::set_bind_ip); + 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_packet_channel"), &NetworkedMultiplayerENet::get_packet_channel); + ClassDB::bind_method(D_METHOD("get_last_packet_channel"), &NetworkedMultiplayerENet::get_last_packet_channel); + ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &NetworkedMultiplayerENet::set_transfer_channel); + ClassDB::bind_method(D_METHOD("get_transfer_channel"), &NetworkedMultiplayerENet::get_transfer_channel); + ClassDB::bind_method(D_METHOD("set_channel_count", "channels"), &NetworkedMultiplayerENet::set_channel_count); + ClassDB::bind_method(D_METHOD("get_channel_count"), &NetworkedMultiplayerENet::get_channel_count); + ClassDB::bind_method(D_METHOD("set_always_ordered", "ordered"), &NetworkedMultiplayerENet::set_always_ordered); + ClassDB::bind_method(D_METHOD("is_always_ordered"), &NetworkedMultiplayerENet::is_always_ordered); ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel"), "set_transfer_channel", "get_transfer_channel"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "channel_count"), "set_channel_count", "get_channel_count"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "always_ordered"), "set_always_ordered", "is_always_ordered"); BIND_ENUM_CONSTANT(COMPRESS_NONE); BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER); @@ -681,6 +859,9 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet() { target_peer = 0; current_packet.packet = NULL; transfer_mode = TRANSFER_MODE_RELIABLE; + channel_count = SYSCH_MAX; + transfer_channel = -1; + always_ordered = false; connection_status = CONNECTION_DISCONNECTED; compression_mode = COMPRESS_NONE; enet_compressor.context = this; @@ -696,7 +877,7 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet() { close_connection(); } -// sets IP for ENet to bind when using create_server +// Sets IP for ENet to bind when using create_server or create_client // if no IP is set, then ENet bind to ENET_HOST_ANY void NetworkedMultiplayerENet::set_bind_ip(const IP_Address &p_ip) { ERR_FAIL_COND(!p_ip.is_valid() && !p_ip.is_wildcard()); diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index 440e9b5400..705807d429 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -68,6 +68,9 @@ private: int target_peer; TransferMode transfer_mode; + int transfer_channel; + int channel_count; + bool always_ordered; ENetEvent event; ENetPeer *peer; @@ -83,6 +86,7 @@ private: ENetPacket *packet; int from; + int channel; }; CompressionMode compression_mode; @@ -115,10 +119,15 @@ public: virtual int get_packet_peer() const; + virtual IP_Address get_peer_address(int p_peer_id) const; + virtual int get_peer_port(int p_peer_id) const; + 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 IP_Address &p_ip, int p_port, 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); + + void close_connection(uint32_t wait_usec = 100); - void close_connection(); + void disconnect_peer(int p_peer, bool now = false); virtual void poll(); @@ -140,6 +149,15 @@ public: void set_compression_mode(CompressionMode p_mode); CompressionMode get_compression_mode() const; + int get_packet_channel() const; + int get_last_packet_channel() const; + void set_transfer_channel(int p_channel); + int get_transfer_channel() const; + void set_channel_count(int p_channel); + int get_channel_count() const; + void set_always_ordered(bool p_ordered); + bool is_always_ordered() const; + NetworkedMultiplayerENet(); ~NetworkedMultiplayerENet(); |