[Net] Implement RPC channels in MultiplayerAPI.

This commit is contained in:
Fabio Alessandrelli 2021-07-27 12:06:48 +02:00
parent c27ef1565f
commit 2cf39b97ae
16 changed files with 157 additions and 62 deletions

View File

@ -478,6 +478,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
packet.write[1] = valid_rpc_checksum; packet.write[1] = valid_rpc_checksum;
encode_cstring(pname.get_data(), &packet.write[2]); encode_cstring(pname.get_data(), &packet.write[2]);
network_peer->set_transfer_channel(0);
network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->set_target_peer(p_from); network_peer->set_target_peer(p_from);
network_peer->put_packet(packet.ptr(), packet.size()); network_peer->put_packet(packet.ptr(), packet.size());
@ -557,6 +558,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC
for (int &E : peers_to_add) { for (int &E : peers_to_add) {
network_peer->set_target_peer(E); // To all of you. network_peer->set_target_peer(E); // To all of you.
network_peer->set_transfer_channel(0);
network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->put_packet(packet.ptr(), packet.size()); network_peer->put_packet(packet.ptr(), packet.size());
@ -858,6 +860,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const
#endif #endif
// Take chance and set transfer mode, since all send methods will use it. // Take chance and set transfer mode, since all send methods will use it.
network_peer->set_transfer_channel(p_config.channel);
network_peer->set_transfer_mode(p_config.transfer_mode); network_peer->set_transfer_mode(p_config.transfer_mode);
if (has_all_peers) { if (has_all_peers) {
@ -996,7 +999,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
} }
Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode) { Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) {
ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet.");
ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active.");
ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected.");
@ -1007,6 +1010,7 @@ Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPe
memcpy(&packet_cache.write[1], &r[0], p_data.size()); memcpy(&packet_cache.write[1], &r[0], p_data.size());
network_peer->set_target_peer(p_to); network_peer->set_target_peer(p_to);
network_peer->set_transfer_channel(p_channel);
network_peer->set_transfer_mode(p_mode); network_peer->set_transfer_mode(p_mode);
return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
@ -1066,7 +1070,7 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
void MultiplayerAPI::_bind_methods() { void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node);
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE)); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer);
ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id);

View File

@ -132,7 +132,7 @@ public:
Node *get_root_node(); Node *get_root_node();
void set_network_peer(const Ref<MultiplayerPeer> &p_peer); void set_network_peer(const Ref<MultiplayerPeer> &p_peer);
Ref<MultiplayerPeer> get_network_peer() const; Ref<MultiplayerPeer> get_network_peer() const;
Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE); Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0);
// Called by Node.rpc // Called by Node.rpc
void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);

View File

@ -54,6 +54,8 @@ uint32_t MultiplayerPeer::generate_unique_id() const {
} }
void MultiplayerPeer::_bind_methods() { void MultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);
ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel);
ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode); ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode); ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer); ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
@ -71,6 +73,7 @@ void MultiplayerPeer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE); BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED); BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);

View File

@ -56,6 +56,8 @@ public:
CONNECTION_CONNECTED, CONNECTION_CONNECTED,
}; };
virtual void set_transfer_channel(int p_channel) = 0;
virtual int get_transfer_channel() const = 0;
virtual void set_transfer_mode(TransferMode p_mode) = 0; virtual void set_transfer_mode(TransferMode p_mode) = 0;
virtual TransferMode get_transfer_mode() const = 0; virtual TransferMode get_transfer_mode() const = 0;
virtual void set_target_peer(int p_peer_id) = 0; virtual void set_target_peer(int p_peer_id) = 0;

View File

@ -61,6 +61,7 @@
<argument index="0" name="bytes" type="PackedByteArray" /> <argument index="0" name="bytes" type="PackedByteArray" />
<argument index="1" name="id" type="int" default="0" /> <argument index="1" name="id" type="int" default="0" />
<argument index="2" name="mode" type="int" enum="MultiplayerPeer.TransferMode" default="2" /> <argument index="2" name="mode" type="int" enum="MultiplayerPeer.TransferMode" default="2" />
<argument index="3" name="channel" type="int" default="0" />
<description> <description>
Sends the given raw [code]bytes[/code] to a specific peer identified by [code]id[/code] (see [method MultiplayerPeer.set_target_peer]). Default ID is [code]0[/code], i.e. broadcast to all peers. Sends the given raw [code]bytes[/code] to a specific peer identified by [code]id[/code] (see [method MultiplayerPeer.set_target_peer]). Default ID is [code]0[/code], i.e. broadcast to all peers.
</description> </description>

