mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 19:42:43 +00:00
[MP] Let MultiplayerAPI handle packet relaying and peer signaling.
MultiplayerPeer changes: - Adds is_server_relay_supported virtual method Informs the upper MultiplayerAPI layer if it can signal peers connected to the server to other clients, and perform packet relaying among them. - Adds get_packet_channel and get_packet_mode virtual methods Allows the MultiplayerAPI to retrieve the channel and transfer modes to use when relaying the last received packet. SceneMultiplayerPeer changes: - Implement peer signaling and packet relaying when the MultiplayerPeer advertise they are supported. ENet, WebRTC, WebSocket changes: - Removed custom code for relaying from WebSocket and ENet, and let it be handled by the upper layer. - Update WebRTC to split create_client, create_server, and create_mesh, with the latter behaving like the old initialize with "server_compatibility = false", and the first two supporting the upper layer relaying protocol.
This commit is contained in:
parent
03e5de37ae
commit
7536d15fe3
@ -25,10 +25,22 @@
|
||||
Returns the current state of the connection. See [enum ConnectionStatus].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_packet_channel" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the channel over which the next available packet was received. See [method PacketPeer.get_available_packet_count].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_packet_mode" qualifiers="const">
|
||||
<return type="int" enum="MultiplayerPeer.TransferMode" />
|
||||
<description>
|
||||
Returns the [enum MultiplayerPeer.TransferMode] the remote peer used to send the next available packet. See [method PacketPeer.get_available_packet_count].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_packet_peer" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the ID of the [MultiplayerPeer] who sent the most recent packet.
|
||||
Returns the ID of the [MultiplayerPeer] who sent the next available packet. See [method PacketPeer.get_available_packet_count].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_unique_id" qualifiers="const">
|
||||
@ -37,6 +49,12 @@
|
||||
Returns the ID of this [MultiplayerPeer].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_server_relay_supported" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns true if the server can act as a relay in the current configuration (i.e. if the higher level [MultiplayerAPI] should notify connected clients of other peers, and implement a relay protocol to allow communication between them).
|
||||
</description>
|
||||
</method>
|
||||
<method name="poll">
|
||||
<return type="void" />
|
||||
<description>
|
||||
|
@ -77,8 +77,5 @@
|
||||
<member name="host" type="ENetConnection" setter="" getter="get_host">
|
||||
The underlying [ENetConnection] created after [method create_client] and [method create_server].
|
||||
</member>
|
||||
<member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
|
||||
Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
||||
|
@ -44,6 +44,22 @@ int ENetMultiplayerPeer::get_packet_peer() const {
|
||||
return incoming_packets.front()->get().from;
|
||||
}
|
||||
|
||||
MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_packet_mode() const {
|
||||
ERR_FAIL_COND_V_MSG(!_is_active(), TRANSFER_MODE_RELIABLE, "The multiplayer instance isn't currently active.");
|
||||
ERR_FAIL_COND_V(incoming_packets.size() == 0, TRANSFER_MODE_RELIABLE);
|
||||
return incoming_packets.front()->get().transfer_mode;
|
||||
}
|
||||
|
||||
int ENetMultiplayerPeer::get_packet_channel() const {
|
||||
ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active.");
|
||||
ERR_FAIL_COND_V(incoming_packets.size() == 0, 1);
|
||||
int ch = incoming_packets.front()->get().channel;
|
||||
if (ch >= SYSCH_MAX) { // First 2 channels are reserved.
|
||||
return ch - SYSCH_MAX + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) {
|
||||
ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
|
||||
set_refuse_new_connections(false);
|
||||
@ -114,6 +130,22 @@ Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_store_packet(int32_t p_source, ENetConnection::Event &p_event) {
|
||||
Packet packet;
|
||||
packet.packet = p_event.packet;
|
||||
packet.channel = p_event.channel_id;
|
||||
packet.from = p_source;
|
||||
if (p_event.packet->flags & ENET_PACKET_FLAG_RELIABLE) {
|
||||
packet.transfer_mode = TRANSFER_MODE_RELIABLE;
|
||||
} else if (p_event.packet->flags & ENET_PACKET_FLAG_UNSEQUENCED) {
|
||||
packet.transfer_mode = TRANSFER_MODE_UNRELIABLE;
|
||||
} else {
|
||||
packet.transfer_mode = TRANSFER_MODE_UNRELIABLE_ORDERED;
|
||||
}
|
||||
packet.packet->referenceCount++;
|
||||
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: {
|
||||
@ -131,9 +163,6 @@ bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type,
|
||||
peers[id] = p_event.peer;
|
||||
|
||||
emit_signal(SNAME("peer_connected"), id);
|
||||
if (server_relay) {
|
||||
_notify_peers(id, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ENetConnection::EVENT_DISCONNECT: {
|
||||
@ -145,50 +174,11 @@ bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type,
|
||||
|
||||
emit_signal(SNAME("peer_disconnected"), id);
|
||||
peers.erase(id);
|
||||
if (server_relay) {
|
||||
_notify_peers(id, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ENetConnection::EVENT_RECEIVE: {
|
||||
if (p_event.channel_id == SYSCH_CONFIG) {
|
||||
_destroy_unused(p_event.packet);
|
||||
ERR_FAIL_V_MSG(false, "Only server can send config messages");
|
||||
} else {
|
||||
if (p_event.packet->dataLength < 8) {
|
||||
_destroy_unused(p_event.packet);
|
||||
ERR_FAIL_V_MSG(false, "Invalid packet size");
|
||||
}
|
||||
|
||||
uint32_t source = decode_uint32(&p_event.packet->data[0]);
|
||||
int target = decode_uint32(&p_event.packet->data[4]);
|
||||
|
||||
uint32_t id = p_event.peer->get_meta(SNAME("_net_id"));
|
||||
// Someone is cheating and trying to fake the source!
|
||||
if (source != id) {
|
||||
_destroy_unused(p_event.packet);
|
||||
ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!");
|
||||
}
|
||||
|
||||
Packet packet;
|
||||
packet.packet = p_event.packet;
|
||||
packet.channel = p_event.channel_id;
|
||||
packet.from = id;
|
||||
|
||||
// Even if relaying is disabled, these targets are valid as incoming packets.
|
||||
if (target == 1 || target == 0 || target < -1) {
|
||||
packet.packet->referenceCount++;
|
||||
incoming_packets.push_back(packet);
|
||||
}
|
||||
|
||||
if (server_relay && target != 1) {
|
||||
packet.packet->referenceCount++;
|
||||
_relay(source, target, p_event.channel_id, p_event.packet);
|
||||
packet.packet->referenceCount--;
|
||||
_destroy_unused(p_event.packet);
|
||||
}
|
||||
// Destroy packet later
|
||||
}
|
||||
int32_t source = p_event.peer->get_meta(SNAME("_net_id"));
|
||||
_store_packet(source, p_event);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
@ -215,44 +205,7 @@ bool ENetMultiplayerPeer::_parse_client_event(ENetConnection::EventType p_type,
|
||||
return true;
|
||||
}
|
||||
case ENetConnection::EVENT_RECEIVE: {
|
||||
if (p_event.channel_id == SYSCH_CONFIG) {
|
||||
// Config message
|
||||
if (p_event.packet->dataLength != 8) {
|
||||
_destroy_unused(p_event.packet);
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
int msg = decode_uint32(&p_event.packet->data[0]);
|
||||
int id = decode_uint32(&p_event.packet->data[4]);
|
||||
|
||||
switch (msg) {
|
||||
case SYSMSG_ADD_PEER: {
|
||||
peers[id] = Ref<ENetPacketPeer>();
|
||||
emit_signal(SNAME("peer_connected"), id);
|
||||
|
||||
} break;
|
||||
case SYSMSG_REMOVE_PEER: {
|
||||
peers.erase(id);
|
||||
emit_signal(SNAME("peer_disconnected"), id);
|
||||
} break;
|
||||
}
|
||||
_destroy_unused(p_event.packet);
|
||||
} else {
|
||||
if (p_event.packet->dataLength < 8) {
|
||||
_destroy_unused(p_event.packet);
|
||||
ERR_FAIL_V_MSG(false, "Invalid packet size");
|
||||
}
|
||||
|
||||
uint32_t source = decode_uint32(&p_event.packet->data[0]);
|
||||
Packet packet;
|
||||
packet.packet = p_event.packet;
|
||||
packet.from = source;
|
||||
packet.channel = p_event.channel_id;
|
||||
|
||||
packet.packet->referenceCount++;
|
||||
incoming_packets.push_back(packet);
|
||||
// Destroy packet later
|
||||
}
|
||||
_store_packet(1, p_event);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
@ -273,18 +226,7 @@ bool ENetMultiplayerPeer::_parse_mesh_event(ENetConnection::EventType p_type, EN
|
||||
hosts.erase(p_peer_id);
|
||||
return true;
|
||||
case ENetConnection::EVENT_RECEIVE: {
|
||||
if (p_event.packet->dataLength < 8) {
|
||||
_destroy_unused(p_event.packet);
|
||||
ERR_FAIL_V_MSG(false, "Invalid packet size");
|
||||
}
|
||||
|
||||
Packet packet;
|
||||
packet.packet = p_event.packet;
|
||||
packet.from = p_peer_id;
|
||||
packet.channel = p_event.channel_id;
|
||||
|
||||
packet.packet->referenceCount++;
|
||||
incoming_packets.push_back(packet);
|
||||
_store_packet(p_peer_id, p_event);
|
||||
return false;
|
||||
} break;
|
||||
default:
|
||||
@ -376,6 +318,10 @@ bool ENetMultiplayerPeer::is_server() const {
|
||||
return active_mode == MODE_SERVER;
|
||||
}
|
||||
|
||||
bool ENetMultiplayerPeer::is_server_relay_supported() const {
|
||||
return active_mode == MODE_SERVER || active_mode == MODE_CLIENT;
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) {
|
||||
if (!_is_active()) {
|
||||
return;
|
||||
@ -422,8 +368,8 @@ Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_si
|
||||
current_packet = incoming_packets.front()->get();
|
||||
incoming_packets.pop_front();
|
||||
|
||||
*r_buffer = (const uint8_t *)(¤t_packet.packet->data[8]);
|
||||
r_buffer_size = current_packet.packet->dataLength - 8;
|
||||
*r_buffer = (const uint8_t *)(current_packet.packet->data);
|
||||
r_buffer_size = current_packet.packet->dataLength;
|
||||
|
||||
return OK;
|
||||
}
|
||||
@ -457,15 +403,13 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size + 8 > ENET_HOST_DEFAULT_MTU) {
|
||||
WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size + 8, ENET_HOST_DEFAULT_MTU));
|
||||
if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size > ENET_HOST_DEFAULT_MTU) {
|
||||
WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU));
|
||||
}
|
||||
#endif
|
||||
|
||||
ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags);
|
||||
encode_uint32(unique_id, &packet->data[0]); // Source ID
|
||||
encode_uint32(target_peer, &packet->data[4]); // Dest ID
|
||||
memcpy(&packet->data[8], p_buffer, p_buffer_size);
|
||||
ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size, packet_flags);
|
||||
memcpy(&packet->data[0], p_buffer, p_buffer_size);
|
||||
|
||||
if (is_server()) {
|
||||
if (target_peer == 0) {
|
||||
@ -548,16 +492,6 @@ void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enabled) {
|
||||
MultiplayerPeer::set_refuse_new_connections(p_enabled);
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) {
|
||||
ERR_FAIL_COND_MSG(_is_active(), "Server relaying can't be toggled while the multiplayer instance is active.");
|
||||
|
||||
server_relay = p_enabled;
|
||||
}
|
||||
|
||||
bool ENetMultiplayerPeer::is_server_relay_enabled() const {
|
||||
return server_relay;
|
||||
}
|
||||
|
||||
Ref<ENetConnection> ENetMultiplayerPeer::get_host() const {
|
||||
ERR_FAIL_COND_V(!_is_active(), nullptr);
|
||||
ERR_FAIL_COND_V(active_mode == MODE_MESH, nullptr);
|
||||
@ -577,70 +511,6 @@ void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) {
|
||||
}
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet) {
|
||||
if (p_to == 0) {
|
||||
// Re-send to everyone but sender :|
|
||||
for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
|
||||
if (E.key == p_from) {
|
||||
continue;
|
||||
}
|
||||
|
||||
E.value->send(p_channel, p_packet);
|
||||
}
|
||||
} else if (p_to < 0) {
|
||||
// Re-send to everyone but excluded and sender.
|
||||
for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
|
||||
if (E.key == p_from || E.key == -p_to) { // Do not resend to self, also do not send to excluded
|
||||
continue;
|
||||
}
|
||||
|
||||
E.value->send(p_channel, p_packet);
|
||||
}
|
||||
} else {
|
||||
// To someone else, specifically
|
||||
ERR_FAIL_COND(!peers.has(p_to));
|
||||
ENetPacket *packet = enet_packet_create(p_packet->data, p_packet->dataLength, p_packet->flags);
|
||||
peers[p_to]->send(p_channel, packet);
|
||||
}
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_notify_peers(int p_id, bool p_connected) {
|
||||
if (p_connected) {
|
||||
ERR_FAIL_COND(!peers.has(p_id));
|
||||
// Someone connected, notify all the peers available.
|
||||
Ref<ENetPacketPeer> peer = peers[p_id];
|
||||
ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
|
||||
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
|
||||
encode_uint32(p_id, &packet->data[4]);
|
||||
for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
|
||||
if (E.key == p_id) {
|
||||
continue;
|
||||
}
|
||||
// Send new peer to existing peer.
|
||||
E.value->send(SYSCH_CONFIG, packet);
|
||||
// Send existing peer to new peer.
|
||||
// This packet will be automatically destroyed by ENet after send.
|
||||
ENetPacket *packet2 = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
|
||||
encode_uint32(SYSMSG_ADD_PEER, &packet2->data[0]);
|
||||
encode_uint32(E.key, &packet2->data[4]);
|
||||
peer->send(SYSCH_CONFIG, packet2);
|
||||
}
|
||||
_destroy_unused(packet);
|
||||
} else {
|
||||
// Server just received a client disconnect and is in relay mode, notify everyone else.
|
||||
ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
|
||||
encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
|
||||
encode_uint32(p_id, &packet->data[4]);
|
||||
for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
|
||||
if (E.key == p_id) {
|
||||
continue;
|
||||
}
|
||||
E.value->send(SYSCH_CONFIG, packet);
|
||||
}
|
||||
_destroy_unused(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
|
||||
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));
|
||||
@ -649,12 +519,9 @@ void ENetMultiplayerPeer::_bind_methods() {
|
||||
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("set_server_relay_enabled", "enabled"), &ENetMultiplayerPeer::set_server_relay_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &ENetMultiplayerPeer::is_server_relay_enabled);
|
||||
ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host);
|
||||
ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host");
|
||||
}
|
||||
|
||||
|
@ -47,10 +47,9 @@ private:
|
||||
};
|
||||
|
||||
enum {
|
||||
SYSCH_CONFIG = 0,
|
||||
SYSCH_RELIABLE = 1,
|
||||
SYSCH_UNRELIABLE = 2,
|
||||
SYSCH_MAX = 3
|
||||
SYSCH_RELIABLE = 0,
|
||||
SYSCH_UNRELIABLE = 1,
|
||||
SYSCH_MAX = 2
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
@ -66,8 +65,6 @@ private:
|
||||
|
||||
int target_peer = 0;
|
||||
|
||||
bool server_relay = true;
|
||||
|
||||
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
|
||||
|
||||
HashMap<int, Ref<ENetConnection>> hosts;
|
||||
@ -77,18 +74,18 @@ private:
|
||||
ENetPacket *packet = nullptr;
|
||||
int from = 0;
|
||||
int channel = 0;
|
||||
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
|
||||
};
|
||||
|
||||
List<Packet> incoming_packets;
|
||||
|
||||
Packet current_packet;
|
||||
|
||||
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 _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet);
|
||||
void _notify_peers(int p_id, bool p_connected);
|
||||
void _destroy_unused(ENetPacket *p_packet);
|
||||
_FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; }
|
||||
|
||||
@ -99,10 +96,15 @@ protected:
|
||||
|
||||
public:
|
||||
virtual void set_target_peer(int p_peer) override;
|
||||
|
||||
virtual int get_packet_peer() const override;
|
||||
virtual TransferMode get_packet_mode() const override;
|
||||
virtual int get_packet_channel() const override;
|
||||
|
||||
virtual void poll() override;
|
||||
virtual bool is_server() const override;
|
||||
virtual bool is_server_relay_supported() const override;
|
||||
|
||||
// Overridden so we can instrument the DTLSServer when needed.
|
||||
virtual void set_refuse_new_connections(bool p_enabled) override;
|
||||
|
||||
@ -125,8 +127,6 @@ public:
|
||||
void disconnect_peer(int p_peer, bool now = false);
|
||||
|
||||
void set_bind_ip(const IPAddress &p_ip);
|
||||
void set_server_relay_enabled(bool p_enabled);
|
||||
bool is_server_relay_enabled() const;
|
||||
|
||||
Ref<ENetConnection> get_host() const;
|
||||
Ref<ENetPacketPeer> get_peer(int p_id) const;
|
||||
|
@ -42,6 +42,10 @@
|
||||
The root path to use for RPCs and replication. Instead of an absolute path, a relative path will be used to find the node upon which the RPC should be executed.
|
||||
This effectively allows to have different branches of the scene tree to be managed by different MultiplayerAPI, allowing for example to run both client and server in the same scene.
|
||||
</member>
|
||||
<member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
|
||||
Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
|
||||
[b]Note:[/b] Support for this feature may depend on the current [MultiplayerPeer] configuration. See [method MultiplayerPeer.is_server_relay_supported].
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="peer_packet">
|
||||
|
@ -105,8 +105,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
|
||||
|
||||
multiplayer_peer->set_transfer_channel(0);
|
||||
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
|
||||
multiplayer_peer->set_target_peer(p_from);
|
||||
multiplayer_peer->put_packet(packet.ptr(), packet.size());
|
||||
multiplayer->send_command(p_from, packet.ptr(), packet.size());
|
||||
}
|
||||
|
||||
void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
|
||||
@ -162,10 +161,9 @@ Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Pat
|
||||
|
||||
Error err = OK;
|
||||
for (int peer_id : p_peers) {
|
||||
multiplayer_peer->set_target_peer(peer_id);
|
||||
multiplayer_peer->set_transfer_channel(0);
|
||||
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
|
||||
err = multiplayer_peer->put_packet(packet.ptr(), packet.size());
|
||||
err = multiplayer->send_command(peer_id, packet.ptr(), packet.size());
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
// Insert into confirmed, but as false since it was not confirmed.
|
||||
psc->confirmed_peers.insert(peer_id, false);
|
||||
|
@ -67,12 +67,20 @@ Error SceneMultiplayer::poll() {
|
||||
const uint8_t *packet;
|
||||
int len;
|
||||
|
||||
int channel = multiplayer_peer->get_packet_channel();
|
||||
MultiplayerPeer::TransferMode mode = multiplayer_peer->get_packet_mode();
|
||||
|
||||
Error err = multiplayer_peer->get_packet(&packet, len);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error getting packet! %d", err));
|
||||
|
||||
remote_sender_id = sender;
|
||||
_process_packet(sender, packet, len);
|
||||
remote_sender_id = 0;
|
||||
if (len && (packet[0] & CMD_MASK) == NETWORK_COMMAND_SYS) {
|
||||
// Sys messages are processed separately since they might call _process_packet themselves.
|
||||
_process_sys(sender, packet, len, mode, channel);
|
||||
} else {
|
||||
remote_sender_id = sender;
|
||||
_process_packet(sender, packet, len);
|
||||
remote_sender_id = 0;
|
||||
}
|
||||
|
||||
if (!multiplayer_peer.is_valid()) {
|
||||
return OK; // It's also possible that a packet or RPC caused a disconnection, so also check here.
|
||||
@ -86,6 +94,7 @@ void SceneMultiplayer::clear() {
|
||||
connected_peers.clear();
|
||||
packet_cache.clear();
|
||||
cache->clear();
|
||||
relay_buffer->clear();
|
||||
}
|
||||
|
||||
void SceneMultiplayer::set_root_path(const NodePath &p_path) {
|
||||
@ -166,10 +175,123 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int
|
||||
case NETWORK_COMMAND_SYNC: {
|
||||
replicator->on_sync_receive(p_from, p_packet, p_packet_len);
|
||||
} break;
|
||||
default: {
|
||||
ERR_FAIL_MSG("Invalid network command from " + itos(p_from));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_packet_len) {
|
||||
if (server_relay && get_unique_id() != 1 && p_to != 1 && multiplayer_peer->is_server_relay_supported()) {
|
||||
// Send relay packet.
|
||||
relay_buffer->seek(0);
|
||||
relay_buffer->put_u8(NETWORK_COMMAND_SYS);
|
||||
relay_buffer->put_u8(SYS_COMMAND_RELAY);
|
||||
relay_buffer->put_32(p_to); // Set the destination.
|
||||
relay_buffer->put_data(p_packet, p_packet_len);
|
||||
multiplayer_peer->set_target_peer(1);
|
||||
const Vector<uint8_t> data = relay_buffer->get_data_array();
|
||||
return multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
|
||||
}
|
||||
if (p_to < 0) {
|
||||
for (const int &pid : connected_peers) {
|
||||
if (pid == -p_to) {
|
||||
continue;
|
||||
}
|
||||
multiplayer_peer->set_target_peer(pid);
|
||||
multiplayer_peer->put_packet(p_packet, p_packet_len);
|
||||
}
|
||||
return OK;
|
||||
} else {
|
||||
multiplayer_peer->set_target_peer(p_to);
|
||||
return multiplayer_peer->put_packet(p_packet, p_packet_len);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel) {
|
||||
ERR_FAIL_COND_MSG(p_packet_len < SYS_CMD_SIZE, "Invalid packet received. Size too small.");
|
||||
uint8_t sys_cmd_type = p_packet[1];
|
||||
int32_t peer = int32_t(decode_uint32(&p_packet[2]));
|
||||
switch (sys_cmd_type) {
|
||||
case SYS_COMMAND_ADD_PEER: {
|
||||
ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1);
|
||||
_add_peer(peer);
|
||||
} break;
|
||||
case SYS_COMMAND_DEL_PEER: {
|
||||
ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1);
|
||||
_del_peer(peer);
|
||||
} break;
|
||||
case SYS_COMMAND_RELAY: {
|
||||
ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported());
|
||||
ERR_FAIL_COND(p_packet_len < SYS_CMD_SIZE + 1);
|
||||
const uint8_t *packet = p_packet + SYS_CMD_SIZE;
|
||||
int len = p_packet_len - SYS_CMD_SIZE;
|
||||
bool should_process = false;
|
||||
if (get_unique_id() == 1) { // I am the server.
|
||||
// Direct messages to server should not go through relay.
|
||||
ERR_FAIL_COND(peer > 0 && !connected_peers.has(peer));
|
||||
// Send relay packet.
|
||||
relay_buffer->seek(0);
|
||||
relay_buffer->put_u8(NETWORK_COMMAND_SYS);
|
||||
relay_buffer->put_u8(SYS_COMMAND_RELAY);
|
||||
relay_buffer->put_32(p_from); // Set the source.
|
||||
relay_buffer->put_data(packet, len);
|
||||
const Vector<uint8_t> data = relay_buffer->get_data_array();
|
||||
multiplayer_peer->set_transfer_mode(p_mode);
|
||||
multiplayer_peer->set_transfer_channel(p_channel);
|
||||
if (peer > 0) {
|
||||
multiplayer_peer->set_target_peer(peer);
|
||||
multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
|
||||
} else {
|
||||
for (const int &P : connected_peers) {
|
||||
// Not to sender, nor excluded.
|
||||
if (P == p_from || (peer < 0 && P != -peer)) {
|
||||
continue;
|
||||
}
|
||||
multiplayer_peer->set_target_peer(P);
|
||||
multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
|
||||
}
|
||||
}
|
||||
if (peer == 0 || peer == -1) {
|
||||
should_process = true;
|
||||
peer = p_from; // Process as the source.
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_COND(p_from != 1); // Bug.
|
||||
should_process = true;
|
||||
}
|
||||
if (should_process) {
|
||||
remote_sender_id = peer;
|
||||
_process_packet(peer, packet, len);
|
||||
remote_sender_id = 0;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneMultiplayer::_add_peer(int p_id) {
|
||||
if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) {
|
||||
// Notify others of connection, and send connected peers to newly connected one.
|
||||
uint8_t buf[SYS_CMD_SIZE];
|
||||
buf[0] = NETWORK_COMMAND_SYS;
|
||||
buf[1] = SYS_COMMAND_ADD_PEER;
|
||||
multiplayer_peer->set_transfer_channel(0);
|
||||
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
|
||||
for (const int &P : connected_peers) {
|
||||
// Send new peer to already connected.
|
||||
encode_uint32(p_id, &buf[2]);
|
||||
multiplayer_peer->set_target_peer(P);
|
||||
multiplayer_peer->put_packet(buf, sizeof(buf));
|
||||
// Send already connected to new peer.
|
||||
encode_uint32(P, &buf[2]);
|
||||
multiplayer_peer->set_target_peer(p_id);
|
||||
multiplayer_peer->put_packet(buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
|
||||
connected_peers.insert(p_id);
|
||||
cache->on_peer_change(p_id, true);
|
||||
replicator->on_peer_change(p_id, true);
|
||||
@ -177,6 +299,23 @@ void SceneMultiplayer::_add_peer(int p_id) {
|
||||
}
|
||||
|
||||
void SceneMultiplayer::_del_peer(int p_id) {
|
||||
if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) {
|
||||
// Notify others of disconnection.
|
||||
uint8_t buf[SYS_CMD_SIZE];
|
||||
buf[0] = NETWORK_COMMAND_SYS;
|
||||
buf[1] = SYS_COMMAND_DEL_PEER;
|
||||
multiplayer_peer->set_transfer_channel(0);
|
||||
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
|
||||
encode_uint32(p_id, &buf[2]);
|
||||
for (const int &P : connected_peers) {
|
||||
if (P == p_id) {
|
||||
continue;
|
||||
}
|
||||
multiplayer_peer->set_target_peer(P);
|
||||
multiplayer_peer->put_packet(buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
|
||||
replicator->on_peer_change(p_id, false);
|
||||
cache->on_peer_change(p_id, false);
|
||||
connected_peers.erase(p_id);
|
||||
@ -209,11 +348,9 @@ Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer
|
||||
packet_cache.write[0] = NETWORK_COMMAND_RAW;
|
||||
memcpy(&packet_cache.write[1], &r[0], p_data.size());
|
||||
|
||||
multiplayer_peer->set_target_peer(p_to);
|
||||
multiplayer_peer->set_transfer_channel(p_channel);
|
||||
multiplayer_peer->set_transfer_mode(p_mode);
|
||||
|
||||
return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
|
||||
return send_command(p_to, packet_cache.ptr(), p_data.size() + 1);
|
||||
}
|
||||
|
||||
void SceneMultiplayer::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
|
||||
@ -303,6 +440,15 @@ Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_con
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void SceneMultiplayer::set_server_relay_enabled(bool p_enabled) {
|
||||
ERR_FAIL_COND_MSG(multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_DISCONNECTED, "Cannot change the server relay option while the multiplayer peer is active.");
|
||||
server_relay = p_enabled;
|
||||
}
|
||||
|
||||
bool SceneMultiplayer::is_server_relay_enabled() const {
|
||||
return server_relay;
|
||||
}
|
||||
|
||||
void SceneMultiplayer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_root_path", "path"), &SceneMultiplayer::set_root_path);
|
||||
ClassDB::bind_method(D_METHOD("get_root_path"), &SceneMultiplayer::get_root_path);
|
||||
@ -311,17 +457,22 @@ void SceneMultiplayer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &SceneMultiplayer::is_refusing_new_connections);
|
||||
ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &SceneMultiplayer::set_allow_object_decoding);
|
||||
ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &SceneMultiplayer::is_object_decoding_allowed);
|
||||
ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &SceneMultiplayer::set_server_relay_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &SceneMultiplayer::is_server_relay_enabled);
|
||||
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &SceneMultiplayer::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
|
||||
|
||||
ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));
|
||||
}
|
||||
|
||||
SceneMultiplayer::SceneMultiplayer() {
|
||||
relay_buffer.instantiate();
|
||||
replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this)));
|
||||
rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this)));
|
||||
cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this)));
|
||||
|
@ -49,6 +49,17 @@ public:
|
||||
NETWORK_COMMAND_SPAWN,
|
||||
NETWORK_COMMAND_DESPAWN,
|
||||
NETWORK_COMMAND_SYNC,
|
||||
NETWORK_COMMAND_SYS,
|
||||
};
|
||||
|
||||
enum SysCommands {
|
||||
SYS_COMMAND_ADD_PEER,
|
||||
SYS_COMMAND_DEL_PEER,
|
||||
SYS_COMMAND_RELAY,
|
||||
};
|
||||
|
||||
enum {
|
||||
SYS_CMD_SIZE = 6, // Command + sys command + peer_id (+ optional payload).
|
||||
};
|
||||
|
||||
// For each command, the 4 MSB can contain custom flags, as defined by subsystems.
|
||||
@ -74,6 +85,8 @@ private:
|
||||
|
||||
NodePath root_path;
|
||||
bool allow_object_decoding = false;
|
||||
bool server_relay = true;
|
||||
Ref<StreamPeerBuffer> relay_buffer;
|
||||
|
||||
Ref<SceneCacheInterface> cache;
|
||||
Ref<SceneReplicationInterface> replicator;
|
||||
@ -84,6 +97,7 @@ protected:
|
||||
|
||||
void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
|
||||
void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
|
||||
void _process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel);
|
||||
|
||||
void _add_peer(int p_id);
|
||||
void _del_peer(int p_id);
|
||||
@ -111,6 +125,7 @@ public:
|
||||
void set_root_path(const NodePath &p_path);
|
||||
NodePath get_root_path() const;
|
||||
|
||||
Error send_command(int p_to, const uint8_t *p_packet, int p_packet_len); // Used internally to relay packets when needed.
|
||||
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);
|
||||
String get_rpc_md5(const Object *p_obj);
|
||||
|
||||
@ -123,6 +138,9 @@ public:
|
||||
void set_allow_object_decoding(bool p_enable);
|
||||
bool is_object_decoding_allowed() const;
|
||||
|
||||
void set_server_relay_enabled(bool p_enabled);
|
||||
bool is_server_relay_enabled() const;
|
||||
|
||||
Ref<SceneCacheInterface> get_path_cache() { return cache; }
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
@ -370,10 +370,9 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
|
||||
#endif
|
||||
|
||||
Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
|
||||
peer->set_target_peer(p_peer);
|
||||
peer->set_transfer_channel(0);
|
||||
peer->set_transfer_mode(p_reliable ? MultiplayerPeer::TRANSFER_MODE_RELIABLE : MultiplayerPeer::TRANSFER_MODE_UNRELIABLE);
|
||||
return peer->put_packet(p_buffer, p_size);
|
||||
return multiplayer->send_command(p_peer, p_buffer, p_size);
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len) {
|
||||
|
@ -412,8 +412,7 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
|
||||
|
||||
if (has_all_peers) {
|
||||
// They all have verified paths, so send fast.
|
||||
peer->set_target_peer(p_to); // To all of you.
|
||||
peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
|
||||
multiplayer->send_command(p_to, packet_cache.ptr(), ofs);
|
||||
} else {
|
||||
// Unreachable because the node ID is never compressed if the peers doesn't know it.
|
||||
CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
|
||||
@ -438,16 +437,14 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
|
||||
|
||||
bool confirmed = multiplayer->get_path_cache()->is_cache_confirmed(from_path, P);
|
||||
|
||||
peer->set_target_peer(P); // To this one specifically.
|
||||
|
||||
if (confirmed) {
|
||||
// This one confirmed path, so use id.
|
||||
encode_uint32(psc_id, &(packet_cache.write[1]));
|
||||
peer->put_packet(packet_cache.ptr(), ofs);
|
||||
multiplayer->send_command(P, packet_cache.ptr(), ofs);
|
||||
} else {
|
||||
// This one did not confirm path yet, so use entire path (sorry!).
|
||||
encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag.
|
||||
peer->put_packet(packet_cache.ptr(), ofs + path_len);
|
||||
multiplayer->send_command(P, packet_cache.ptr(), ofs + path_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<description>
|
||||
This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_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].
|
||||
[signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless the peer is created using [method create_client]. Beside that data transfer works like in a [MultiplayerPeer].
|
||||
[b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
|
||||
</description>
|
||||
<tutorials>
|
||||
@ -28,6 +28,31 @@
|
||||
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" />
|
||||
<param index="1" name="channels_config" type="Array" default="[]" />
|
||||
<description>
|
||||
Initialize the multiplayer peer as a client with the given [code]peer_id[/code] (must be between 2 and 2147483647). In this mode, you should only call [method add_peer] once and with [code]peer_id[/code] of [code]1[/code]. This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
|
||||
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>
|
||||
</method>
|
||||
<method name="create_mesh">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="peer_id" type="int" />
|
||||
<param index="1" name="channels_config" type="Array" default="[]" />
|
||||
<description>
|
||||
Initialize the multiplayer peer as a mesh (i.e. all peers connect to each other) with the given [code]peer_id[/code] (must be between 1 and 2147483647).
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_server">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="channels_config" type="Array" default="[]" />
|
||||
<description>
|
||||
Initialize the multiplayer peer as a server (with unique ID of [code]1[/code]). This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
|
||||
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>
|
||||
</method>
|
||||
<method name="get_peer">
|
||||
<return type="Dictionary" />
|
||||
<param index="0" name="peer_id" type="int" />
|
||||
@ -48,18 +73,6 @@
|
||||
Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though).
|
||||
</description>
|
||||
</method>
|
||||
<method name="initialize">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="peer_id" type="int" />
|
||||
<param index="1" name="server_compatibility" type="bool" default="false" />
|
||||
<param index="2" name="channels_config" type="Array" default="[]" />
|
||||
<description>
|
||||
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].
|
||||
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>
|
||||
</method>
|
||||
<method name="remove_peer">
|
||||
<return type="void" />
|
||||
<param index="0" name="peer_id" type="int" />
|
||||
|
@ -34,7 +34,9 @@
|
||||
#include "core/os/os.h"
|
||||
|
||||
void WebRTCMultiplayerPeer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility", "channels_config"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false), DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("create_server", "channels_config"), &WebRTCMultiplayerPeer::create_server, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("create_client", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_client, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("create_mesh", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_mesh, DEFVAL(Array()));
|
||||
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);
|
||||
@ -52,6 +54,15 @@ int WebRTCMultiplayerPeer::get_packet_peer() const {
|
||||
return next_packet_peer;
|
||||
}
|
||||
|
||||
int WebRTCMultiplayerPeer::get_packet_channel() const {
|
||||
return next_packet_channel < CH_RESERVED_MAX ? 0 : next_packet_channel - CH_RESERVED_MAX + 1;
|
||||
}
|
||||
|
||||
MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_packet_mode() const {
|
||||
ERR_FAIL_INDEX_V(next_packet_channel, channels_modes.size(), TRANSFER_MODE_RELIABLE);
|
||||
return channels_modes[next_packet_channel];
|
||||
}
|
||||
|
||||
bool WebRTCMultiplayerPeer::is_server() const {
|
||||
return unique_id == TARGET_PEER_SERVER;
|
||||
}
|
||||
@ -113,24 +124,14 @@ void WebRTCMultiplayerPeer::poll() {
|
||||
// Signal newly connected peers
|
||||
for (int &E : add) {
|
||||
// Already connected to server: simply notify new peer.
|
||||
// NOTE: Mesh is always connected.
|
||||
if (connection_status == CONNECTION_CONNECTED) {
|
||||
emit_signal(SNAME("peer_connected"), E);
|
||||
}
|
||||
|
||||
// Server emulation mode suppresses peer_conencted until server connects.
|
||||
if (server_compat && E == TARGET_PEER_SERVER) {
|
||||
if (network_mode == MODE_CLIENT) {
|
||||
ERR_CONTINUE(E != TARGET_PEER_SERVER); // Bug.
|
||||
// Server connected.
|
||||
connection_status = CONNECTION_CONNECTED;
|
||||
emit_signal(SNAME("peer_connected"), TARGET_PEER_SERVER);
|
||||
emit_signal(SNAME("connection_succeeded"));
|
||||
// Notify of all previously connected peers
|
||||
for (const KeyValue<int, Ref<ConnectedPeer>> &F : peer_map) {
|
||||
if (F.key != 1 && F.value->connected) {
|
||||
emit_signal(SNAME("peer_connected"), F.key);
|
||||
}
|
||||
}
|
||||
break; // Because we already notified of all newly added peers.
|
||||
} else {
|
||||
emit_signal(SNAME("peer_connected"), E);
|
||||
}
|
||||
}
|
||||
// Fetch next packet
|
||||
@ -150,11 +151,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
|
||||
++E;
|
||||
continue;
|
||||
}
|
||||
int idx = 0;
|
||||
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
|
||||
if (F->get_available_packet_count()) {
|
||||
next_packet_channel = idx;
|
||||
next_packet_peer = E->key;
|
||||
return;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
++E;
|
||||
}
|
||||
@ -165,11 +169,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
|
||||
++E;
|
||||
continue;
|
||||
}
|
||||
int idx = 0;
|
||||
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
|
||||
if (F->get_available_packet_count()) {
|
||||
next_packet_channel = idx;
|
||||
next_packet_peer = E->key;
|
||||
return;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (E->key == (int)next_packet_peer) {
|
||||
break;
|
||||
@ -177,6 +184,7 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
|
||||
++E;
|
||||
}
|
||||
// No packet found
|
||||
next_packet_channel = 0;
|
||||
next_packet_peer = 0;
|
||||
}
|
||||
|
||||
@ -184,11 +192,28 @@ MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status()
|
||||
return connection_status;
|
||||
}
|
||||
|
||||
Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Array p_channels_config) {
|
||||
Error WebRTCMultiplayerPeer::create_server(Array p_channels_config) {
|
||||
return _initialize(1, MODE_SERVER, p_channels_config);
|
||||
}
|
||||
|
||||
Error WebRTCMultiplayerPeer::create_client(int p_self_id, Array p_channels_config) {
|
||||
ERR_FAIL_COND_V_MSG(p_self_id == 1, ERR_INVALID_PARAMETER, "Clients cannot have ID 1.");
|
||||
return _initialize(p_self_id, MODE_CLIENT, p_channels_config);
|
||||
}
|
||||
|
||||
Error WebRTCMultiplayerPeer::create_mesh(int p_self_id, Array p_channels_config) {
|
||||
return _initialize(p_self_id, MODE_MESH, p_channels_config);
|
||||
}
|
||||
|
||||
Error WebRTCMultiplayerPeer::_initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config) {
|
||||
ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
|
||||
channels_config.clear();
|
||||
channels_modes.clear();
|
||||
channels_modes.push_back(TRANSFER_MODE_RELIABLE);
|
||||
channels_modes.push_back(TRANSFER_MODE_UNRELIABLE_ORDERED);
|
||||
channels_modes.push_back(TRANSFER_MODE_UNRELIABLE);
|
||||
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.Multiplayer::TransferMode'");
|
||||
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;
|
||||
@ -207,16 +232,17 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
|
||||
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.Multiplayer::TransferMode'. Got: %d", mode));
|
||||
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);
|
||||
channels_modes.push_back((TransferMode)mode);
|
||||
}
|
||||
|
||||
unique_id = p_self_id;
|
||||
server_compat = p_server_compat;
|
||||
network_mode = p_mode;
|
||||
|
||||
// Mesh and server are always connected
|
||||
if (!server_compat || p_self_id == 1) {
|
||||
if (p_mode != MODE_CLIENT) {
|
||||
connection_status = CONNECTION_CONNECTED;
|
||||
} else {
|
||||
connection_status = CONNECTION_CONNECTING;
|
||||
@ -224,6 +250,10 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool WebRTCMultiplayerPeer::is_server_relay_supported() const {
|
||||
return network_mode == MODE_SERVER || network_mode == MODE_CLIENT;
|
||||
}
|
||||
|
||||
int WebRTCMultiplayerPeer::get_unique_id() const {
|
||||
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1);
|
||||
return unique_id;
|
||||
@ -261,7 +291,10 @@ Dictionary WebRTCMultiplayerPeer::get_peers() {
|
||||
}
|
||||
|
||||
Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> 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(network_mode == MODE_NONE, ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(network_mode == MODE_CLIENT && p_peer_id != 1, ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(network_mode == MODE_SERVER && p_peer_id == 1, ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(p_peer_id < 1 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(is_refusing_new_connections(), ERR_UNAUTHORIZED);
|
||||
// Peer must be valid, and in new state (to create data channels)
|
||||
@ -308,8 +341,12 @@ void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) {
|
||||
if (peer->connected) {
|
||||
peer->connected = false;
|
||||
emit_signal(SNAME("peer_disconnected"), p_peer_id);
|
||||
if (server_compat && p_peer_id == TARGET_PEER_SERVER) {
|
||||
emit_signal(SNAME("server_disconnected"));
|
||||
if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) {
|
||||
if (connection_status == CONNECTION_CONNECTING) {
|
||||
emit_signal(SNAME("connection_failed"));
|
||||
} else {
|
||||
emit_signal(SNAME("server_disconnected"));
|
||||
}
|
||||
connection_status = CONNECTION_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
@ -403,7 +440,9 @@ void WebRTCMultiplayerPeer::close() {
|
||||
channels_config.clear();
|
||||
unique_id = 0;
|
||||
next_packet_peer = 0;
|
||||
next_packet_channel = 0;
|
||||
target_peer = 0;
|
||||
network_mode = MODE_NONE;
|
||||
connection_status = CONNECTION_DISCONNECTED;
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,13 @@ private:
|
||||
CH_RESERVED_MAX = 3
|
||||
};
|
||||
|
||||
enum NetworkMode {
|
||||
MODE_NONE,
|
||||
MODE_SERVER,
|
||||
MODE_CLIENT,
|
||||
MODE_MESH,
|
||||
};
|
||||
|
||||
class ConnectedPeer : public RefCounted {
|
||||
public:
|
||||
Ref<WebRTCPeerConnection> connection;
|
||||
@ -67,19 +74,25 @@ private:
|
||||
int client_count = 0;
|
||||
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
|
||||
int next_packet_peer = 0;
|
||||
bool server_compat = false;
|
||||
int next_packet_channel = 0;
|
||||
NetworkMode network_mode = MODE_NONE;
|
||||
|
||||
HashMap<int, Ref<ConnectedPeer>> peer_map;
|
||||
List<TransferMode> channels_modes;
|
||||
List<Dictionary> channels_config;
|
||||
|
||||
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
|
||||
void _find_next_peer();
|
||||
Ref<ConnectedPeer> _get_next_peer();
|
||||
Error _initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config = Array());
|
||||
|
||||
public:
|
||||
WebRTCMultiplayerPeer() {}
|
||||
~WebRTCMultiplayerPeer();
|
||||
|
||||
Error initialize(int p_self_id, bool p_server_compat = false, Array p_channels_config = Array());
|
||||
Error create_server(Array p_channels_config = Array());
|
||||
Error create_client(int p_self_id, Array p_channels_config = Array());
|
||||
Error create_mesh(int p_self_id, Array p_channels_config = Array());
|
||||
Error add_peer(Ref<WebRTCPeerConnection> 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);
|
||||
@ -98,8 +111,11 @@ public:
|
||||
|
||||
int get_unique_id() const override;
|
||||
int get_packet_peer() const override;
|
||||
int get_packet_channel() const override;
|
||||
TransferMode get_packet_mode() const override;
|
||||
|
||||
bool is_server() const override;
|
||||
bool is_server_relay_supported() const override;
|
||||
|
||||
void poll() override;
|
||||
|
||||
|
@ -142,12 +142,21 @@ Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buff
|
||||
Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
|
||||
ERR_FAIL_COND_V(get_connection_status() != CONNECTION_CONNECTED, ERR_UNCONFIGURED);
|
||||
|
||||
Vector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), target_peer, p_buffer, p_buffer_size);
|
||||
|
||||
if (is_server()) {
|
||||
return _server_relay(1, target_peer, &(buffer.ptr()[0]), buffer.size());
|
||||
if (target_peer > 0) {
|
||||
ERR_FAIL_COND_V_MSG(!peers_map.has(target_peer), ERR_INVALID_PARAMETER, "Peer not found: " + itos(target_peer));
|
||||
get_peer(target_peer)->put_packet(p_buffer, p_buffer_size);
|
||||
} else {
|
||||
for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
|
||||
if (target_peer && -target_peer == E.key) {
|
||||
continue; // Excluded.
|
||||
}
|
||||
E.value->put_packet(p_buffer, p_buffer_size);
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
} else {
|
||||
return get_peer(1)->put_packet(&(buffer.ptr()[0]), buffer.size());
|
||||
return get_peer(1)->put_packet(p_buffer, p_buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,8 +228,41 @@ void WebSocketMultiplayerPeer::_poll_client() {
|
||||
peer->poll(); // Update state and fetch packets.
|
||||
WebSocketPeer::State ready_state = peer->get_ready_state();
|
||||
if (ready_state == WebSocketPeer::STATE_OPEN) {
|
||||
while (peer->get_available_packet_count()) {
|
||||
_process_multiplayer(peer, 1);
|
||||
if (connection_status == CONNECTION_CONNECTING) {
|
||||
if (peer->get_available_packet_count() > 0) {
|
||||
const uint8_t *in_buffer;
|
||||
int size = 0;
|
||||
Error err = peer->get_packet(&in_buffer, size);
|
||||
if (err != OK || size != 4) {
|
||||
peer->close(); // Will cause connection error on next poll.
|
||||
ERR_FAIL_MSG("Invalid ID received from server");
|
||||
}
|
||||
unique_id = *((int32_t *)in_buffer);
|
||||
if (unique_id < 2) {
|
||||
peer->close(); // Will cause connection error on next poll.
|
||||
ERR_FAIL_MSG("Invalid ID received from server");
|
||||
}
|
||||
connection_status = CONNECTION_CONNECTED;
|
||||
emit_signal("peer_connected", 1);
|
||||
emit_signal("connection_succeeded");
|
||||
} else {
|
||||
return; // Still waiting for an ID.
|
||||
}
|
||||
}
|
||||
int pkts = peer->get_available_packet_count();
|
||||
while (pkts > 0 && peer->get_ready_state() == WebSocketPeer::STATE_OPEN) {
|
||||
const uint8_t *in_buffer;
|
||||
int size = 0;
|
||||
Error err = peer->get_packet(&in_buffer, size);
|
||||
ERR_FAIL_COND(err != OK);
|
||||
ERR_FAIL_COND(size <= 0);
|
||||
Packet packet;
|
||||
packet.data = (uint8_t *)memalloc(size);
|
||||
memcpy(packet.data, in_buffer, size);
|
||||
packet.size = size;
|
||||
packet.source = 1;
|
||||
incoming_packets.push_back(packet);
|
||||
pkts--;
|
||||
}
|
||||
} else if (peer->get_ready_state() == WebSocketPeer::STATE_CLOSED) {
|
||||
if (connection_status == CONNECTION_CONNECTED) {
|
||||
@ -278,9 +320,14 @@ void WebSocketMultiplayerPeer::_poll_server() {
|
||||
// The user does not want new connections, dropping it.
|
||||
continue;
|
||||
}
|
||||
peers_map[id] = peer.ws;
|
||||
_send_ack(peer.ws, id);
|
||||
emit_signal("peer_connected", id);
|
||||
int32_t peer_id = id;
|
||||
Error err = peer.ws->put_packet((const uint8_t *)&peer_id, sizeof(peer_id));
|
||||
if (err == OK) {
|
||||
peers_map[id] = peer.ws;
|
||||
emit_signal("peer_connected", id);
|
||||
} else {
|
||||
ERR_PRINT("Failed to send ID to newly connected peer.");
|
||||
}
|
||||
continue;
|
||||
} else if (state == WebSocketPeer::STATE_CONNECTING) {
|
||||
continue; // Still connecting.
|
||||
@ -338,8 +385,19 @@ void WebSocketMultiplayerPeer::_poll_server() {
|
||||
}
|
||||
// Fetch packets
|
||||
int pkts = ws->get_available_packet_count();
|
||||
while (pkts && ws->get_ready_state() == WebSocketPeer::STATE_OPEN) {
|
||||
_process_multiplayer(ws, id);
|
||||
while (pkts > 0 && ws->get_ready_state() == WebSocketPeer::STATE_OPEN) {
|
||||
const uint8_t *in_buffer;
|
||||
int size = 0;
|
||||
Error err = ws->get_packet(&in_buffer, size);
|
||||
if (err != OK || size <= 0) {
|
||||
break;
|
||||
}
|
||||
Packet packet;
|
||||
packet.data = (uint8_t *)memalloc(size);
|
||||
memcpy(packet.data, in_buffer, size);
|
||||
packet.size = size;
|
||||
packet.source = E.key;
|
||||
incoming_packets.push_back(packet);
|
||||
pkts--;
|
||||
}
|
||||
}
|
||||
@ -371,180 +429,6 @@ Ref<WebSocketPeer> WebSocketMultiplayerPeer::get_peer(int p_id) const {
|
||||
return peers_map[p_id];
|
||||
}
|
||||
|
||||
void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) {
|
||||
ERR_FAIL_COND(!p_peer.is_valid());
|
||||
ERR_FAIL_COND(p_peer->get_ready_state() != WebSocketPeer::STATE_OPEN);
|
||||
|
||||
Vector<uint8_t> message = _make_pkt(p_type, 1, 0, (uint8_t *)&p_peer_id, 4);
|
||||
p_peer->put_packet(&(message.ptr()[0]), message.size());
|
||||
}
|
||||
|
||||
Vector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) {
|
||||
Vector<uint8_t> out;
|
||||
out.resize(PROTO_SIZE + p_data_size);
|
||||
|
||||
uint8_t *w = out.ptrw();
|
||||
memcpy(&w[0], &p_type, 1);
|
||||
memcpy(&w[1], &p_from, 4);
|
||||
memcpy(&w[5], &p_to, 4);
|
||||
memcpy(&w[PROTO_SIZE], p_data, p_data_size);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void WebSocketMultiplayerPeer::_send_ack(Ref<WebSocketPeer> p_peer, int32_t p_peer_id) {
|
||||
ERR_FAIL_COND(p_peer.is_null());
|
||||
// First of all, confirm the ID!
|
||||
_send_sys(p_peer, SYS_ID, p_peer_id);
|
||||
|
||||
// Then send the server peer (which will trigger connection_succeded in client)
|
||||
_send_sys(p_peer, SYS_ADD, 1);
|
||||
|
||||
for (const KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
|
||||
ERR_CONTINUE(E.value.is_null());
|
||||
|
||||
int32_t id = E.key;
|
||||
if (p_peer_id == id) {
|
||||
continue; // Skip the newly added peer (already confirmed)
|
||||
}
|
||||
|
||||
// Send new peer to others
|
||||
_send_sys(E.value, SYS_ADD, p_peer_id);
|
||||
// Send others to new peer
|
||||
_send_sys(E.value, SYS_ADD, id);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) {
|
||||
for (const KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
|
||||
int32_t id = E.key;
|
||||
if (p_peer_id != id) {
|
||||
_send_sys(E.value, SYS_DEL, p_peer_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size) {
|
||||
Packet packet;
|
||||
packet.data = (uint8_t *)memalloc(p_data_size);
|
||||
packet.size = p_data_size;
|
||||
packet.source = p_source;
|
||||
packet.destination = p_dest;
|
||||
memcpy(packet.data, &p_data[PROTO_SIZE], p_data_size);
|
||||
incoming_packets.push_back(packet);
|
||||
}
|
||||
|
||||
Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) {
|
||||
if (p_to == 1) {
|
||||
return OK; // Will not send to self
|
||||
|
||||
} else if (p_to == 0) {
|
||||
for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
|
||||
if (E.key != p_from) {
|
||||
E.value->put_packet(p_buffer, p_buffer_size);
|
||||
}
|
||||
}
|
||||
return OK; // Sent to all but sender
|
||||
|
||||
} else if (p_to < 0) {
|
||||
for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
|
||||
if (E.key != p_from && E.key != -p_to) {
|
||||
E.value->put_packet(p_buffer, p_buffer_size);
|
||||
}
|
||||
}
|
||||
return OK; // Sent to all but sender and excluded
|
||||
|
||||
} else {
|
||||
ERR_FAIL_COND_V(p_to == p_from, FAILED);
|
||||
|
||||
Ref<WebSocketPeer> peer_to = get_peer(p_to);
|
||||
ERR_FAIL_COND_V(peer_to.is_null(), FAILED);
|
||||
|
||||
return peer_to->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id) {
|
||||
ERR_FAIL_COND(!p_peer.is_valid());
|
||||
|
||||
const uint8_t *in_buffer;
|
||||
int size = 0;
|
||||
int data_size = 0;
|
||||
|
||||
Error err = p_peer->get_packet(&in_buffer, size);
|
||||
|
||||
ERR_FAIL_COND(err != OK);
|
||||
ERR_FAIL_COND(size < PROTO_SIZE);
|
||||
|
||||
data_size = size - PROTO_SIZE;
|
||||
|
||||
uint8_t type = 0;
|
||||
uint32_t from = 0;
|
||||
int32_t to = 0;
|
||||
memcpy(&type, in_buffer, 1);
|
||||
memcpy(&from, &in_buffer[1], 4);
|
||||
memcpy(&to, &in_buffer[5], 4);
|
||||
|
||||
if (is_server()) { // Server can resend
|
||||
|
||||
ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages
|
||||
ERR_FAIL_COND(from != p_peer_id); // Someone is cheating
|
||||
|
||||
if (to == 1) {
|
||||
// This is for the server
|
||||
_store_pkt(from, to, in_buffer, data_size);
|
||||
|
||||
} else if (to == 0) {
|
||||
// Broadcast, for us too
|
||||
_store_pkt(from, to, in_buffer, data_size);
|
||||
|
||||
} else if (to < -1) {
|
||||
// All but one, for us if not excluded
|
||||
_store_pkt(from, to, in_buffer, data_size);
|
||||
}
|
||||
// Relay if needed (i.e. "to" includes a peer that is not the server)
|
||||
_server_relay(from, to, in_buffer, size);
|
||||
|
||||
} else {
|
||||
if (type == SYS_NONE) {
|
||||
// Payload message
|
||||
_store_pkt(from, to, in_buffer, data_size);
|
||||
return;
|
||||
}
|
||||
|
||||
// System message
|
||||
ERR_FAIL_COND(data_size < 4);
|
||||
int id = 0;
|
||||
memcpy(&id, &in_buffer[PROTO_SIZE], 4);
|
||||
|
||||
switch (type) {
|
||||
case SYS_ADD: // Add peer
|
||||
if (id != 1) {
|
||||
peers_map[id] = Ref<WebSocketPeer>();
|
||||
} else {
|
||||
pending_peers.clear();
|
||||
connection_status = CONNECTION_CONNECTED;
|
||||
}
|
||||
emit_signal(SNAME("peer_connected"), id);
|
||||
if (id == 1) { // We just connected to the server
|
||||
emit_signal(SNAME("connection_succeeded"));
|
||||
}
|
||||
break;
|
||||
|
||||
case SYS_DEL: // Remove peer
|
||||
emit_signal(SNAME("peer_disconnected"), id);
|
||||
peers_map.erase(id);
|
||||
break;
|
||||
case SYS_ID: // Hello, server assigned ID
|
||||
unique_id = id;
|
||||
break;
|
||||
default:
|
||||
ERR_FAIL_MSG("Invalid multiplayer message.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketMultiplayerPeer::set_supported_protocols(const Vector<String> &p_protocols) {
|
||||
peer_config->set_supported_protocols(p_protocols);
|
||||
}
|
||||
|
@ -42,9 +42,6 @@ class WebSocketMultiplayerPeer : public MultiplayerPeer {
|
||||
GDCLASS(WebSocketMultiplayerPeer, MultiplayerPeer);
|
||||
|
||||
private:
|
||||
Vector<uint8_t> _make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size);
|
||||
void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size);
|
||||
Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size);
|
||||
Ref<WebSocketPeer> _create_peer();
|
||||
|
||||
protected:
|
||||
@ -59,7 +56,6 @@ protected:
|
||||
|
||||
struct Packet {
|
||||
int source = 0;
|
||||
int destination = 0;
|
||||
uint8_t *data = nullptr;
|
||||
uint32_t size = 0;
|
||||
};
|
||||
@ -90,20 +86,18 @@ protected:
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
void _send_ack(Ref<WebSocketPeer> p_peer, int32_t p_peer_id);
|
||||
void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id);
|
||||
void _send_del(int32_t p_peer_id);
|
||||
void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id);
|
||||
|
||||
void _poll_client();
|
||||
void _poll_server();
|
||||
void _clear();
|
||||
|
||||
public:
|
||||
/* MultiplayerPeer */
|
||||
void set_target_peer(int p_target_peer) override;
|
||||
int get_packet_peer() const override;
|
||||
int get_unique_id() const override;
|
||||
virtual void set_target_peer(int p_target_peer) override;
|
||||
virtual int get_packet_peer() const override;
|
||||
virtual int get_packet_channel() const override { return 0; }
|
||||
virtual TransferMode get_packet_mode() const override { return TRANSFER_MODE_RELIABLE; }
|
||||
virtual int get_unique_id() const override;
|
||||
virtual bool is_server_relay_supported() const override { return true; }
|
||||
|
||||
virtual int get_max_packet_size() const override;
|
||||
virtual bool is_server() const override;
|
||||
|
@ -78,6 +78,10 @@ bool MultiplayerPeer::is_refusing_new_connections() const {
|
||||
return refuse_connections;
|
||||
}
|
||||
|
||||
bool MultiplayerPeer::is_server_relay_supported() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
@ -86,6 +90,8 @@ void MultiplayerPeer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
|
||||
ClassDB::bind_method(D_METHOD("get_packet_channel"), &MultiplayerPeer::get_packet_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
|
||||
|
||||
@ -96,6 +102,8 @@ void MultiplayerPeer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
|
||||
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_server_relay_supported"), &MultiplayerPeer::is_server_relay_supported);
|
||||
|
||||
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_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
|
||||
@ -177,6 +185,14 @@ bool MultiplayerPeerExtension::is_refusing_new_connections() const {
|
||||
return MultiplayerPeer::is_refusing_new_connections();
|
||||
}
|
||||
|
||||
bool MultiplayerPeerExtension::is_server_relay_supported() const {
|
||||
bool can_relay;
|
||||
if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) {
|
||||
return can_relay;
|
||||
}
|
||||
return MultiplayerPeer::is_server_relay_supported();
|
||||
}
|
||||
|
||||
void MultiplayerPeerExtension::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
|
||||
GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
|
||||
|
@ -74,10 +74,13 @@ public:
|
||||
virtual TransferMode get_transfer_mode() const;
|
||||
virtual void set_refuse_new_connections(bool p_enable);
|
||||
virtual bool is_refusing_new_connections() const;
|
||||
virtual bool is_server_relay_supported() const;
|
||||
|
||||
virtual void set_target_peer(int p_peer_id) = 0;
|
||||
|
||||
virtual int get_packet_peer() const = 0;
|
||||
virtual TransferMode get_packet_mode() const = 0;
|
||||
virtual int get_packet_channel() const = 0;
|
||||
|
||||
virtual bool is_server() const = 0;
|
||||
|
||||
@ -123,12 +126,17 @@ public:
|
||||
virtual bool is_refusing_new_connections() const override;
|
||||
GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional.
|
||||
|
||||
virtual bool is_server_relay_supported() const override;
|
||||
GDVIRTUAL0RC(bool, _is_server_relay_supported); // Optional.
|
||||
|
||||
EXBIND1(set_transfer_channel, int);
|
||||
EXBIND0RC(int, get_transfer_channel);
|
||||
EXBIND1(set_transfer_mode, TransferMode);
|
||||
EXBIND0RC(TransferMode, get_transfer_mode);
|
||||
EXBIND1(set_target_peer, int);
|
||||
EXBIND0RC(int, get_packet_peer);
|
||||
EXBIND0RC(TransferMode, get_packet_mode);
|
||||
EXBIND0RC(int, get_packet_channel);
|
||||
EXBIND0RC(bool, is_server);
|
||||
EXBIND0(poll);
|
||||
EXBIND0RC(int, get_unique_id);
|
||||
|
Loading…
Reference in New Issue
Block a user