[Net] ENet poll now only service the connection once.

It used to call `enet_host_service` until all events were consumed, but
that also meant constantly polling the connection leading to potentially
unbounded processing time.

It now only service the connection once, and instead consumes all the
retrieved events via `enet_host_check_events`.
This commit is contained in:
Jordan Schidlowsky 2021-09-27 11:07:00 +02:00 committed by Fabio Alessandrelli
parent 397d895fb7
commit 373d5ea103
4 changed files with 187 additions and 168 deletions

View file

@ -117,6 +117,47 @@ Ref<ENetPacketPeer> ENetConnection::connect_to_host(const String &p_address, int
return out; return out;
} }
ENetConnection::EventType ENetConnection::_parse_event(const ENetEvent &p_event, Event &r_event) {
switch (p_event.type) {
case ENET_EVENT_TYPE_CONNECT: {
if (p_event.peer->data == nullptr) {
Ref<ENetPacketPeer> pp = memnew(ENetPacketPeer(p_event.peer));
peers.push_back(pp);
}
r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
r_event.data = p_event.data;
return EVENT_CONNECT;
} break;
case ENET_EVENT_TYPE_DISCONNECT: {
// A peer disconnected.
if (p_event.peer->data != nullptr) {
Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
pp->_on_disconnect();
peers.erase(pp);
r_event.peer = pp;
r_event.data = p_event.data;
return EVENT_DISCONNECT;
}
return EVENT_ERROR;
} break;
case ENET_EVENT_TYPE_RECEIVE: {
// Packet reveived.
if (p_event.peer->data != nullptr) {
Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
r_event.channel_id = p_event.channelID;
r_event.packet = p_event.packet;
return EVENT_RECEIVE;
}
return EVENT_ERROR;
} break;
case ENET_EVENT_TYPE_NONE:
return EVENT_NONE;
default:
return EVENT_NONE;
}
}
ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event) { ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event) {
ERR_FAIL_COND_V_MSG(!host, EVENT_ERROR, "The ENetConnection instance isn't currently active."); ERR_FAIL_COND_V_MSG(!host, EVENT_ERROR, "The ENetConnection instance isn't currently active.");
ERR_FAIL_COND_V(r_event.peer.is_valid(), EVENT_ERROR); ERR_FAIL_COND_V(r_event.peer.is_valid(), EVENT_ERROR);
@ -140,44 +181,19 @@ ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event)
} else if (ret == 0) { } else if (ret == 0) {
return EVENT_NONE; return EVENT_NONE;
} }
switch (event.type) { return _parse_event(event, r_event);
case ENET_EVENT_TYPE_CONNECT: { }
if (event.peer->data == nullptr) {
Ref<ENetPacketPeer> pp = memnew(ENetPacketPeer(event.peer)); int ENetConnection::check_events(EventType &r_type, Event &r_event) {
peers.push_back(pp); ERR_FAIL_COND_V_MSG(!host, -1, "The ENetConnection instance isn't currently active.");
} ENetEvent event;
r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data); int ret = enet_host_check_events(host, &event);
r_event.data = event.data; if (ret < 0) {
return EVENT_CONNECT; r_type = EVENT_ERROR;
} break; return ret;
case ENET_EVENT_TYPE_DISCONNECT: {
// A peer disconnected.
if (event.peer->data != nullptr) {
Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
pp->_on_disconnect();
peers.erase(pp);
r_event.peer = pp;
r_event.data = event.data;
return EVENT_DISCONNECT;
}
return EVENT_ERROR;
} break;
case ENET_EVENT_TYPE_RECEIVE: {
// Packet reveived.
if (event.peer->data != nullptr) {
Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
r_event.channel_id = event.channelID;
r_event.packet = event.packet;
return EVENT_RECEIVE;
}
return EVENT_ERROR;
} break;
case ENET_EVENT_TYPE_NONE:
return EVENT_NONE;
default:
return EVENT_NONE;
} }
r_type = _parse_event(event, r_event);
return ret;
} }
void ENetConnection::flush() { void ENetConnection::flush() {

View file

@ -79,6 +79,7 @@ private:
ENetHost *host = nullptr; ENetHost *host = nullptr;
List<Ref<ENetPacketPeer>> peers; List<Ref<ENetPacketPeer>> peers;
EventType _parse_event(const ENetEvent &p_event, Event &r_event);
Error _create(ENetAddress *p_address, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth); Error _create(ENetAddress *p_address, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth);
Array _service(int p_timeout = 0); Array _service(int p_timeout = 0);
void _broadcast(int p_channel, PackedByteArray p_packet, int p_flags); void _broadcast(int p_channel, PackedByteArray p_packet, int p_flags);
@ -110,6 +111,7 @@ public:
void destroy(); void destroy();
Ref<ENetPacketPeer> connect_to_host(const String &p_address, int p_port, int p_channels, int p_data = 0); Ref<ENetPacketPeer> connect_to_host(const String &p_address, int p_port, int p_channels, int p_data = 0);
EventType service(int p_timeout, Event &r_event); EventType service(int p_timeout, Event &r_event);
int check_events(EventType &r_type, Event &r_event);
void flush(); void flush();
void bandwidth_limit(int p_in_bandwidth = 0, int p_out_bandwidth = 0); void bandwidth_limit(int p_in_bandwidth = 0, int p_out_bandwidth = 0);
void channel_limit(int p_max_channels); void channel_limit(int p_max_channels);

View file

@ -114,32 +114,21 @@ Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) {
return OK; return OK;
} }
bool ENetMultiplayerPeer::_poll_server() { bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) {
for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { switch (p_type) {
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 true;
}
switch (ret) {
case ENetConnection::EVENT_CONNECT: { case ENetConnection::EVENT_CONNECT: {
if (is_refusing_new_connections()) { if (is_refusing_new_connections()) {
event.peer->reset(); p_event.peer->reset();
return false; return false;
} }
// Client joined with invalid ID, probably trying to exploit us. // Client joined with invalid ID, probably trying to exploit us.
if (event.data < 2 || peers.has((int)event.data)) { if (p_event.data < 2 || peers.has((int)p_event.data)) {
event.peer->reset(); p_event.peer->reset();
return false; return false;
} }
int id = event.data; int id = p_event.data;
event.peer->set_meta(SNAME("_net_id"), id); p_event.peer->set_meta(SNAME("_net_id"), id);
peers[id] = event.peer; peers[id] = p_event.peer;
emit_signal(SNAME("peer_connected"), id); emit_signal(SNAME("peer_connected"), id);
if (server_relay) { if (server_relay) {
@ -148,7 +137,7 @@ bool ENetMultiplayerPeer::_poll_server() {
return false; return false;
} }
case ENetConnection::EVENT_DISCONNECT: { case ENetConnection::EVENT_DISCONNECT: {
int id = event.peer->get_meta(SNAME("_net_id")); int id = p_event.peer->get_meta(SNAME("_net_id"));
if (!peers.has(id)) { if (!peers.has(id)) {
// Never fully connected. // Never fully connected.
return false; return false;
@ -162,28 +151,28 @@ bool ENetMultiplayerPeer::_poll_server() {
return false; return false;
} }
case ENetConnection::EVENT_RECEIVE: { case ENetConnection::EVENT_RECEIVE: {
if (event.channel_id == SYSCH_CONFIG) { if (p_event.channel_id == SYSCH_CONFIG) {
_destroy_unused(event.packet); _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Only server can send config messages"); ERR_FAIL_V_MSG(false, "Only server can send config messages");
} else { } else {
if (event.packet->dataLength < 8) { if (p_event.packet->dataLength < 8) {
_destroy_unused(event.packet); _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Invalid packet size"); ERR_FAIL_V_MSG(false, "Invalid packet size");
} }
uint32_t source = decode_uint32(&event.packet->data[0]); uint32_t source = decode_uint32(&p_event.packet->data[0]);
int target = decode_uint32(&event.packet->data[4]); int target = decode_uint32(&p_event.packet->data[4]);
uint32_t id = event.peer->get_meta(SNAME("_net_id")); uint32_t id = p_event.peer->get_meta(SNAME("_net_id"));
// Someone is cheating and trying to fake the source! // Someone is cheating and trying to fake the source!
if (source != id) { if (source != id) {
_destroy_unused(event.packet); _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!"); ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!");
} }
Packet packet; Packet packet;
packet.packet = event.packet; packet.packet = p_event.packet;
packet.channel = event.channel_id; packet.channel = p_event.channel_id;
packet.from = id; packet.from = id;
// Even if relaying is disabled, these targets are valid as incoming packets. // Even if relaying is disabled, these targets are valid as incoming packets.
@ -194,9 +183,9 @@ bool ENetMultiplayerPeer::_poll_server() {
if (server_relay && target != 1) { if (server_relay && target != 1) {
packet.packet->referenceCount++; packet.packet->referenceCount++;
_relay(source, target, event.channel_id, event.packet); _relay(source, target, p_event.channel_id, p_event.packet);
packet.packet->referenceCount--; packet.packet->referenceCount--;
_destroy_unused(event.packet); _destroy_unused(p_event.packet);
} }
// Destroy packet later // Destroy packet later
} }
@ -207,23 +196,8 @@ bool ENetMultiplayerPeer::_poll_server() {
} }
} }
bool ENetMultiplayerPeer::_poll_client() { bool ENetMultiplayerPeer::_parse_client_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) {
if (peers.has(1) && !peers[1]->is_active()) { switch (p_type) {
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;
}
ENetConnection::Event event;
ENetConnection::EventType ret = hosts[0]->service(0, event);
if (ret == ENetConnection::EVENT_ERROR) {
return true;
}
switch (ret) {
case ENetConnection::EVENT_CONNECT: { case ENetConnection::EVENT_CONNECT: {
connection_status = CONNECTION_CONNECTED; connection_status = CONNECTION_CONNECTED;
emit_signal(SNAME("peer_connected"), 1); emit_signal(SNAME("peer_connected"), 1);
@ -241,15 +215,15 @@ bool ENetMultiplayerPeer::_poll_client() {
return true; return true;
} }
case ENetConnection::EVENT_RECEIVE: { case ENetConnection::EVENT_RECEIVE: {
if (event.channel_id == SYSCH_CONFIG) { if (p_event.channel_id == SYSCH_CONFIG) {
// Config message // Config message
if (event.packet->dataLength != 8) { if (p_event.packet->dataLength != 8) {
_destroy_unused(event.packet); _destroy_unused(p_event.packet);
ERR_FAIL_V(false); ERR_FAIL_V(false);
} }
int msg = decode_uint32(&event.packet->data[0]); int msg = decode_uint32(&p_event.packet->data[0]);
int id = decode_uint32(&event.packet->data[4]); int id = decode_uint32(&p_event.packet->data[4]);
switch (msg) { switch (msg) {
case SYSMSG_ADD_PEER: { case SYSMSG_ADD_PEER: {
@ -262,18 +236,18 @@ bool ENetMultiplayerPeer::_poll_client() {
emit_signal(SNAME("peer_disconnected"), id); emit_signal(SNAME("peer_disconnected"), id);
} break; } break;
} }
_destroy_unused(event.packet); _destroy_unused(p_event.packet);
} else { } else {
if (event.packet->dataLength < 8) { if (p_event.packet->dataLength < 8) {
_destroy_unused(event.packet); _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Invalid packet size"); ERR_FAIL_V_MSG(false, "Invalid packet size");
} }
uint32_t source = decode_uint32(&event.packet->data[0]); uint32_t source = decode_uint32(&p_event.packet->data[0]);
Packet packet; Packet packet;
packet.packet = event.packet; packet.packet = p_event.packet;
packet.from = source; packet.from = source;
packet.channel = event.channel_id; packet.channel = p_event.channel_id;
packet.packet->referenceCount++; packet.packet->referenceCount++;
incoming_packets.push_back(packet); incoming_packets.push_back(packet);
@ -286,7 +260,86 @@ bool ENetMultiplayerPeer::_poll_client() {
} }
} }
bool ENetMultiplayerPeer::_poll_mesh() { 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;
}
}
void ENetMultiplayerPeer::poll() {
ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active.");
_pop_current_packet();
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();
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;
}
} while (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;
}
} while (hosts[0]->check_events(ret, event) > 0);
} break;
case MODE_MESH: {
for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
if (!(E.value->is_active())) { if (!(E.value->is_active())) {
emit_signal(SNAME("peer_disconnected"), E.key); emit_signal(SNAME("peer_disconnected"), E.key);
@ -296,7 +349,6 @@ bool ENetMultiplayerPeer::_poll_mesh() {
} }
} }
} }
bool should_stop = true;
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
ENetConnection::Event event; ENetConnection::Event event;
ENetConnection::EventType ret = E.value->service(0, event); ENetConnection::EventType ret = E.value->service(0, event);
@ -308,67 +360,16 @@ bool ENetMultiplayerPeer::_poll_mesh() {
hosts.erase(E.key); hosts.erase(E.key);
continue; continue;
} }
switch (ret) { do {
case ENetConnection::EVENT_CONNECT: if (_parse_mesh_event(ret, event, E.key)) {
should_stop = false; break; // Keep polling the others.
event.peer->reset();
break;
case ENetConnection::EVENT_DISCONNECT:
should_stop = false;
if (peers.has(E.key)) {
emit_signal(SNAME("peer_disconnected"), E.key);
peers.erase(E.key);
} }
hosts.erase(E.key); } while (E.value->check_events(ret, event) > 0);
break;
case ENetConnection::EVENT_RECEIVE: {
should_stop = false;
if (event.packet->dataLength < 8) {
_destroy_unused(event.packet);
ERR_CONTINUE_MSG(true, "Invalid packet size");
} }
Packet packet;
packet.packet = event.packet;
packet.from = E.key;
packet.channel = event.channel_id;
packet.packet->referenceCount++;
incoming_packets.push_back(packet);
} break; } break;
default: default:
break; // Nothing to do
}
}
return should_stop;
}
void ENetMultiplayerPeer::poll() {
ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active.");
_pop_current_packet();
while (true) {
switch (active_mode) {
case MODE_CLIENT:
if (_poll_client()) {
return; return;
} }
break;
case MODE_SERVER:
if (_poll_server()) {
return;
}
break;
case MODE_MESH:
if (_poll_mesh()) {
return;
}
break;
default:
return;
}
}
} }
bool ENetMultiplayerPeer::is_server() const { bool ENetMultiplayerPeer::is_server() const {

View file

@ -84,9 +84,9 @@ private:
Packet current_packet; Packet current_packet;
void _pop_current_packet(); void _pop_current_packet();
bool _poll_server(); bool _parse_server_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
bool _poll_client(); bool _parse_client_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
bool _poll_mesh(); 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 _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet);
void _notify_peers(int p_id, bool p_connected); void _notify_peers(int p_id, bool p_connected);
void _destroy_unused(ENetPacket *p_packet); void _destroy_unused(ENetPacket *p_packet);