View File

@ -55,6 +55,10 @@
<member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="true"> <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="true">
If [code]true[/code], this [MultiplayerPeer] refuses new connections. If [code]true[/code], this [MultiplayerPeer] refuses new connections.
</member> </member>
<member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel" default="0">
The channel to use to send packets. Many network APIs such as ENet and WebRTC allow the creation of multiple independent channels which behaves, in a way, like separate connections. This means that reliable data will only block delivery of other packets on that channel, and ordering will only be in respect to the channel the packet is being sent on. Using different channels to send [b]different and independent[/b] state updates is a common way to optimize network usage and decrease latency in fast-paced games.
[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="0"> <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" enum="MultiplayerPeer.TransferMode" default="0">
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 [code]target_peer[/code]. See [enum TransferMode].
</member> </member>

View File

@ -33,6 +33,14 @@
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/os/os.h" #include "core/os/os.h"
void ENetMultiplayerPeer::set_transfer_channel(int p_channel) {
transfer_channel = p_channel;
}
int ENetMultiplayerPeer::get_transfer_channel() const {
return transfer_channel;
}
void ENetMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { void ENetMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
transfer_mode = p_mode; transfer_mode = p_mode;
} }
@ -441,7 +449,9 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
int packet_flags = 0; int packet_flags = 0;
int channel = SYSCH_RELIABLE; int channel = SYSCH_RELIABLE;
if (transfer_channel > 0) {
channel = SYSCH_MAX + transfer_channel - 1;
} else {
switch (transfer_mode) { switch (transfer_mode) {
case TRANSFER_MODE_UNRELIABLE: { case TRANSFER_MODE_UNRELIABLE: {
packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
@ -456,6 +466,7 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
channel = SYSCH_RELIABLE; channel = SYSCH_RELIABLE;
} break; } break;
} }
}
ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags); ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags);
encode_uint32(unique_id, &packet->data[0]); // Source ID encode_uint32(unique_id, &packet->data[0]); // Source ID

View File

@ -47,10 +47,10 @@ private:
}; };
enum { enum {
SYSCH_CONFIG, SYSCH_CONFIG = 0,
SYSCH_RELIABLE, SYSCH_RELIABLE = 1,
SYSCH_UNRELIABLE, SYSCH_UNRELIABLE = 2,
SYSCH_MAX SYSCH_MAX = 3
}; };
enum Mode { enum Mode {
@ -65,6 +65,7 @@ private:
uint32_t unique_id = 0; uint32_t unique_id = 0;
int target_peer = 0; int target_peer = 0;
int transfer_channel = 0;
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
bool refuse_connections = false; bool refuse_connections = false;
@ -100,6 +101,9 @@ protected:
static void _bind_methods(); static void _bind_methods();
public: public:
virtual void set_transfer_channel(int p_channel) override;
virtual int get_transfer_channel() const override;
virtual void set_transfer_mode(TransferMode p_mode) override; virtual void set_transfer_mode(TransferMode p_mode) override;
virtual TransferMode get_transfer_mode() const override; virtual TransferMode get_transfer_mode() const override;
virtual void set_target_peer(int p_peer) override; virtual void set_target_peer(int p_peer) override;

View File

@ -91,6 +91,8 @@ typedef struct {
godot_int (*get_max_packet_size)(const void *); godot_int (*get_max_packet_size)(const void *);
/* This is MultiplayerPeer */ /* This is MultiplayerPeer */
void (*set_transfer_channel)(void *, godot_int);
godot_int (*get_transfer_channel)(void *);
void (*set_transfer_mode)(void *, godot_int); void (*set_transfer_mode)(void *, godot_int);
godot_int (*get_transfer_mode)(const void *); godot_int (*get_transfer_mode)(const void *);
// 0 = broadcast, 1 = server, <0 = all but abs(value) // 0 = broadcast, 1 = server, <0 = all but abs(value)

View File

@ -62,6 +62,16 @@ int MultiplayerPeerGDNative::get_available_packet_count() const {
} }
/* MultiplayerPeer */ /* MultiplayerPeer */
void MultiplayerPeerGDNative::set_transfer_channel(int p_channel) {
ERR_FAIL_COND(interface == nullptr);
return interface->set_transfer_channel(interface->data, p_channel);
}
int MultiplayerPeerGDNative::get_transfer_channel() const {
ERR_FAIL_COND_V(interface == nullptr, 0);
return interface->get_transfer_channel(interface->data);
}
void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) { void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
ERR_FAIL_COND(interface == nullptr); ERR_FAIL_COND(interface == nullptr);
interface->set_transfer_mode(interface->data, (godot_int)p_mode); interface->set_transfer_mode(interface->data, (godot_int)p_mode);
@ -113,6 +123,7 @@ MultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status
} }
void MultiplayerPeerGDNative::_bind_methods() { void MultiplayerPeerGDNative::_bind_methods() {
ADD_PROPERTY_DEFAULT("transfer_channel", 0);
ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_UNRELIABLE); ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_UNRELIABLE);
ADD_PROPERTY_DEFAULT("refuse_new_connections", true); ADD_PROPERTY_DEFAULT("refuse_new_connections", true);
} }

