[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:
Fabio Alessandrelli 2022-10-22 17:49:40 +02:00
parent 028db9f2b5
commit 39f3d9d59e
13 changed files with 188 additions and 201 deletions

View file

@ -13,6 +13,20 @@
<link title="WebRTC Signaling Demo">https://godotengine.org/asset-library/asset/537</link>
</tutorials>
<methods>
<method name="close">
<return type="void" />
<description>
Immediately close the multiplayer peer returning to the state [constant CONNECTION_DISCONNECTED]. Connected peers will be dropped without emitting [signal peer_disconnected].
</description>
</method>
<method name="disconnect_peer">
<return type="void" />
<param index="0" name="peer" type="int" />
<param index="1" name="force" type="bool" default="false" />
<description>
Disconnects the given [param peer] from this host. If [param force] is [code]true[/code] the [signal peer_disconnected] signal will not be emitted for this peer.
</description>
</method>
<method name="generate_unique_id" qualifiers="const">
<return type="int" />
<description>
@ -79,7 +93,7 @@
[b]Note:[/b] The default channel ([code]0[/code]) actually works as 3 separate channels (one for each [enum TransferMode]) so that [constant TRANSFER_MODE_RELIABLE] and [constant TRANSFER_MODE_UNRELIABLE_ORDERED] does not interact with each other by default. Refer to the specific network API documentation (e.g. ENet or WebRTC) to learn how to set up channels correctly.
</member>
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" enum="MultiplayerPeer.TransferMode" default="2">
The manner in which to send packets to the [code]target_peer[/code]. See [enum TransferMode].
The manner in which to send packets to the target peer. See [enum TransferMode], and the [method set_target_peer] method.
</member>
</members>
<signals>

View file

@ -9,6 +9,20 @@
<tutorials>
</tutorials>
<methods>
<method name="_close" qualifiers="virtual">
<return type="void" />
<description>
Called when the multiplayer peer should be immediately closed (see [method MultiplayerPeer.close]).
</description>
</method>
<method name="_disconnect_peer" qualifiers="virtual">
<return type="void" />
<param index="0" name="p_peer" type="int" />
<param index="1" name="p_force" type="bool" />
<description>
Called when the connected [param p_peer] should be forcibly disconnected (see [method MultiplayerPeer.disconnect_peer]).
</description>
</method>
<method name="_get_available_packet_count" qualifiers="virtual const">
<return type="int" />
<description>

View file

@ -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">

View file

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

View file

@ -83,9 +83,7 @@ private:
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 _disconnect_inactive_peers();
void _destroy_unused(ENetPacket *p_packet);
_FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; }
@ -102,6 +100,9 @@ public:
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;
@ -122,10 +123,6 @@ 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);
Ref<ENetConnection> get_host() const;

View file

@ -22,12 +22,6 @@
Three channels will be created for reliable, unreliable, and ordered transport. The value of [code]unreliable_lifetime[/code] will be passed to the [code]maxPacketLifetime[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]).
</description>
</method>
<method name="close">
<return type="void" />
<description>
Close all the add peer connections and channels, freeing all resources.
</description>
</method>
<method name="create_client">
<return type="int" enum="Error" />
<param index="0" name="peer_id" type="int" />

View file

@ -42,7 +42,6 @@ void WebRTCMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer);
ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayerPeer::get_peer);
ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayerPeer::get_peers);
ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close);
}
void WebRTCMultiplayerPeer::set_target_peer(int p_peer_id) {
@ -352,6 +351,18 @@ void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) {
}
}
void WebRTCMultiplayerPeer::disconnect_peer(int p_peer_id, bool p_force) {
ERR_FAIL_COND(!peer_map.has(p_peer_id));
if (p_force) {
peer_map.erase(p_peer_id);
if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) {
connection_status = CONNECTION_DISCONNECTED;
}
} else {
peer_map[p_peer_id]->connection->close(); // Will be removed during next poll.
}
}
Error WebRTCMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
// Peer not available
if (next_packet_peer == 0 || !peer_map.has(next_packet_peer)) {

View file

@ -98,28 +98,29 @@ public:
bool has_peer(int p_peer_id);
Dictionary get_peer(int p_peer_id);
Dictionary get_peers();
void close();
// PacketPeer
Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
int get_available_packet_count() const override;
int get_max_packet_size() const override;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
virtual int get_available_packet_count() const override;
virtual int get_max_packet_size() const override;
// MultiplayerPeer
void set_target_peer(int p_peer_id) override;
virtual void set_target_peer(int p_peer_id) override;
int get_unique_id() const override;
int get_packet_peer() const override;
int get_packet_channel() const override;
TransferMode get_packet_mode() const override;
virtual int get_unique_id() const override;
virtual int get_packet_peer() const override;
virtual int get_packet_channel() const override;
virtual TransferMode get_packet_mode() const override;
bool is_server() const override;
bool is_server_relay_supported() const override;
virtual bool is_server() const override;
virtual bool is_server_relay_supported() const override;
void poll() override;
virtual void poll() override;
virtual void close() override;
virtual void disconnect_peer(int p_peer_id, bool p_force = false) override;
ConnectionStatus get_connection_status() const override;
virtual ConnectionStatus get_connection_status() const override;
};
#endif // WEBRTC_MULTIPLAYER_PEER_H

