You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-06 12:20:30 +00:00
[MP] Add MultiplayerPeer disconnect_peer, close.
Update ENet, WebRTC, and WebSocket to support peer disconnection and unify the close function.
This commit is contained in:
@@ -146,92 +146,24 @@ void ENetMultiplayerPeer::_store_packet(int32_t p_source, ENetConnection::Event
|
||||
incoming_packets.push_back(packet);
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
case ENetConnection::EVENT_RECEIVE: {
|
||||
int32_t source = p_event.peer->get_meta(SNAME("_net_id"));
|
||||
_store_packet(source, p_event);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
to_drop.insert(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
for (const int &P : to_drop) {
|
||||
peers.erase(P);
|
||||
if (hosts.has(P)) {
|
||||
hosts.erase(P);
|
||||
}
|
||||
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;
|
||||
if (active_mode == MODE_CLIENT) {
|
||||
ERR_CONTINUE(P != TARGET_PEER_SERVER);
|
||||
emit_signal(SNAME("server_disconnected"));
|
||||
}
|
||||
case ENetConnection::EVENT_RECEIVE: {
|
||||
_store_packet(1, p_event);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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: {
|
||||
_store_packet(p_peer_id, p_event);
|
||||
return false;
|
||||
} break;
|
||||
default:
|
||||
// Nothing to do
|
||||
return true;
|
||||
emit_signal(SNAME("peer_disconnected"), P);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,74 +172,96 @@ 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);
|
||||
emit_signal(SNAME("connection_succeeded"));
|
||||
} else if (ret == ENetConnection::EVENT_DISCONNECT) {
|
||||
if (connection_status == CONNECTION_CONNECTED) {
|
||||
// Client just disconnected from server.
|
||||
emit_signal(SNAME("server_disconnected"));
|
||||
emit_signal(SNAME("peer_disconnected"), 1);
|
||||
} else {
|
||||
emit_signal(SNAME("connection_failed"));
|
||||
}
|
||||
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;
|
||||
@@ -322,29 +276,41 @@ bool ENetMultiplayerPeer::is_server_relay_supported() const {
|
||||
return active_mode == MODE_SERVER || active_mode == MODE_CLIENT;
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) {
|
||||
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;
|
||||
@@ -516,7 +482,6 @@ void ENetMultiplayerPeer::_bind_methods() {
|
||||
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("get_host"), &ENetMultiplayerPeer::get_host);
|
||||
@@ -531,7 +496,7 @@ ENetMultiplayerPeer::ENetMultiplayerPeer() {
|
||||
|
||||
ENetMultiplayerPeer::~ENetMultiplayerPeer() {
|
||||
if (_is_active()) {
|
||||
close_connection();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user