View File

@ -56,6 +56,8 @@ public:
virtual int get_available_packet_count() const override; virtual int get_available_packet_count() const override;
/* Specific to MultiplayerPeer */ /* Specific to MultiplayerPeer */
virtual void set_transfer_channel(int p_channel) override;
virtual int get_transfer_channel() const override;
virtual void set_transfer_mode(TransferMode p_mode) override; virtual void set_transfer_mode(TransferMode p_mode) override;
virtual TransferMode get_transfer_mode() const override; virtual TransferMode get_transfer_mode() const override;
virtual void set_target_peer(int p_peer_id) override; virtual void set_target_peer(int p_peer_id) override;

View File

@ -51,10 +51,12 @@
<return type="int" enum="Error" /> <return type="int" enum="Error" />
<argument index="0" name="peer_id" type="int" /> <argument index="0" name="peer_id" type="int" />
<argument index="1" name="server_compatibility" type="bool" default="false" /> <argument index="1" name="server_compatibility" type="bool" default="false" />
<argument index="2" name="channels_config" type="Array" default="[]" />
<description> <description>
Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647). 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]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]. 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].
You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description> </description>
</method> </method>
<method name="remove_peer"> <method name="remove_peer">

View File

@ -34,7 +34,7 @@
#include "core/os/os.h" #include "core/os/os.h"
void WebRTCMultiplayerPeer::_bind_methods() { void WebRTCMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false)); ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility", "channels_config"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false), DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1)); 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("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer);
ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer); ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer);
@ -43,6 +43,14 @@ void WebRTCMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close); ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close);
} }
void WebRTCMultiplayerPeer::set_transfer_channel(int p_channel) {
transfer_channel = p_channel;
}
int WebRTCMultiplayerPeer::get_transfer_channel() const {
return transfer_channel;
}
void WebRTCMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { void WebRTCMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
transfer_mode = p_mode; transfer_mode = p_mode;
} }
@ -192,8 +200,34 @@ MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status()
return connection_status; return connection_status;
} }
Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat) { Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Array p_channels_config) {
ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
channels_config.clear();
for (int i = 0; i < p_channels_config.size(); i++) {
ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'");
int mode = p_channels_config[i].operator int();
// Initialize data channel configurations.
Dictionary cfg;
cfg["id"] = CH_RESERVED_MAX + i + 1;
cfg["negotiated"] = true;
cfg["ordered"] = true;
switch (mode) {
case TRANSFER_MODE_UNRELIABLE_ORDERED:
cfg["maxPacketLifetime"] = 1;
break;
case TRANSFER_MODE_UNRELIABLE:
cfg["maxPacketLifetime"] = 1;
cfg["ordered"] = false;
break;
case TRANSFER_MODE_RELIABLE:
break;
default:
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode));
}
channels_config.push_back(cfg);
}
unique_id = p_self_id; unique_id = p_self_id;
server_compat = p_server_compat; server_compat = p_server_compat;
@ -260,17 +294,23 @@ Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_pe
cfg["id"] = 1; cfg["id"] = 1;
peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg); peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg);
ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED); ERR_FAIL_COND_V(peer->channels[CH_RELIABLE].is_null(), FAILED);
cfg["id"] = 2; cfg["id"] = 2;
cfg["maxPacketLifetime"] = p_unreliable_lifetime; cfg["maxPacketLifetime"] = p_unreliable_lifetime;
peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg); peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg);
ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED); ERR_FAIL_COND_V(peer->channels[CH_ORDERED].is_null(), FAILED);
cfg["id"] = 3; cfg["id"] = 3;
cfg["ordered"] = false; cfg["ordered"] = false;
peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg); peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg);
ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED); ERR_FAIL_COND_V(peer->channels[CH_UNRELIABLE].is_null(), FAILED);
for (const Dictionary &dict : channels_config) {
Ref<WebRTCDataChannel> ch = p_peer->create_data_channel(String::num_int64(dict["id"]), dict);
ERR_FAIL_COND_V(ch.is_null(), FAILED);
peer->channels.push_back(ch);
}
peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map
@ -312,7 +352,8 @@ Error WebRTCMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_
Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED); ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED);
int ch = CH_RELIABLE; int ch = transfer_channel;
if (ch == 0) {
switch (transfer_mode) { switch (transfer_mode) {
case TRANSFER_MODE_RELIABLE: case TRANSFER_MODE_RELIABLE:
ch = CH_RELIABLE; ch = CH_RELIABLE;
@ -324,6 +365,9 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
ch = CH_UNRELIABLE; ch = CH_UNRELIABLE;
break; break;
} }
} else {
ch += CH_RESERVED_MAX - 1;
}
Map<int, Ref<ConnectedPeer>>::Element *E = nullptr; Map<int, Ref<ConnectedPeer>>::Element *E = nullptr;
@ -331,8 +375,8 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
E = peer_map.find(target_peer); 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_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_MSG(E->value()->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG); ERR_FAIL_COND_V(E->value()->channels[ch].is_null(), ERR_BUG);
return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
} else { } else {
@ -344,7 +388,8 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
continue; continue;
} }
ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid()); ERR_CONTINUE_MSG(F->value()->channels.size() <= ch, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
ERR_CONTINUE(F->value()->channels[ch].is_null());
F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
} }
} }
@ -370,23 +415,13 @@ int WebRTCMultiplayerPeer::get_max_packet_size() const {
void WebRTCMultiplayerPeer::close() { void WebRTCMultiplayerPeer::close() {
peer_map.clear(); peer_map.clear();
channels_config.clear();
unique_id = 0; unique_id = 0;
next_packet_peer = 0; next_packet_peer = 0;
target_peer = 0; target_peer = 0;
connection_status = CONNECTION_DISCONNECTED; 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() { WebRTCMultiplayerPeer::~WebRTCMultiplayerPeer() {
close(); close();
} }

View File

@ -62,25 +62,27 @@ private:
} }
}; };
uint32_t unique_id; uint32_t unique_id = 0;
int target_peer; int target_peer = 0;
int client_count; int client_count = 0;
bool refuse_connections; bool refuse_connections = false;
ConnectionStatus connection_status; ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
TransferMode transfer_mode; int transfer_channel = 0;
int next_packet_peer; TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
bool server_compat; int next_packet_peer = 0;
bool server_compat = false;
Map<int, Ref<ConnectedPeer>> peer_map; Map<int, Ref<ConnectedPeer>> peer_map;
List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict); void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
void _find_next_peer(); void _find_next_peer();
public: public:
WebRTCMultiplayerPeer(); WebRTCMultiplayerPeer() {}
~WebRTCMultiplayerPeer(); ~WebRTCMultiplayerPeer();
Error initialize(int p_self_id, bool p_server_compat = false); Error initialize(int p_self_id, bool p_server_compat = false, Array p_channels_config = Array());
Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1); Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1);
void remove_peer(int p_peer_id); void remove_peer(int p_peer_id);
bool has_peer(int p_peer_id); bool has_peer(int p_peer_id);
@ -95,6 +97,8 @@ public:
int get_max_packet_size() const override; int get_max_packet_size() const override;
// MultiplayerPeer // MultiplayerPeer
void set_transfer_channel(int p_channel) override;
int get_transfer_channel() const override;
void set_transfer_mode(TransferMode p_mode) override; void set_transfer_mode(TransferMode p_mode) override;
TransferMode get_transfer_mode() const override; TransferMode get_transfer_mode() const override;
void set_target_peer(int p_peer_id) override; void set_target_peer(int p_peer_id) override;

View File

@ -105,6 +105,14 @@ Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer
// //
// MultiplayerPeer // MultiplayerPeer
// //
void WebSocketMultiplayerPeer::set_transfer_channel(int p_channel) {
// Websocket does not have channels.
}
int WebSocketMultiplayerPeer::get_transfer_channel() const {
return 0;
}
void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
// Websocket uses TCP, reliable // Websocket uses TCP, reliable
} }

View File

@ -78,6 +78,8 @@ protected:
public: public:
/* MultiplayerPeer */ /* MultiplayerPeer */
void set_transfer_channel(int p_channel) override;
int get_transfer_channel() const override;
void set_transfer_mode(TransferMode p_mode) override; void set_transfer_mode(TransferMode p_mode) override;
TransferMode get_transfer_mode() const override; TransferMode get_transfer_mode() const override;
void set_target_peer(int p_target_peer) override; void set_target_peer(int p_target_peer) override;