diff options
Diffstat (limited to 'modules/enet')
-rw-r--r-- | modules/enet/doc_classes/ENetMultiplayerPeer.xml | 14 | ||||
-rw-r--r-- | modules/enet/doc_classes/ENetPacketPeer.xml | 26 | ||||
-rw-r--r-- | modules/enet/enet_multiplayer_peer.cpp | 461 | ||||
-rw-r--r-- | modules/enet/enet_multiplayer_peer.h | 31 |
4 files changed, 181 insertions, 351 deletions
diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 5181ae76ce..2e0d1f5079 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -21,13 +21,6 @@ [b]Note:[/b] The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. </description> </method> - <method name="close_connection"> - <return type="void" /> - <param index="0" name="wait_usec" type="int" default="100" /> - <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" /> <param index="0" name="address" type="String" /> @@ -37,7 +30,7 @@ <param index="4" name="out_bandwidth" type="int" default="0" /> <param index="5" name="local_port" type="int" default="0" /> <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]channel_count[/code] parameter can be used to specify the number of ENet channels allocated for the connection. 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 ENetMultiplayerPeer 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. + 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]channel_count[/code] parameter can be used to specify the number of ENet channels allocated for the connection. 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 ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] 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_mesh"> @@ -55,7 +48,7 @@ <param index="3" name="in_bandwidth" type="int" default="0" /> <param index="4" name="out_bandwidth" type="int" default="0" /> <description> - 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 4095 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 [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the server could not be created. + 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 4095 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 [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the server could not be created. </description> </method> <method name="get_peer" qualifiers="const"> @@ -77,8 +70,5 @@ <member name="host" type="ENetConnection" setter="" getter="get_host"> The underlying [ENetConnection] created after [method create_client] and [method create_server]. </member> - <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> - Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. - </member> </members> </class> diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml index 67760ba5e8..52f45b2338 100644 --- a/modules/enet/doc_classes/ENetPacketPeer.xml +++ b/modules/enet/doc_classes/ENetPacketPeer.xml @@ -80,7 +80,7 @@ <return type="void" /> <param index="0" name="ping_interval" type="int" /> <description> - Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. + Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. The default ping interval is [code]500[/code] milliseconds. </description> </method> <method name="reset"> @@ -115,33 +115,43 @@ <param index="2" name="deceleration" type="int" /> <description> Configures throttle parameter for a peer. - Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probably by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]). + Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probability by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]). When the throttle has a value of [constant PACKET_THROTTLE_SCALE], no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent. - When the throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent. + When the throttle has a value of [code]0[/code], all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent. Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions. </description> </method> </methods> <constants> <constant name="STATE_DISCONNECTED" value="0" enum="PeerState"> + The peer is disconnected. </constant> <constant name="STATE_CONNECTING" value="1" enum="PeerState"> + The peer is currently attempting to connect. </constant> <constant name="STATE_ACKNOWLEDGING_CONNECT" value="2" enum="PeerState"> + The peer has acknowledged the connection request. </constant> <constant name="STATE_CONNECTION_PENDING" value="3" enum="PeerState"> + The peer is currently connecting. </constant> <constant name="STATE_CONNECTION_SUCCEEDED" value="4" enum="PeerState"> + The peer has successfully connected, but is not ready to communicate with yet ([constant STATE_CONNECTED]). </constant> <constant name="STATE_CONNECTED" value="5" enum="PeerState"> + The peer is currently connected and ready to communicate with. </constant> <constant name="STATE_DISCONNECT_LATER" value="6" enum="PeerState"> + The peer is slated to disconnect after it has no more outgoing packets to send. </constant> <constant name="STATE_DISCONNECTING" value="7" enum="PeerState"> + The peer is currently disconnecting. </constant> <constant name="STATE_ACKNOWLEDGING_DISCONNECT" value="8" enum="PeerState"> + The peer has acknowledged the disconnection request. </constant> <constant name="STATE_ZOMBIE" value="9" enum="PeerState"> + The peer has lost connection, but is not considered truly disconnected (as the peer didn't acknowledge the disconnection request). </constant> <constant name="PEER_PACKET_LOSS" value="0" enum="PeerStatistic"> Mean packet loss of reliable packets as a ratio with respect to the [constant PACKET_LOSS_SCALE]. @@ -150,6 +160,7 @@ Packet loss variance. </constant> <constant name="PEER_PACKET_LOSS_EPOCH" value="2" enum="PeerStatistic"> + The time at which packet loss statistics were last updated (in milliseconds since the connection started). The interval for packet loss statistics updates is 10 seconds, and at least one packet must have been sent since the last statistics update. </constant> <constant name="PEER_ROUND_TRIP_TIME" value="3" enum="PeerStatistic"> Mean packet round trip time for reliable packets. @@ -164,24 +175,31 @@ Variance of the last trip time recorded. </constant> <constant name="PEER_PACKET_THROTTLE" value="7" enum="PeerStatistic"> + The peer's current throttle status. </constant> <constant name="PEER_PACKET_THROTTLE_LIMIT" value="8" enum="PeerStatistic"> + The maximum number of unreliable packets that should not be dropped. This value is always greater than or equal to [code]1[/code]. The initial value is equal to [constant PACKET_THROTTLE_SCALE]. </constant> <constant name="PEER_PACKET_THROTTLE_COUNTER" value="9" enum="PeerStatistic"> + Internal value used to increment the packet throttle counter. The value is hardcoded to [code]7[/code] and cannot be changed. You probably want to look at [constant PEER_PACKET_THROTTLE_ACCELERATION] instead. </constant> <constant name="PEER_PACKET_THROTTLE_EPOCH" value="10" enum="PeerStatistic"> + The time at which throttle statistics were last updated (in milliseconds since the connection started). The interval for throttle statistics updates is [constant PEER_PACKET_THROTTLE_INTERVAL]. </constant> <constant name="PEER_PACKET_THROTTLE_ACCELERATION" value="11" enum="PeerStatistic"> + The throttle's acceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]more[/i] often. The default value is [code]2[/code]. </constant> <constant name="PEER_PACKET_THROTTLE_DECELERATION" value="12" enum="PeerStatistic"> + The throttle's deceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]less[/i] often. The default value is [code]2[/code]. </constant> <constant name="PEER_PACKET_THROTTLE_INTERVAL" value="13" enum="PeerStatistic"> + The interval over which the lowest mean round trip time should be measured for use by the throttle mechanism (in milliseconds). The default value is [code]5000[/code]. </constant> <constant name="PACKET_LOSS_SCALE" value="65536"> The reference scale for packet loss. See [method get_statistic] and [constant PEER_PACKET_LOSS]. </constant> <constant name="PACKET_THROTTLE_SCALE" value="32"> - The reference value for throttle configuration. See [method throttle_configure]. + The reference value for throttle configuration. The default value is [code]32[/code]. See [method throttle_configure]. </constant> <constant name="FLAG_RELIABLE" value="1"> Mark the packet to be sent as reliable. diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index dfdd08c9f4..43c512ae16 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -44,6 +44,22 @@ int ENetMultiplayerPeer::get_packet_peer() const { return incoming_packets.front()->get().from; } +MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_packet_mode() const { + ERR_FAIL_COND_V_MSG(!_is_active(), TRANSFER_MODE_RELIABLE, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_V(incoming_packets.size() == 0, TRANSFER_MODE_RELIABLE); + return incoming_packets.front()->get().transfer_mode; +} + +int ENetMultiplayerPeer::get_packet_channel() const { + ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_V(incoming_packets.size() == 0, 1); + int ch = incoming_packets.front()->get().channel; + if (ch >= SYSCH_MAX) { // First 2 channels are reserved. + return ch - SYSCH_MAX + 1; + } + return 0; +} + Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) { ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); set_refuse_new_connections(false); @@ -114,182 +130,37 @@ Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) { return OK; } -bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) { - switch (p_type) { - case ENetConnection::EVENT_CONNECT: { - if (is_refusing_new_connections()) { - p_event.peer->reset(); - return false; - } - // Client joined with invalid ID, probably trying to exploit us. - if (p_event.data < 2 || peers.has((int)p_event.data)) { - p_event.peer->reset(); - return false; - } - int id = p_event.data; - p_event.peer->set_meta(SNAME("_net_id"), id); - peers[id] = p_event.peer; - - emit_signal(SNAME("peer_connected"), id); - if (server_relay) { - _notify_peers(id, true); - } - return false; - } - case ENetConnection::EVENT_DISCONNECT: { - int id = p_event.peer->get_meta(SNAME("_net_id")); - if (!peers.has(id)) { - // Never fully connected. - return false; - } - - emit_signal(SNAME("peer_disconnected"), id); - peers.erase(id); - if (server_relay) { - _notify_peers(id, false); - } - return false; - } - case ENetConnection::EVENT_RECEIVE: { - if (p_event.channel_id == SYSCH_CONFIG) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Only server can send config messages"); - } else { - if (p_event.packet->dataLength < 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Invalid packet size"); - } - - uint32_t source = decode_uint32(&p_event.packet->data[0]); - int target = decode_uint32(&p_event.packet->data[4]); - - uint32_t id = p_event.peer->get_meta(SNAME("_net_id")); - // Someone is cheating and trying to fake the source! - if (source != id) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!"); - } - - Packet packet; - packet.packet = p_event.packet; - packet.channel = p_event.channel_id; - packet.from = id; - - // Even if relaying is disabled, these targets are valid as incoming packets. - if (target == 1 || target == 0 || target < -1) { - packet.packet->referenceCount++; - incoming_packets.push_back(packet); - } - - if (server_relay && target != 1) { - packet.packet->referenceCount++; - _relay(source, target, p_event.channel_id, p_event.packet); - packet.packet->referenceCount--; - _destroy_unused(p_event.packet); - } - // Destroy packet later - } - return false; - } - default: - return true; +void ENetMultiplayerPeer::_store_packet(int32_t p_source, ENetConnection::Event &p_event) { + Packet packet; + packet.packet = p_event.packet; + packet.channel = p_event.channel_id; + packet.from = p_source; + if (p_event.packet->flags & ENET_PACKET_FLAG_RELIABLE) { + packet.transfer_mode = TRANSFER_MODE_RELIABLE; + } else if (p_event.packet->flags & ENET_PACKET_FLAG_UNSEQUENCED) { + packet.transfer_mode = TRANSFER_MODE_UNRELIABLE; + } else { + packet.transfer_mode = TRANSFER_MODE_UNRELIABLE_ORDERED; } + packet.packet->referenceCount++; + incoming_packets.push_back(packet); } -bool ENetMultiplayerPeer::_parse_client_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) { - switch (p_type) { - case ENetConnection::EVENT_CONNECT: { - connection_status = CONNECTION_CONNECTED; - emit_signal(SNAME("peer_connected"), 1); - emit_signal(SNAME("connection_succeeded")); - return false; - } - case ENetConnection::EVENT_DISCONNECT: { - if (connection_status == CONNECTION_CONNECTED) { - // Client just disconnected from server. - emit_signal(SNAME("server_disconnected")); - } else { - emit_signal(SNAME("connection_failed")); - } - close_connection(); - return true; - } - case ENetConnection::EVENT_RECEIVE: { - if (p_event.channel_id == SYSCH_CONFIG) { - // Config message - if (p_event.packet->dataLength != 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V(false); - } - - int msg = decode_uint32(&p_event.packet->data[0]); - int id = decode_uint32(&p_event.packet->data[4]); - - switch (msg) { - case SYSMSG_ADD_PEER: { - peers[id] = Ref<ENetPacketPeer>(); - emit_signal(SNAME("peer_connected"), id); - - } break; - case SYSMSG_REMOVE_PEER: { - peers.erase(id); - emit_signal(SNAME("peer_disconnected"), id); - } break; - } - _destroy_unused(p_event.packet); - } else { - if (p_event.packet->dataLength < 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Invalid packet size"); - } - - uint32_t source = decode_uint32(&p_event.packet->data[0]); - Packet packet; - packet.packet = p_event.packet; - packet.from = source; - packet.channel = p_event.channel_id; - - packet.packet->referenceCount++; - incoming_packets.push_back(packet); - // Destroy packet later - } - return false; +void ENetMultiplayerPeer::_disconnect_inactive_peers() { + HashSet<int> to_drop; + for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.value->is_active()) { + continue; } - default: - return true; + to_drop.insert(E.key); } -} - -bool ENetMultiplayerPeer::_parse_mesh_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event, int p_peer_id) { - switch (p_type) { - case ENetConnection::EVENT_CONNECT: - p_event.peer->reset(); - return false; - case ENetConnection::EVENT_DISCONNECT: - if (peers.has(p_peer_id)) { - emit_signal(SNAME("peer_disconnected"), p_peer_id); - peers.erase(p_peer_id); - } - hosts.erase(p_peer_id); - return true; - case ENetConnection::EVENT_RECEIVE: { - if (p_event.packet->dataLength < 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Invalid packet size"); - } - - Packet packet; - packet.packet = p_event.packet; - packet.from = p_peer_id; - packet.channel = p_event.channel_id; - - packet.packet->referenceCount++; - incoming_packets.push_back(packet); - return false; - } break; - default: - // Nothing to do - return true; + for (const int &P : to_drop) { + peers.erase(P); + if (hosts.has(P)) { + hosts.erase(P); + } + ERR_CONTINUE(active_mode == MODE_CLIENT && P != TARGET_PEER_SERVER); + emit_signal(SNAME("peer_disconnected"), P); } } @@ -298,74 +169,92 @@ void ENetMultiplayerPeer::poll() { _pop_current_packet(); + _disconnect_inactive_peers(); + switch (active_mode) { case MODE_CLIENT: { - if (peers.has(1) && !peers[1]->is_active()) { - if (connection_status == CONNECTION_CONNECTED) { - // Client just disconnected from server. - emit_signal(SNAME("server_disconnected")); - } else { - emit_signal(SNAME("connection_failed")); - } - close_connection(); + if (!peers.has(1)) { + close(); return; } ENetConnection::Event event; ENetConnection::EventType ret = hosts[0]->service(0, event); - if (ret == ENetConnection::EVENT_ERROR) { - return; - } do { - if (_parse_client_event(ret, event)) { - return; + if (ret == ENetConnection::EVENT_CONNECT) { + connection_status = CONNECTION_CONNECTED; + emit_signal(SNAME("peer_connected"), 1); + } else if (ret == ENetConnection::EVENT_DISCONNECT) { + if (connection_status == CONNECTION_CONNECTED) { + // Client just disconnected from server. + emit_signal(SNAME("peer_disconnected"), 1); + } + close(); + } else if (ret == ENetConnection::EVENT_RECEIVE) { + _store_packet(1, event); + } else if (ret != ENetConnection::EVENT_NONE) { + close(); // Error. } - } while (hosts[0]->check_events(ret, event) > 0); + } while (hosts.has(0) && hosts[0]->check_events(ret, event) > 0); } break; case MODE_SERVER: { - for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (!(E.value->is_active())) { - emit_signal(SNAME("peer_disconnected"), E.value->get_meta(SNAME("_net_id"))); - peers.erase(E.key); - } - } ENetConnection::Event event; ENetConnection::EventType ret = hosts[0]->service(0, event); - if (ret == ENetConnection::EVENT_ERROR) { - return; - } do { - if (_parse_server_event(ret, event)) { - return; + if (ret == ENetConnection::EVENT_CONNECT) { + if (is_refusing_new_connections()) { + event.peer->reset(); + continue; + } + // Client joined with invalid ID, probably trying to exploit us. + if (event.data < 2 || peers.has((int)event.data)) { + event.peer->reset(); + continue; + } + int id = event.data; + event.peer->set_meta(SNAME("_net_id"), id); + peers[id] = event.peer; + emit_signal(SNAME("peer_connected"), id); + } else if (ret == ENetConnection::EVENT_DISCONNECT) { + int id = event.peer->get_meta(SNAME("_net_id")); + if (!peers.has(id)) { + // Never fully connected. + continue; + } + emit_signal(SNAME("peer_disconnected"), id); + peers.erase(id); + } else if (ret == ENetConnection::EVENT_RECEIVE) { + int32_t source = event.peer->get_meta(SNAME("_net_id")); + _store_packet(source, event); + } else if (ret != ENetConnection::EVENT_NONE) { + close(); // Error } - } while (hosts[0]->check_events(ret, event) > 0); + } while (hosts.has(0) && hosts[0]->check_events(ret, event) > 0); } break; case MODE_MESH: { - for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (!(E.value->is_active())) { - emit_signal(SNAME("peer_disconnected"), E.key); - peers.erase(E.key); - if (hosts.has(E.key)) { - hosts.erase(E.key); - } - } - } + HashSet<int> to_drop; for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { ENetConnection::Event event; ENetConnection::EventType ret = E.value->service(0, event); - if (ret == ENetConnection::EVENT_ERROR) { - if (peers.has(E.key)) { - emit_signal(SNAME("peer_disconnected"), E.key); - peers.erase(E.key); - } - hosts.erase(E.key); - continue; - } do { - if (_parse_mesh_event(ret, event, E.key)) { + if (ret == ENetConnection::EVENT_CONNECT) { + event.peer->reset(); + } else if (ret == ENetConnection::EVENT_RECEIVE) { + _store_packet(E.key, event); + } else if (ret == ENetConnection::EVENT_NONE) { + break; // Keep polling the others. + } else { + to_drop.insert(E.key); // Error or disconnect. break; // Keep polling the others. } } while (E.value->check_events(ret, event) > 0); } + for (const int &P : to_drop) { + if (peers.has(P)) { + emit_signal(SNAME("peer_disconnected"), P); + peers.erase(P); + } + hosts.erase(P); + } } break; default: return; @@ -376,29 +265,45 @@ bool ENetMultiplayerPeer::is_server() const { return active_mode == MODE_SERVER; } -void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) { +bool ENetMultiplayerPeer::is_server_relay_supported() const { + return active_mode == MODE_SERVER || active_mode == MODE_CLIENT; +} + +void ENetMultiplayerPeer::disconnect_peer(int p_peer, bool p_force) { + ERR_FAIL_COND(!_is_active() || !peers.has(p_peer)); + peers[p_peer]->peer_disconnect(0); // Will be removed during next poll. + if (active_mode == MODE_CLIENT || active_mode == MODE_SERVER) { + hosts[0]->flush(); + } else { + ERR_FAIL_COND(!hosts.has(p_peer)); + hosts[p_peer]->flush(); + } + if (p_force) { + peers.erase(p_peer); + if (hosts.has(p_peer)) { + hosts.erase(p_peer); + } + if (active_mode == MODE_CLIENT) { + hosts.clear(); // Avoid flushing again. + close(); + } + } +} + +void ENetMultiplayerPeer::close() { if (!_is_active()) { return; } _pop_current_packet(); - bool peers_disconnected = false; for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { if (E.value.is_valid() && E.value->get_state() == ENetPacketPeer::STATE_CONNECTED) { - E.value->peer_disconnect_now(unique_id); - peers_disconnected = true; + E.value->peer_disconnect_now(0); } } - - if (peers_disconnected) { - for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { - E.value->flush(); - } - - if (wait_usec > 0) { - OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send - } + for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { + E.value->flush(); } active_mode = MODE_NONE; @@ -422,8 +327,8 @@ Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_si current_packet = incoming_packets.front()->get(); incoming_packets.pop_front(); - *r_buffer = (const uint8_t *)(¤t_packet.packet->data[8]); - r_buffer_size = current_packet.packet->dataLength - 8; + *r_buffer = (const uint8_t *)(current_packet.packet->data); + r_buffer_size = current_packet.packet->dataLength; return OK; } @@ -436,9 +341,9 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size int packet_flags = 0; int channel = SYSCH_RELIABLE; - int transfer_channel = get_transfer_channel(); - if (transfer_channel > 0) { - channel = SYSCH_MAX + transfer_channel - 1; + int tr_channel = get_transfer_channel(); + if (tr_channel > 0) { + channel = SYSCH_MAX + tr_channel - 1; } else { switch (get_transfer_mode()) { case TRANSFER_MODE_UNRELIABLE: { @@ -457,15 +362,13 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size } #ifdef DEBUG_ENABLED - if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size + 8 > ENET_HOST_DEFAULT_MTU) { - WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size + 8, ENET_HOST_DEFAULT_MTU)); + if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size > ENET_HOST_DEFAULT_MTU) { + WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU)); } #endif - ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags); - encode_uint32(unique_id, &packet->data[0]); // Source ID - encode_uint32(target_peer, &packet->data[4]); // Dest ID - memcpy(&packet->data[8], p_buffer, p_buffer_size); + ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size, packet_flags); + memcpy(&packet->data[0], p_buffer, p_buffer_size); if (is_server()) { if (target_peer == 0) { @@ -548,16 +451,6 @@ void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enabled) { MultiplayerPeer::set_refuse_new_connections(p_enabled); } -void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) { - ERR_FAIL_COND_MSG(_is_active(), "Server relaying can't be toggled while the multiplayer instance is active."); - - server_relay = p_enabled; -} - -bool ENetMultiplayerPeer::is_server_relay_enabled() const { - return server_relay; -} - Ref<ENetConnection> ENetMultiplayerPeer::get_host() const { ERR_FAIL_COND_V(!_is_active(), nullptr); ERR_FAIL_COND_V(active_mode == MODE_MESH, nullptr); @@ -577,84 +470,16 @@ void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) { } } -void ENetMultiplayerPeer::_relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet) { - if (p_to == 0) { - // Re-send to everyone but sender :| - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_from) { - continue; - } - - E.value->send(p_channel, p_packet); - } - } else if (p_to < 0) { - // Re-send to everyone but excluded and sender. - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_from || E.key == -p_to) { // Do not resend to self, also do not send to excluded - continue; - } - - E.value->send(p_channel, p_packet); - } - } else { - // To someone else, specifically - ERR_FAIL_COND(!peers.has(p_to)); - ENetPacket *packet = enet_packet_create(p_packet->data, p_packet->dataLength, p_packet->flags); - peers[p_to]->send(p_channel, packet); - } -} - -void ENetMultiplayerPeer::_notify_peers(int p_id, bool p_connected) { - if (p_connected) { - ERR_FAIL_COND(!peers.has(p_id)); - // Someone connected, notify all the peers available. - Ref<ENetPacketPeer> peer = peers[p_id]; - ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); - encode_uint32(p_id, &packet->data[4]); - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_id) { - continue; - } - // Send new peer to existing peer. - E.value->send(SYSCH_CONFIG, packet); - // Send existing peer to new peer. - // This packet will be automatically destroyed by ENet after send. - ENetPacket *packet2 = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_ADD_PEER, &packet2->data[0]); - encode_uint32(E.key, &packet2->data[4]); - peer->send(SYSCH_CONFIG, packet2); - } - _destroy_unused(packet); - } else { - // Server just received a client disconnect and is in relay mode, notify everyone else. - ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); - encode_uint32(p_id, &packet->data[4]); - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_id) { - continue; - } - E.value->send(SYSCH_CONFIG, packet); - } - _destroy_unused(packet); - } -} - void ENetMultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("create_client", "address", "port", "channel_count", "in_bandwidth", "out_bandwidth", "local_port"), &ENetMultiplayerPeer::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("create_mesh", "unique_id"), &ENetMultiplayerPeer::create_mesh); ClassDB::bind_method(D_METHOD("add_mesh_peer", "peer_id", "host"), &ENetMultiplayerPeer::add_mesh_peer); - ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &ENetMultiplayerPeer::close_connection, DEFVAL(100)); ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &ENetMultiplayerPeer::set_bind_ip); - ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &ENetMultiplayerPeer::set_server_relay_enabled); - ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &ENetMultiplayerPeer::is_server_relay_enabled); ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host); ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host"); } @@ -664,7 +489,7 @@ ENetMultiplayerPeer::ENetMultiplayerPeer() { ENetMultiplayerPeer::~ENetMultiplayerPeer() { if (_is_active()) { - close_connection(); + close(); } } diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h index 3152068d46..2665b69669 100644 --- a/modules/enet/enet_multiplayer_peer.h +++ b/modules/enet/enet_multiplayer_peer.h @@ -47,10 +47,9 @@ private: }; enum { - SYSCH_CONFIG = 0, - SYSCH_RELIABLE = 1, - SYSCH_UNRELIABLE = 2, - SYSCH_MAX = 3 + SYSCH_RELIABLE = 0, + SYSCH_UNRELIABLE = 1, + SYSCH_MAX = 2 }; enum Mode { @@ -66,8 +65,6 @@ private: int target_peer = 0; - bool server_relay = true; - ConnectionStatus connection_status = CONNECTION_DISCONNECTED; HashMap<int, Ref<ENetConnection>> hosts; @@ -77,18 +74,16 @@ private: ENetPacket *packet = nullptr; int from = 0; int channel = 0; + TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; }; List<Packet> incoming_packets; Packet current_packet; + void _store_packet(int32_t p_source, ENetConnection::Event &p_event); void _pop_current_packet(); - bool _parse_server_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event); - bool _parse_client_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event); - bool _parse_mesh_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event, int p_peer_id); - void _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet); - void _notify_peers(int p_id, bool p_connected); + void _disconnect_inactive_peers(); void _destroy_unused(ENetPacket *p_packet); _FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; } @@ -99,10 +94,18 @@ protected: public: virtual void set_target_peer(int p_peer) override; + virtual int get_packet_peer() const override; + virtual TransferMode get_packet_mode() const override; + virtual int get_packet_channel() const override; virtual void poll() override; + virtual void close() override; + virtual void disconnect_peer(int p_peer, bool p_force = false) override; + virtual bool is_server() const override; + virtual bool is_server_relay_supported() const override; + // Overridden so we can instrument the DTLSServer when needed. virtual void set_refuse_new_connections(bool p_enabled) override; @@ -120,13 +123,7 @@ public: Error create_mesh(int p_id); Error add_mesh_peer(int p_id, Ref<ENetConnection> p_host); - void close_connection(uint32_t wait_usec = 100); - - void disconnect_peer(int p_peer, bool now = false); - void set_bind_ip(const IPAddress &p_ip); - void set_server_relay_enabled(bool p_enabled); - bool is_server_relay_enabled() const; Ref<ENetConnection> get_host() const; Ref<ENetPacketPeer> get_peer(int p_id) const; |