View file

@ -10,13 +10,6 @@
<tutorials>
</tutorials>
<methods>
<method name="close">
<return type="void" />
<description>
Closes this [MultiplayerPeer], resetting the state to [constant MultiplayerPeer.CONNECTION_CONNECTED].
[b]Note:[/b] To make sure remote peers receive a clean close prefer disconnecting clients via [method disconnect_peer].
</description>
</method>
<method name="create_client">
<return type="int" enum="Error" />
<param index="0" name="url" type="String" />
@ -37,15 +30,6 @@
Starts a new multiplayer server listening on the given [param port]. You can optionally specify a [param bind_address], and provide a [param tls_key] and [param tls_certificate] to use TLS.
</description>
</method>
<method name="disconnect_peer">
<return type="void" />
<param index="0" name="id" type="int" />
<param index="1" name="code" type="int" default="1000" />
<param index="2" name="reason" type="String" default="&quot;&quot;" />
<description>
Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more information.
</description>
</method>
<method name="get_peer" qualifiers="const">
<return type="WebSocketPeer" />
<param index="0" name="peer_id" type="int" />

View file

@ -75,12 +75,10 @@ void WebSocketMultiplayerPeer::_clear() {
void WebSocketMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_client", "url", "verify_tls", "tls_certificate"), &WebSocketMultiplayerPeer::create_client, DEFVAL(true), DEFVAL(Ref<X509Certificate>()));
ClassDB::bind_method(D_METHOD("create_server", "port", "bind_address", "tls_key", "tls_certificate"), &WebSocketMultiplayerPeer::create_server, DEFVAL("*"), DEFVAL(Ref<CryptoKey>()), DEFVAL(Ref<X509Certificate>()));
ClassDB::bind_method(D_METHOD("close"), &WebSocketMultiplayerPeer::close);
ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer);
ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketMultiplayerPeer::get_peer_address);
ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketMultiplayerPeer::get_peer_port);
ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketMultiplayerPeer::disconnect_peer, DEFVAL(1000), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_supported_protocols"), &WebSocketMultiplayerPeer::get_supported_protocols);
ClassDB::bind_method(D_METHOD("set_supported_protocols", "protocols"), &WebSocketMultiplayerPeer::set_supported_protocols);
@ -488,9 +486,15 @@ int WebSocketMultiplayerPeer::get_peer_port(int p_peer_id) const {
return peers_map[p_peer_id]->get_connected_port();
}
void WebSocketMultiplayerPeer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
void WebSocketMultiplayerPeer::disconnect_peer(int p_peer_id, bool p_force) {
ERR_FAIL_COND(!peers_map.has(p_peer_id));
peers_map[p_peer_id]->close(p_code, p_reason);
peers_map[p_peer_id]->close();
if (p_force) {
peers_map.erase(p_peer_id);
if (!is_server()) {
_clear();
}
}
}
void WebSocketMultiplayerPeer::close() {

View file

@ -102,6 +102,9 @@ public:
virtual int get_max_packet_size() const override;
virtual bool is_server() const override;
virtual void poll() override;
virtual void close() override;
virtual void disconnect_peer(int p_peer_id, bool p_force = false) override;
virtual ConnectionStatus get_connection_status() const override;
/* PacketPeer */
@ -132,8 +135,6 @@ public:
IPAddress get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
void close();
void set_max_queued_packets(int p_max_queued_packets);
int get_max_queued_packets() const;

View file

@ -94,6 +94,8 @@ void MultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
ClassDB::bind_method(D_METHOD("close"), &MultiplayerPeer::close);
ClassDB::bind_method(D_METHOD("disconnect_peer", "peer", "force"), &MultiplayerPeer::disconnect_peer, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
@ -213,6 +215,8 @@ void MultiplayerPeerExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_packet_peer);
GDVIRTUAL_BIND(_is_server);
GDVIRTUAL_BIND(_poll);
GDVIRTUAL_BIND(_close);
GDVIRTUAL_BIND(_disconnect_peer, "p_peer", "p_force");
GDVIRTUAL_BIND(_get_unique_id);
GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable");
GDVIRTUAL_BIND(_is_refusing_new_connections);

View file

@ -82,9 +82,12 @@ public:
virtual TransferMode get_packet_mode() const = 0;
virtual int get_packet_channel() const = 0;
virtual void disconnect_peer(int p_peer, bool p_force = false) = 0;
virtual bool is_server() const = 0;
virtual void poll() = 0;
virtual void close() = 0;
virtual int get_unique_id() const = 0;
@ -139,6 +142,8 @@ public:
EXBIND0RC(int, get_packet_channel);
EXBIND0RC(bool, is_server);
EXBIND0(poll);
EXBIND0(close);
EXBIND2(disconnect_peer, int, bool);
EXBIND0RC(int, get_unique_id);
EXBIND0RC(ConnectionStatus, get_connection_status);
};