From 97e763dddf6aad72b7dd05ad52dfe16cecc1f221 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 12 Jul 2021 16:26:37 +0200 Subject: [Net] Rename WebRTCMultiplayer to WebRTCMultiplayerPeer. --- modules/webrtc/config.py | 2 +- modules/webrtc/doc_classes/WebRTCMultiplayer.xml | 89 ----- .../webrtc/doc_classes/WebRTCMultiplayerPeer.xml | 89 +++++ modules/webrtc/register_types.cpp | 4 +- modules/webrtc/webrtc_multiplayer.cpp | 392 --------------------- modules/webrtc/webrtc_multiplayer.h | 115 ------ modules/webrtc/webrtc_multiplayer_peer.cpp | 392 +++++++++++++++++++++ modules/webrtc/webrtc_multiplayer_peer.h | 115 ++++++ 8 files changed, 599 insertions(+), 599 deletions(-) delete mode 100644 modules/webrtc/doc_classes/WebRTCMultiplayer.xml create mode 100644 modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml delete mode 100644 modules/webrtc/webrtc_multiplayer.cpp delete mode 100644 modules/webrtc/webrtc_multiplayer.h create mode 100644 modules/webrtc/webrtc_multiplayer_peer.cpp create mode 100644 modules/webrtc/webrtc_multiplayer_peer.h diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py index 0a075ccef1..3281415f38 100644 --- a/modules/webrtc/config.py +++ b/modules/webrtc/config.py @@ -10,7 +10,7 @@ def get_doc_classes(): return [ "WebRTCPeerConnection", "WebRTCDataChannel", - "WebRTCMultiplayer", + "WebRTCMultiplayerPeer", ] diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml deleted file mode 100644 index 99c7236bfc..0000000000 --- a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI]. - - - This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.network_peer]. - You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. - [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer]. - - - - - - - - - - - - - - - Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW]. - 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]). - - - - - - - Close all the add peer connections and channels, freeing all resources. - - - - - - - - - Return a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open). - - - - - - - Returns a dictionary which keys are the peer ids and values the peer representation as in [method get_peer]. - - - - - - - - - Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though). - - - - - - - - - - - Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647). - If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted. - If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED]. - - - - - - - - - Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted. - - - - - - - - - - diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml new file mode 100644 index 0000000000..26c5bfa6ce --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -0,0 +1,89 @@ + + + + A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI]. + + + This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.network_peer]. + You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. + [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer]. + + + + + + + + + + + + + + + Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW]. + 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]). + + + + + + + Close all the add peer connections and channels, freeing all resources. + + + + + + + + + Return a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open). + + + + + + + Returns a dictionary which keys are the peer ids and values the peer representation as in [method get_peer]. + + + + + + + + + Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though). + + + + + + + + + + + Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647). + If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted. + If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED]. + + + + + + + + + Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted. + + + + + + + + + + diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp index ecfaed9089..fcdf04fc2b 100644 --- a/modules/webrtc/register_types.cpp +++ b/modules/webrtc/register_types.cpp @@ -41,7 +41,7 @@ #include "webrtc_data_channel_gdnative.h" #include "webrtc_peer_connection_gdnative.h" #endif -#include "webrtc_multiplayer.h" +#include "webrtc_multiplayer_peer.h" void register_webrtc_types() { #define _SET_HINT(NAME, _VAL_, _MAX_) \ @@ -62,7 +62,7 @@ void register_webrtc_types() { ClassDB::register_class(); #endif ClassDB::register_virtual_class(); - ClassDB::register_class(); + ClassDB::register_class(); } void unregister_webrtc_types() {} diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer.cpp deleted file mode 100644 index 019ac890b8..0000000000 --- a/modules/webrtc/webrtc_multiplayer.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/*************************************************************************/ -/* webrtc_multiplayer.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "webrtc_multiplayer.h" - -#include "core/io/marshalls.h" -#include "core/os/os.h" - -void WebRTCMultiplayer::_bind_methods() { - ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayer::initialize, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayer::add_peer, DEFVAL(1)); - ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayer::remove_peer); - ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayer::has_peer); - ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayer::get_peer); - ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayer::get_peers); - ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayer::close); -} - -void WebRTCMultiplayer::set_transfer_mode(TransferMode p_mode) { - transfer_mode = p_mode; -} - -MultiplayerPeer::TransferMode WebRTCMultiplayer::get_transfer_mode() const { - return transfer_mode; -} - -void WebRTCMultiplayer::set_target_peer(int p_peer_id) { - target_peer = p_peer_id; -} - -/* Returns the ID of the MultiplayerPeer who sent the most recent packet: */ -int WebRTCMultiplayer::get_packet_peer() const { - return next_packet_peer; -} - -bool WebRTCMultiplayer::is_server() const { - return unique_id == TARGET_PEER_SERVER; -} - -void WebRTCMultiplayer::poll() { - if (peer_map.size() == 0) { - return; - } - - List remove; - List add; - for (Map>::Element *E = peer_map.front(); E; E = E->next()) { - Ref peer = E->get(); - peer->connection->poll(); - // Check peer state - switch (peer->connection->get_connection_state()) { - case WebRTCPeerConnection::STATE_NEW: - case WebRTCPeerConnection::STATE_CONNECTING: - // Go to next peer, not ready yet. - continue; - case WebRTCPeerConnection::STATE_CONNECTED: - // Good to go, go ahead and check channel state. - break; - default: - // Peer is closed or in error state. Got to next peer. - remove.push_back(E->key()); - continue; - } - // Check channels state - int ready = 0; - for (List>::Element *C = peer->channels.front(); C && C->get().is_valid(); C = C->next()) { - Ref ch = C->get(); - switch (ch->get_ready_state()) { - case WebRTCDataChannel::STATE_CONNECTING: - continue; - case WebRTCDataChannel::STATE_OPEN: - ready++; - continue; - default: - // Channel was closed or in error state, remove peer id. - remove.push_back(E->key()); - } - // We got a closed channel break out, the peer will be removed. - break; - } - // This peer has newly connected, and all channels are now open. - if (ready == peer->channels.size() && !peer->connected) { - peer->connected = true; - add.push_back(E->key()); - } - } - // Remove disconnected peers - for (List::Element *E = remove.front(); E; E = E->next()) { - remove_peer(E->get()); - if (next_packet_peer == E->get()) { - next_packet_peer = 0; - } - } - // Signal newly connected peers - for (List::Element *E = add.front(); E; E = E->next()) { - // Already connected to server: simply notify new peer. - // NOTE: Mesh is always connected. - if (connection_status == CONNECTION_CONNECTED) { - emit_signal("peer_connected", E->get()); - } - - // Server emulation mode suppresses peer_conencted until server connects. - if (server_compat && E->get() == TARGET_PEER_SERVER) { - // Server connected. - connection_status = CONNECTION_CONNECTED; - emit_signal("peer_connected", TARGET_PEER_SERVER); - emit_signal("connection_succeeded"); - // Notify of all previously connected peers - for (Map>::Element *F = peer_map.front(); F; F = F->next()) { - if (F->key() != 1 && F->get()->connected) { - emit_signal("peer_connected", F->key()); - } - } - break; // Because we already notified of all newly added peers. - } - } - // Fetch next packet - if (next_packet_peer == 0) { - _find_next_peer(); - } -} - -void WebRTCMultiplayer::_find_next_peer() { - Map>::Element *E = peer_map.find(next_packet_peer); - if (E) { - E = E->next(); - } - // After last. - while (E) { - for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { - if (F->get()->get_available_packet_count()) { - next_packet_peer = E->key(); - return; - } - } - E = E->next(); - } - E = peer_map.front(); - // Before last - while (E) { - for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { - if (F->get()->get_available_packet_count()) { - next_packet_peer = E->key(); - return; - } - } - if (E->key() == (int)next_packet_peer) { - break; - } - E = E->next(); - } - // No packet found - next_packet_peer = 0; -} - -void WebRTCMultiplayer::set_refuse_new_connections(bool p_enable) { - refuse_connections = p_enable; -} - -bool WebRTCMultiplayer::is_refusing_new_connections() const { - return refuse_connections; -} - -MultiplayerPeer::ConnectionStatus WebRTCMultiplayer::get_connection_status() const { - return connection_status; -} - -Error WebRTCMultiplayer::initialize(int p_self_id, bool p_server_compat) { - ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER); - unique_id = p_self_id; - server_compat = p_server_compat; - - // Mesh and server are always connected - if (!server_compat || p_self_id == 1) { - connection_status = CONNECTION_CONNECTED; - } else { - connection_status = CONNECTION_CONNECTING; - } - return OK; -} - -int WebRTCMultiplayer::get_unique_id() const { - ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1); - return unique_id; -} - -void WebRTCMultiplayer::_peer_to_dict(Ref p_connected_peer, Dictionary &r_dict) { - Array channels; - for (List>::Element *F = p_connected_peer->channels.front(); F; F = F->next()) { - channels.push_back(F->get()); - } - r_dict["connection"] = p_connected_peer->connection; - r_dict["connected"] = p_connected_peer->connected; - r_dict["channels"] = channels; -} - -bool WebRTCMultiplayer::has_peer(int p_peer_id) { - return peer_map.has(p_peer_id); -} - -Dictionary WebRTCMultiplayer::get_peer(int p_peer_id) { - ERR_FAIL_COND_V(!peer_map.has(p_peer_id), Dictionary()); - Dictionary out; - _peer_to_dict(peer_map[p_peer_id], out); - return out; -} - -Dictionary WebRTCMultiplayer::get_peers() { - Dictionary out; - for (Map>::Element *E = peer_map.front(); E; E = E->next()) { - Dictionary d; - _peer_to_dict(E->get(), d); - out[E->key()] = d; - } - return out; -} - -Error WebRTCMultiplayer::add_peer(Ref p_peer, int p_peer_id, int p_unreliable_lifetime) { - ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(refuse_connections, ERR_UNAUTHORIZED); - // Peer must be valid, and in new state (to create data channels) - ERR_FAIL_COND_V(!p_peer.is_valid(), ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER); - - Ref peer = memnew(ConnectedPeer); - peer->connection = p_peer; - - // Initialize data channels - Dictionary cfg; - cfg["negotiated"] = true; - cfg["ordered"] = true; - - cfg["id"] = 1; - peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg); - ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED); - - cfg["id"] = 2; - cfg["maxPacketLifetime"] = p_unreliable_lifetime; - peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg); - ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED); - - cfg["id"] = 3; - cfg["ordered"] = false; - peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg); - ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED); - - peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map - - return OK; -} - -void WebRTCMultiplayer::remove_peer(int p_peer_id) { - ERR_FAIL_COND(!peer_map.has(p_peer_id)); - Ref peer = peer_map[p_peer_id]; - peer_map.erase(p_peer_id); - if (peer->connected) { - peer->connected = false; - emit_signal("peer_disconnected", p_peer_id); - if (server_compat && p_peer_id == TARGET_PEER_SERVER) { - emit_signal("server_disconnected"); - connection_status = CONNECTION_DISCONNECTED; - } - } -} - -Error WebRTCMultiplayer::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)) { - _find_next_peer(); - ERR_FAIL_V(ERR_UNAVAILABLE); - } - for (List>::Element *E = peer_map[next_packet_peer]->channels.front(); E; E = E->next()) { - if (E->get()->get_available_packet_count()) { - Error err = E->get()->get_packet(r_buffer, r_buffer_size); - _find_next_peer(); - return err; - } - } - // Channels for that peer were empty. Bug? - _find_next_peer(); - ERR_FAIL_V(ERR_BUG); -} - -Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED); - - int ch = CH_RELIABLE; - switch (transfer_mode) { - case TRANSFER_MODE_RELIABLE: - ch = CH_RELIABLE; - break; - case TRANSFER_MODE_UNRELIABLE_ORDERED: - ch = CH_ORDERED; - break; - case TRANSFER_MODE_UNRELIABLE: - ch = CH_UNRELIABLE; - break; - } - - Map>::Element *E = nullptr; - - if (target_peer > 0) { - E = peer_map.find(target_peer); - ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + "."); - - ERR_FAIL_COND_V(E->value()->channels.size() <= ch, ERR_BUG); - ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG); - return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); - - } else { - int exclude = -target_peer; - - for (Map>::Element *F = peer_map.front(); F; F = F->next()) { - // Exclude packet. If target_peer == 0 then don't exclude any packets - if (target_peer != 0 && F->key() == exclude) { - continue; - } - - ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid()); - F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); - } - } - return OK; -} - -int WebRTCMultiplayer::get_available_packet_count() const { - if (next_packet_peer == 0) { - return 0; // To be sure next call to get_packet works if size > 0 . - } - int size = 0; - for (Map>::Element *E = peer_map.front(); E; E = E->next()) { - for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { - size += F->get()->get_available_packet_count(); - } - } - return size; -} - -int WebRTCMultiplayer::get_max_packet_size() const { - return 1200; -} - -void WebRTCMultiplayer::close() { - peer_map.clear(); - unique_id = 0; - next_packet_peer = 0; - target_peer = 0; - connection_status = CONNECTION_DISCONNECTED; -} - -WebRTCMultiplayer::WebRTCMultiplayer() { - unique_id = 0; - next_packet_peer = 0; - target_peer = 0; - client_count = 0; - transfer_mode = TRANSFER_MODE_RELIABLE; - refuse_connections = false; - connection_status = CONNECTION_DISCONNECTED; - server_compat = false; -} - -WebRTCMultiplayer::~WebRTCMultiplayer() { - close(); -} diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h deleted file mode 100644 index 5e8b6be24e..0000000000 --- a/modules/webrtc/webrtc_multiplayer.h +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************/ -/* webrtc_multiplayer.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef WEBRTC_MULTIPLAYER_H -#define WEBRTC_MULTIPLAYER_H - -#include "core/io/multiplayer_peer.h" -#include "webrtc_peer_connection.h" - -class WebRTCMultiplayer : public MultiplayerPeer { - GDCLASS(WebRTCMultiplayer, MultiplayerPeer); - -protected: - static void _bind_methods(); - -private: - enum { - CH_RELIABLE = 0, - CH_ORDERED = 1, - CH_UNRELIABLE = 2, - CH_RESERVED_MAX = 3 - }; - - class ConnectedPeer : public RefCounted { - public: - Ref connection; - List> channels; - bool connected; - - ConnectedPeer() { - connected = false; - for (int i = 0; i < CH_RESERVED_MAX; i++) { - channels.push_front(Ref()); - } - } - }; - - uint32_t unique_id; - int target_peer; - int client_count; - bool refuse_connections; - ConnectionStatus connection_status; - TransferMode transfer_mode; - int next_packet_peer; - bool server_compat; - - Map> peer_map; - - void _peer_to_dict(Ref p_connected_peer, Dictionary &r_dict); - void _find_next_peer(); - -public: - WebRTCMultiplayer(); - ~WebRTCMultiplayer(); - - Error initialize(int p_self_id, bool p_server_compat = false); - Error add_peer(Ref p_peer, int p_peer_id, int p_unreliable_lifetime = 1); - void remove_peer(int p_peer_id); - 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; - - // MultiplayerPeer - void set_transfer_mode(TransferMode p_mode) override; - TransferMode get_transfer_mode() const override; - void set_target_peer(int p_peer_id) override; - - int get_unique_id() const override; - int get_packet_peer() const override; - - bool is_server() const override; - - void poll() override; - - void set_refuse_new_connections(bool p_enable) override; - bool is_refusing_new_connections() const override; - - ConnectionStatus get_connection_status() const override; -}; - -#endif // WEBRTC_MULTIPLAYER_H diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp new file mode 100644 index 0000000000..ac75f9e860 --- /dev/null +++ b/modules/webrtc/webrtc_multiplayer_peer.cpp @@ -0,0 +1,392 @@ +/*************************************************************************/ +/* webrtc_multiplayer_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webrtc_multiplayer_peer.h" + +#include "core/io/marshalls.h" +#include "core/os/os.h" + +void WebRTCMultiplayerPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1)); + ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer); + 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_transfer_mode(TransferMode p_mode) { + transfer_mode = p_mode; +} + +MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_transfer_mode() const { + return transfer_mode; +} + +void WebRTCMultiplayerPeer::set_target_peer(int p_peer_id) { + target_peer = p_peer_id; +} + +/* Returns the ID of the MultiplayerPeer who sent the most recent packet: */ +int WebRTCMultiplayerPeer::get_packet_peer() const { + return next_packet_peer; +} + +bool WebRTCMultiplayerPeer::is_server() const { + return unique_id == TARGET_PEER_SERVER; +} + +void WebRTCMultiplayerPeer::poll() { + if (peer_map.size() == 0) { + return; + } + + List remove; + List add; + for (Map>::Element *E = peer_map.front(); E; E = E->next()) { + Ref peer = E->get(); + peer->connection->poll(); + // Check peer state + switch (peer->connection->get_connection_state()) { + case WebRTCPeerConnection::STATE_NEW: + case WebRTCPeerConnection::STATE_CONNECTING: + // Go to next peer, not ready yet. + continue; + case WebRTCPeerConnection::STATE_CONNECTED: + // Good to go, go ahead and check channel state. + break; + default: + // Peer is closed or in error state. Got to next peer. + remove.push_back(E->key()); + continue; + } + // Check channels state + int ready = 0; + for (List>::Element *C = peer->channels.front(); C && C->get().is_valid(); C = C->next()) { + Ref ch = C->get(); + switch (ch->get_ready_state()) { + case WebRTCDataChannel::STATE_CONNECTING: + continue; + case WebRTCDataChannel::STATE_OPEN: + ready++; + continue; + default: + // Channel was closed or in error state, remove peer id. + remove.push_back(E->key()); + } + // We got a closed channel break out, the peer will be removed. + break; + } + // This peer has newly connected, and all channels are now open. + if (ready == peer->channels.size() && !peer->connected) { + peer->connected = true; + add.push_back(E->key()); + } + } + // Remove disconnected peers + for (List::Element *E = remove.front(); E; E = E->next()) { + remove_peer(E->get()); + if (next_packet_peer == E->get()) { + next_packet_peer = 0; + } + } + // Signal newly connected peers + for (List::Element *E = add.front(); E; E = E->next()) { + // Already connected to server: simply notify new peer. + // NOTE: Mesh is always connected. + if (connection_status == CONNECTION_CONNECTED) { + emit_signal("peer_connected", E->get()); + } + + // Server emulation mode suppresses peer_conencted until server connects. + if (server_compat && E->get() == TARGET_PEER_SERVER) { + // Server connected. + connection_status = CONNECTION_CONNECTED; + emit_signal("peer_connected", TARGET_PEER_SERVER); + emit_signal("connection_succeeded"); + // Notify of all previously connected peers + for (Map>::Element *F = peer_map.front(); F; F = F->next()) { + if (F->key() != 1 && F->get()->connected) { + emit_signal("peer_connected", F->key()); + } + } + break; // Because we already notified of all newly added peers. + } + } + // Fetch next packet + if (next_packet_peer == 0) { + _find_next_peer(); + } +} + +void WebRTCMultiplayerPeer::_find_next_peer() { + Map>::Element *E = peer_map.find(next_packet_peer); + if (E) { + E = E->next(); + } + // After last. + while (E) { + for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { + if (F->get()->get_available_packet_count()) { + next_packet_peer = E->key(); + return; + } + } + E = E->next(); + } + E = peer_map.front(); + // Before last + while (E) { + for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { + if (F->get()->get_available_packet_count()) { + next_packet_peer = E->key(); + return; + } + } + if (E->key() == (int)next_packet_peer) { + break; + } + E = E->next(); + } + // No packet found + next_packet_peer = 0; +} + +void WebRTCMultiplayerPeer::set_refuse_new_connections(bool p_enable) { + refuse_connections = p_enable; +} + +bool WebRTCMultiplayerPeer::is_refusing_new_connections() const { + return refuse_connections; +} + +MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status() const { + return connection_status; +} + +Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat) { + ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER); + unique_id = p_self_id; + server_compat = p_server_compat; + + // Mesh and server are always connected + if (!server_compat || p_self_id == 1) { + connection_status = CONNECTION_CONNECTED; + } else { + connection_status = CONNECTION_CONNECTING; + } + return OK; +} + +int WebRTCMultiplayerPeer::get_unique_id() const { + ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1); + return unique_id; +} + +void WebRTCMultiplayerPeer::_peer_to_dict(Ref p_connected_peer, Dictionary &r_dict) { + Array channels; + for (List>::Element *F = p_connected_peer->channels.front(); F; F = F->next()) { + channels.push_back(F->get()); + } + r_dict["connection"] = p_connected_peer->connection; + r_dict["connected"] = p_connected_peer->connected; + r_dict["channels"] = channels; +} + +bool WebRTCMultiplayerPeer::has_peer(int p_peer_id) { + return peer_map.has(p_peer_id); +} + +Dictionary WebRTCMultiplayerPeer::get_peer(int p_peer_id) { + ERR_FAIL_COND_V(!peer_map.has(p_peer_id), Dictionary()); + Dictionary out; + _peer_to_dict(peer_map[p_peer_id], out); + return out; +} + +Dictionary WebRTCMultiplayerPeer::get_peers() { + Dictionary out; + for (Map>::Element *E = peer_map.front(); E; E = E->next()) { + Dictionary d; + _peer_to_dict(E->get(), d); + out[E->key()] = d; + } + return out; +} + +Error WebRTCMultiplayerPeer::add_peer(Ref p_peer, int p_peer_id, int p_unreliable_lifetime) { + ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(refuse_connections, ERR_UNAUTHORIZED); + // Peer must be valid, and in new state (to create data channels) + ERR_FAIL_COND_V(!p_peer.is_valid(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER); + + Ref peer = memnew(ConnectedPeer); + peer->connection = p_peer; + + // Initialize data channels + Dictionary cfg; + cfg["negotiated"] = true; + cfg["ordered"] = true; + + cfg["id"] = 1; + peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED); + + cfg["id"] = 2; + cfg["maxPacketLifetime"] = p_unreliable_lifetime; + peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED); + + cfg["id"] = 3; + cfg["ordered"] = false; + peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED); + + peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map + + return OK; +} + +void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) { + ERR_FAIL_COND(!peer_map.has(p_peer_id)); + Ref peer = peer_map[p_peer_id]; + peer_map.erase(p_peer_id); + if (peer->connected) { + peer->connected = false; + emit_signal("peer_disconnected", p_peer_id); + if (server_compat && p_peer_id == TARGET_PEER_SERVER) { + emit_signal("server_disconnected"); + connection_status = CONNECTION_DISCONNECTED; + } + } +} + +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)) { + _find_next_peer(); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + for (List>::Element *E = peer_map[next_packet_peer]->channels.front(); E; E = E->next()) { + if (E->get()->get_available_packet_count()) { + Error err = E->get()->get_packet(r_buffer, r_buffer_size); + _find_next_peer(); + return err; + } + } + // Channels for that peer were empty. Bug? + _find_next_peer(); + ERR_FAIL_V(ERR_BUG); +} + +Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED); + + int ch = CH_RELIABLE; + switch (transfer_mode) { + case TRANSFER_MODE_RELIABLE: + ch = CH_RELIABLE; + break; + case TRANSFER_MODE_UNRELIABLE_ORDERED: + ch = CH_ORDERED; + break; + case TRANSFER_MODE_UNRELIABLE: + ch = CH_UNRELIABLE; + break; + } + + Map>::Element *E = nullptr; + + if (target_peer > 0) { + E = peer_map.find(target_peer); + ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + "."); + + ERR_FAIL_COND_V(E->value()->channels.size() <= ch, ERR_BUG); + ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG); + return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); + + } else { + int exclude = -target_peer; + + for (Map>::Element *F = peer_map.front(); F; F = F->next()) { + // Exclude packet. If target_peer == 0 then don't exclude any packets + if (target_peer != 0 && F->key() == exclude) { + continue; + } + + ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid()); + F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); + } + } + return OK; +} + +int WebRTCMultiplayerPeer::get_available_packet_count() const { + if (next_packet_peer == 0) { + return 0; // To be sure next call to get_packet works if size > 0 . + } + int size = 0; + for (Map>::Element *E = peer_map.front(); E; E = E->next()) { + for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { + size += F->get()->get_available_packet_count(); + } + } + return size; +} + +int WebRTCMultiplayerPeer::get_max_packet_size() const { + return 1200; +} + +void WebRTCMultiplayerPeer::close() { + peer_map.clear(); + unique_id = 0; + next_packet_peer = 0; + target_peer = 0; + connection_status = CONNECTION_DISCONNECTED; +} + +WebRTCMultiplayerPeer::WebRTCMultiplayerPeer() { + unique_id = 0; + next_packet_peer = 0; + target_peer = 0; + client_count = 0; + transfer_mode = TRANSFER_MODE_RELIABLE; + refuse_connections = false; + connection_status = CONNECTION_DISCONNECTED; + server_compat = false; +} + +WebRTCMultiplayerPeer::~WebRTCMultiplayerPeer() { + close(); +} diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h new file mode 100644 index 0000000000..1d9387b6dc --- /dev/null +++ b/modules/webrtc/webrtc_multiplayer_peer.h @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* webrtc_multiplayer_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBRTC_MULTIPLAYER_H +#define WEBRTC_MULTIPLAYER_H + +#include "core/io/multiplayer_peer.h" +#include "webrtc_peer_connection.h" + +class WebRTCMultiplayerPeer : public MultiplayerPeer { + GDCLASS(WebRTCMultiplayerPeer, MultiplayerPeer); + +protected: + static void _bind_methods(); + +private: + enum { + CH_RELIABLE = 0, + CH_ORDERED = 1, + CH_UNRELIABLE = 2, + CH_RESERVED_MAX = 3 + }; + + class ConnectedPeer : public RefCounted { + public: + Ref connection; + List> channels; + bool connected; + + ConnectedPeer() { + connected = false; + for (int i = 0; i < CH_RESERVED_MAX; i++) { + channels.push_front(Ref()); + } + } + }; + + uint32_t unique_id; + int target_peer; + int client_count; + bool refuse_connections; + ConnectionStatus connection_status; + TransferMode transfer_mode; + int next_packet_peer; + bool server_compat; + + Map> peer_map; + + void _peer_to_dict(Ref p_connected_peer, Dictionary &r_dict); + void _find_next_peer(); + +public: + WebRTCMultiplayerPeer(); + ~WebRTCMultiplayerPeer(); + + Error initialize(int p_self_id, bool p_server_compat = false); + Error add_peer(Ref p_peer, int p_peer_id, int p_unreliable_lifetime = 1); + void remove_peer(int p_peer_id); + 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; + + // MultiplayerPeer + void set_transfer_mode(TransferMode p_mode) override; + TransferMode get_transfer_mode() const override; + void set_target_peer(int p_peer_id) override; + + int get_unique_id() const override; + int get_packet_peer() const override; + + bool is_server() const override; + + void poll() override; + + void set_refuse_new_connections(bool p_enable) override; + bool is_refusing_new_connections() const override; + + ConnectionStatus get_connection_status() const override; +}; + +#endif // WEBRTC_MULTIPLAYER_H -- cgit v1.2.3