[Net] Move multiplayer to core subdir, split RPCManager.

Move multiplayer classes to "core/multiplayer" subdir.

Move the RPCConfig and enums (TransferMode, RPCMode) to a separate
file (multiplayer.h), and bind them to the global namespace.

Move the RPC handling code to its own class (RPCManager).

Renames "get_rpc_sender_id" to "get_remote_sender_id".
This commit is contained in:
Fabio Alessandrelli 2021-09-03 19:40:47 +02:00
parent b0b30aaf41
commit bf9aae09ba
57 changed files with 976 additions and 767 deletions

View file

@ -183,6 +183,7 @@ SConscript("os/SCsub")
SConscript("math/SCsub")
SConscript("crypto/SCsub")
SConscript("io/SCsub")
SConscript("multiplayer/SCsub")
SConscript("debugger/SCsub")
SConscript("input/SCsub")
SConscript("variant/SCsub")

View file

@ -31,6 +31,7 @@
#include "core_constants.h"
#include "core/input/input_event.h"
#include "core/multiplayer/multiplayer.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.h"
#include "core/variant/variant.h"
@ -609,6 +610,15 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_OBJECT_CORE);
BIND_CORE_ENUM_CONSTANT(METHOD_FLAGS_DEFAULT);
// rpc
BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_DISABLED", Multiplayer::RPC_MODE_DISABLED);
BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_ANY", Multiplayer::RPC_MODE_ANY);
BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_AUTH", Multiplayer::RPC_MODE_AUTHORITY);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE", Multiplayer::TRANSFER_MODE_UNRELIABLE);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_ORDERED", Multiplayer::TRANSFER_MODE_ORDERED);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_RELIABLE", Multiplayer::TRANSFER_MODE_RELIABLE);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT);

5
core/multiplayer/SCsub Normal file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.core_sources, "*.cpp")

View file

@ -0,0 +1,80 @@
/*************************************************************************/
/* multiplayer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MULTIPLAYER_H
#define MULTIPLAYER_H
#include "core/variant/binder_common.h"
#include "core/string/string_name.h"
namespace Multiplayer {
enum TransferMode {
TRANSFER_MODE_UNRELIABLE,
TRANSFER_MODE_ORDERED,
TRANSFER_MODE_RELIABLE
};
enum RPCMode {
RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
RPC_MODE_ANY, // Any peer can call this rpc()
RPC_MODE_AUTHORITY, // / Only the node's network authority (server by default) can call this rpc()
};
struct RPCConfig {
StringName name;
RPCMode rpc_mode = RPC_MODE_DISABLED;
bool sync = false;
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
int channel = 0;
bool operator==(RPCConfig const &p_other) const {
return name == p_other.name;
}
};
struct SortRPCConfig {
StringName::AlphCompare compare;
bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const {
return compare(p_a.name, p_b.name);
}
};
}; // namespace Multiplayer
// This is needed for proper docs generation (i.e. not "Multiplayer."-prefixed).
typedef Multiplayer::RPCMode RPCMode;
typedef Multiplayer::TransferMode TransferMode;
VARIANT_ENUM_CAST(RPCMode);
VARIANT_ENUM_CAST(TransferMode);
#endif // MULTIPLAYER_H

View file

@ -32,7 +32,8 @@
#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
#include "core/io/multiplayer_replicator.h"
#include "core/multiplayer/multiplayer_replicator.h"
#include "core/multiplayer/rpc_manager.h"
#include "scene/main/node.h"
#include <stdint.h>
@ -41,71 +42,18 @@
#include "core/os/os.h"
#endif
String _get_rpc_md5(const Node *p_node) {
String rpc_list;
const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods();
for (int i = 0; i < node_config.size(); i++) {
rpc_list += String(node_config[i].name);
#ifdef DEBUG_ENABLED
void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) {
if (EngineDebugger::is_profiling("multiplayer")) {
Array values;
values.push_back("bandwidth");
values.push_back(p_inout);
values.push_back(OS::get_singleton()->get_ticks_msec());
values.push_back(p_size);
EngineDebugger::profiler_add_frame_data("multiplayer", values);
}
if (p_node->get_script_instance()) {
const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
for (int i = 0; i < script_config.size(); i++) {
rpc_list += String(script_config[i].name);
}
}
return rpc_list.md5_text();
}
const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) {
const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods();
for (int i = 0; i < node_config.size(); i++) {
if (node_config[i].name == p_method) {
r_id = ((uint16_t)i) | (1 << 15);
return node_config[i];
}
}
if (p_node->get_script_instance()) {
const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
for (int i = 0; i < script_config.size(); i++) {
if (script_config[i].name == p_method) {
r_id = (uint16_t)i;
return script_config[i];
}
}
}
return MultiplayerAPI::RPCConfig();
}
const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) {
Vector<MultiplayerAPI::RPCConfig> config;
uint16_t id = p_id;
if (id & (1 << 15)) {
id = id & ~(1 << 15);
config = p_node->get_node_rpc_methods();
} else if (p_node->get_script_instance()) {
config = p_node->get_script_instance()->get_rpc_methods();
}
if (id < config.size()) {
return config[id];
}
return MultiplayerAPI::RPCConfig();
}
_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) {
switch (mode) {
case MultiplayerAPI::RPC_MODE_DISABLED: {
return false;
} break;
case MultiplayerAPI::RPC_MODE_ANY: {
return true;
} break;
case MultiplayerAPI::RPC_MODE_AUTHORITY: {
return !p_node->is_network_authority() && p_remote_id == p_node->get_network_authority();
} break;
}
return false;
}
#endif
void MultiplayerAPI::poll() {
if (!network_peer.is_valid() || network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) {
@ -129,9 +77,9 @@ void MultiplayerAPI::poll() {
break; // Something is wrong!
}
rpc_sender_id = sender;
remote_sender_id = sender;
_process_packet(sender, packet, len);
rpc_sender_id = 0;
remote_sender_id = 0;
if (!network_peer.is_valid()) {
break; // It's also possible that a packet or RPC caused a disconnection, so also check here.
@ -191,49 +139,16 @@ Ref<MultiplayerPeer> MultiplayerAPI::get_network_peer() const {
return network_peer;
}
#ifdef DEBUG_ENABLED
void _profile_node_data(const String &p_what, ObjectID p_id) {
if (EngineDebugger::is_profiling("multiplayer")) {
Array values;
values.push_back("node");
values.push_back(p_id);
values.push_back(p_what);
EngineDebugger::profiler_add_frame_data("multiplayer", values);
}
}
void _profile_bandwidth_data(const String &p_inout, int p_size) {
if (EngineDebugger::is_profiling("multiplayer")) {
Array values;
values.push_back("bandwidth");
values.push_back(p_inout);
values.push_back(OS::get_singleton()->get_ticks_msec());
values.push_back(p_size);
EngineDebugger::profiler_add_frame_data("multiplayer", values);
}
}
#endif
// Returns the packet size stripping the node path added when the node is not yet cached.
int get_packet_len(uint32_t p_node_target, int p_packet_len) {
if (p_node_target & 0x80000000) {
int ofs = p_node_target & 0x7FFFFFFF;
return p_packet_len - (p_packet_len - ofs);
} else {
return p_packet_len;
}
}
void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
#ifdef DEBUG_ENABLED
_profile_bandwidth_data("in", p_packet_len);
profile_bandwidth("in", p_packet_len);
#endif
// Extract the `packet_type` from the LSB three bits:
uint8_t packet_type = p_packet[0] & 7;
uint8_t packet_type = p_packet[0] & CMD_MASK;
switch (packet_type) {
case NETWORK_COMMAND_SIMPLIFY_PATH: {
@ -245,76 +160,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
} break;
case NETWORK_COMMAND_REMOTE_CALL: {
// Extract packet meta
int packet_min_size = 1;
int name_id_offset = 1;
ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
// Compute the meta size, which depends on the compression level.
int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT;
int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT;
switch (node_id_compression) {
case NETWORK_NODE_ID_COMPRESSION_8:
packet_min_size += 1;
name_id_offset += 1;
break;
case NETWORK_NODE_ID_COMPRESSION_16:
packet_min_size += 2;
name_id_offset += 2;
break;
case NETWORK_NODE_ID_COMPRESSION_32:
packet_min_size += 4;
name_id_offset += 4;
break;
default:
ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
}
switch (name_id_compression) {
case NETWORK_NAME_ID_COMPRESSION_8:
packet_min_size += 1;
break;
case NETWORK_NAME_ID_COMPRESSION_16:
packet_min_size += 2;
break;
default:
ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
}
ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
uint32_t node_target = 0;
switch (node_id_compression) {
case NETWORK_NODE_ID_COMPRESSION_8:
node_target = p_packet[1];
break;
case NETWORK_NODE_ID_COMPRESSION_16:
node_target = decode_uint16(p_packet + 1);
break;
case NETWORK_NODE_ID_COMPRESSION_32:
node_target = decode_uint32(p_packet + 1);
break;
default:
// Unreachable, checked before.
CRASH_NOW();
}
Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
uint16_t name_id = 0;
switch (name_id_compression) {
case NETWORK_NAME_ID_COMPRESSION_8:
name_id = p_packet[name_id_offset];
break;
case NETWORK_NAME_ID_COMPRESSION_16:
name_id = decode_uint16(p_packet + name_id_offset);
break;
default:
// Unreachable, checked before.
CRASH_NOW();
}
const int packet_len = get_packet_len(node_target, p_packet_len);
_process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
rpc_manager->process_rpc(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_RAW: {
@ -332,101 +178,6 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
}
}
Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
Node *node = nullptr;
if (p_node_target & 0x80000000) {
// Use full path (not cached yet).
int ofs = p_node_target & 0x7FFFFFFF;
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
String paths;
paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
NodePath np = paths;
node = root_node->get_node(np);
if (!node) {
ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
}
return node;
} else {
// Use cached path.
return get_cached_node(p_from, p_node_target);
}
}
void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
// Check that remote can call the RPC on this node.
const RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id);
ERR_FAIL_COND(config.name == StringName());
bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from);
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_network_authority()) + ".");
int argc = 0;
bool byte_only = false;
const bool byte_only_or_no_args = ((p_packet[0] & 64) >> BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1;
if (byte_only_or_no_args) {
if (p_offset < p_packet_len) {
// This packet contains only bytes.
argc = 1;
byte_only = true;
} else {
// This rpc calls a method without parameters.
}
} else {
// Normal variant, takes the argument count from the packet.
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
argc = p_packet[p_offset];
p_offset += 1;
}
Vector<Variant> args;
Vector<const Variant *> argp;
args.resize(argc);
argp.resize(argc);
#ifdef DEBUG_ENABLED
_profile_node_data("in_rpc", p_node->get_instance_id());
#endif
if (byte_only) {
Vector<uint8_t> pure_data;
const int len = p_packet_len - p_offset;
pure_data.resize(len);
memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
args.write[0] = pure_data;
argp.write[0] = &args[0];
p_offset += len;
} else {
for (int i = 0; i < argc; i++) {
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
int vlen;
Error err = decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
argp.write[i] = &args[i];
p_offset += vlen;
}
}
Callable::CallError ce;
p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce);
if (ce.error != Callable::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce);
error = "RPC - " + error;
ERR_PRINT(error);
}
}
void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
int ofs = 1;
@ -449,7 +200,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
Node *node = root_node->get_node(path);
ERR_FAIL_COND(node == nullptr);
const bool valid_rpc_checksum = _get_rpc_md5(node) == methods_md5;
const bool valid_rpc_checksum = rpc_manager->get_rpc_md5(node) == methods_md5;
if (valid_rpc_checksum == false) {
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
}
@ -471,7 +222,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
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(Multiplayer::TRANSFER_MODE_RELIABLE);
network_peer->set_target_peer(p_from);
network_peer->put_packet(packet.ptr(), packet.size());
}
@ -532,7 +283,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC
const int path_len = encode_cstring(path.get_data(), nullptr);
// Extract MD5 from rpc methods list.
const String methods_md5 = _get_rpc_md5(p_node);
const String methods_md5 = rpc_manager->get_rpc_md5(p_node);
const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
Vector<uint8_t> packet;
@ -551,7 +302,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC
for (int &E : peers_to_add) {
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(Multiplayer::TRANSFER_MODE_RELIABLE);
network_peer->put_packet(packet.ptr(), packet.size());
psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed.
@ -716,188 +467,6 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui
return OK;
}
void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree.");
ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree.");
ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected.");
ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255.");
if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + ".");
ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + ".");
}
NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path());
ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
// See if the path is cached.
PathSentCache *psc = path_send_cache.getptr(from_path);
if (!psc) {
// Path is not cached, create.
path_send_cache[from_path] = PathSentCache();
psc = path_send_cache.getptr(from_path);
psc->id = last_send_cache_id++;
}
// See if all peers have cached path (if so, call can be fast).
const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to);
// Create base packet, lots of hardcode because it must be tight.
int ofs = 0;
#define MAKE_ROOM(m_amount) \
if (packet_cache.size() < m_amount) \
packet_cache.resize(m_amount);
// Encode meta.
// The meta is composed by a single byte that contains (starting from the least significant bit):
// - `NetworkCommands` in the first three bits.
// - `NetworkNodeIdCompression` in the next 2 bits.
// - `NetworkNameIdCompression` in the next 1 bit.
// - `byte_only_or_no_args` in the next 1 bit.
// - So we still have the last bit free!
uint8_t command_type = NETWORK_COMMAND_REMOTE_CALL;
uint8_t node_id_compression = UINT8_MAX;
uint8_t name_id_compression = UINT8_MAX;
bool byte_only_or_no_args = false;
MAKE_ROOM(1);
// The meta is composed along the way, so just set 0 for now.
packet_cache.write[0] = 0;
ofs += 1;
// Encode Node ID.
if (has_all_peers) {
// Compress the node ID only if all the target peers already know it.
if (psc->id >= 0 && psc->id <= 255) {
// We can encode the id in 1 byte
node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
MAKE_ROOM(ofs + 1);
packet_cache.write[ofs] = static_cast<uint8_t>(psc->id);
ofs += 1;
} else if (psc->id >= 0 && psc->id <= 65535) {
// We can encode the id in 2 bytes
node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
MAKE_ROOM(ofs + 2);
encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs]));
ofs += 2;
} else {
// Too big, let's use 4 bytes.
node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
MAKE_ROOM(ofs + 4);
encode_uint32(psc->id, &(packet_cache.write[ofs]));
ofs += 4;
}
} else {
// The targets don't know the node yet, so we need to use 32 bits int.
node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
MAKE_ROOM(ofs + 4);
encode_uint32(psc->id, &(packet_cache.write[ofs]));
ofs += 4;
}
// Encode method ID
if (p_rpc_id <= UINT8_MAX) {
// The ID fits in 1 byte
name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
MAKE_ROOM(ofs + 1);
packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id);
ofs += 1;
} else {
// The ID is larger, let's use 2 bytes
name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
MAKE_ROOM(ofs + 2);
encode_uint16(p_rpc_id, &(packet_cache.write[ofs]));
ofs += 2;
}
if (p_argcount == 0) {
byte_only_or_no_args = true;
} else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
byte_only_or_no_args = true;
// Special optimization when only the byte vector is sent.
const Vector<uint8_t> data = *p_arg[0];
MAKE_ROOM(ofs + data.size());
memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
ofs += data.size();
} else {
// Arguments
MAKE_ROOM(ofs + 1);
packet_cache.write[ofs] = p_argcount;
ofs += 1;
for (int i = 0; i < p_argcount; i++) {
int len(0);
Error err = encode_and_compress_variant(*p_arg[i], nullptr, len);
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
MAKE_ROOM(ofs + len);
encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
ofs += len;
}
}
ERR_FAIL_COND(command_type > 7);
ERR_FAIL_COND(node_id_compression > 3);
ERR_FAIL_COND(name_id_compression > 1);
// We can now set the meta
packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT);
#ifdef DEBUG_ENABLED
_profile_bandwidth_data("out", ofs);
#endif
// 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);
if (has_all_peers) {
// They all have verified paths, so send fast.
network_peer->set_target_peer(p_to); // To all of you.
network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
} 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);
// Not all verified path, so send one by one.
// Append path at the end, since we will need it for some packets.
CharString pname = String(from_path).utf8();
int path_len = encode_cstring(pname.get_data(), nullptr);
MAKE_ROOM(ofs + path_len);
encode_cstring(pname.get_data(), &(packet_cache.write[ofs]));
for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
if (p_to < 0 && E->get() == -p_to) {
continue; // Continue, excluded.
}
if (p_to > 0 && E->get() != p_to) {
continue; // Continue, not for this peer.
}
Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
ERR_CONTINUE(!F); // Should never happen.
network_peer->set_target_peer(E->get()); // To this one specifically.
if (F->get()) {
// This one confirmed path, so use id.
encode_uint32(psc->id, &(packet_cache.write[1]));
network_peer->put_packet(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.
network_peer->put_packet(packet_cache.ptr(), ofs + path_len);
}
}
}
}
void MultiplayerAPI::_add_peer(int p_id) {
connected_peers.insert(p_id);
path_get_cache.insert(p_id, PathGetCache());
@ -934,72 +503,15 @@ void MultiplayerAPI::_server_disconnected() {
emit_signal(SNAME("server_disconnected"));
}
void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active.");
ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected.");
int node_id = network_peer->get_unique_id();
bool call_local_native = false;
bool call_local_script = false;
uint16_t rpc_id = UINT16_MAX;
const RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id);
ERR_FAIL_COND_MSG(config.name == StringName(),
vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path()));
if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
if (rpc_id & (1 << 15)) {
call_local_native = config.sync;
} else {
call_local_script = config.sync;
}
}
if (p_peer_id != node_id) {
#ifdef DEBUG_ENABLED
_profile_node_data("out_rpc", p_node->get_instance_id());
#endif
_send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
}
if (call_local_native) {
int temp_id = rpc_sender_id;
rpc_sender_id = get_network_unique_id();
Callable::CallError ce;
p_node->call(p_method, p_arg, p_argcount, ce);
rpc_sender_id = temp_id;
if (ce.error != Callable::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in local call: - " + error + ".";
ERR_PRINT(error);
return;
}
}
if (call_local_script) {
int temp_id = rpc_sender_id;
rpc_sender_id = get_network_unique_id();
Callable::CallError ce;
ce.error = Callable::CallError::CALL_OK;
p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
rpc_sender_id = temp_id;
if (ce.error != Callable::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in script local call: - " + error + ".";
ERR_PRINT(error);
return;
}
}
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, int p_channel) {
Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer::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(!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.");
MAKE_ROOM(p_data.size() + 1);
if (packet_cache.size() < p_data.size() + 1) {
packet_cache.resize(p_data.size() + 1);
}
const uint8_t *r = p_data.ptr();
packet_cache.write[0] = NETWORK_COMMAND_RAW;
memcpy(&packet_cache.write[1], &r[0], p_data.size());
@ -1024,6 +536,14 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac
emit_signal(SNAME("network_peer_packet"), p_from, out);
}
bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) {
const PathSentCache *psc = path_send_cache.getptr(p_path);
ERR_FAIL_COND_V(!psc, false);
const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer);
ERR_FAIL_COND_V(!F, false); // Should never happen.
return F->get();
}
bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) {
// See if the path is cached.
PathSentCache *psc = path_send_cache.getptr(p_path);
@ -1092,23 +612,23 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
return allow_object_decoding;
}
MultiplayerReplicator *MultiplayerAPI::get_replicator() const {
return replicator;
}
void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
replicator->scene_enter_exit_notify(p_scene, p_node, p_enter);
}
void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
rpc_manager->rpcp(p_node, p_peer_id, p_method, p_arg, p_argcount);
}
void MultiplayerAPI::_bind_methods() {
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("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("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
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_unique_id"), &MultiplayerAPI::get_network_unique_id);
ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server);
ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id);
ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear);
@ -1133,18 +653,16 @@ void MultiplayerAPI::_bind_methods() {
ADD_SIGNAL(MethodInfo("connected_to_server"));
ADD_SIGNAL(MethodInfo("connection_failed"));
ADD_SIGNAL(MethodInfo("server_disconnected"));
BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
BIND_ENUM_CONSTANT(RPC_MODE_ANY);
BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY);
}
MultiplayerAPI::MultiplayerAPI() {
replicator = memnew(MultiplayerReplicator(this));
rpc_manager = memnew(RPCManager(this));
clear();
}
MultiplayerAPI::~MultiplayerAPI() {
clear();
memdelete(replicator);
memdelete(rpc_manager);
}

View file

@ -31,41 +31,17 @@
#ifndef MULTIPLAYER_API_H
#define MULTIPLAYER_API_H
#include "core/io/multiplayer_peer.h"
#include "core/io/resource_uid.h"
#include "core/multiplayer/multiplayer.h"
#include "core/multiplayer/multiplayer_peer.h"
#include "core/object/ref_counted.h"
class MultiplayerReplicator;
class RPCManager;
class MultiplayerAPI : public RefCounted {
GDCLASS(MultiplayerAPI, RefCounted);
public:
enum RPCMode {
RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
RPC_MODE_ANY, // Any peer can call this rpc()
RPC_MODE_AUTHORITY, // Only the node's network authority (server by default) can call this rpc()
};
struct RPCConfig {
StringName name;
RPCMode rpc_mode = RPC_MODE_DISABLED;
bool sync = false;
MultiplayerPeer::TransferMode transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
int channel = 0;
bool operator==(RPCConfig const &p_other) const {
return name == p_other.name;
}
};
struct SortRPCConfig {
StringName::AlphCompare compare;
bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const {
return compare(p_a.name, p_b.name);
}
};
enum NetworkCommands {
NETWORK_COMMAND_REMOTE_CALL = 0,
NETWORK_COMMAND_SIMPLIFY_PATH,
@ -73,24 +49,20 @@ public:
NETWORK_COMMAND_RAW,
NETWORK_COMMAND_SPAWN,
NETWORK_COMMAND_DESPAWN,
NETWORK_COMMAND_SYNC, // This is the max we can have. We should optmize simplify/confirm, possibly spawn/despawn.
};
enum NetworkNodeIdCompression {
NETWORK_NODE_ID_COMPRESSION_8 = 0,
NETWORK_NODE_ID_COMPRESSION_16,
NETWORK_NODE_ID_COMPRESSION_32,
};
enum NetworkNameIdCompression {
NETWORK_NAME_ID_COMPRESSION_8 = 0,
NETWORK_NAME_ID_COMPRESSION_16,
NETWORK_COMMAND_SYNC,
};
// For each command, the 4 MSB can contain custom flags, as defined by subsystems.
enum {
NODE_ID_COMPRESSION_SHIFT = 3,
NAME_ID_COMPRESSION_SHIFT = 5,
BYTE_ONLY_OR_NO_ARGS_SHIFT = 6,
CMD_FLAG_0_SHIFT = 4,
CMD_FLAG_1_SHIFT = 5,
CMD_FLAG_2_SHIFT = 6,
CMD_FLAG_3_SHIFT = 7,
};
// This is the mask that will be used to extract the command.
enum {
CMD_MASK = 7, // 0x7 -> 0b00001111
};
private:
@ -111,29 +83,30 @@ private:
};
Ref<MultiplayerPeer> network_peer;
int rpc_sender_id = 0;
Set<int> connected_peers;
int remote_sender_id = 0;
int remote_sender_override = 0;
HashMap<NodePath, PathSentCache> path_send_cache;
Map<int, PathGetCache> path_get_cache;
int last_send_cache_id;
Vector<uint8_t> packet_cache;
Node *root_node = nullptr;
bool allow_object_decoding = false;
MultiplayerReplicator *replicator = nullptr;
RPCManager *rpc_manager = nullptr;
protected:
static void _bind_methods();
bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
public:
void poll();
void clear();
@ -141,18 +114,20 @@ public:
Node *get_root_node();
void set_network_peer(const Ref<MultiplayerPeer> &p_peer);
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, int p_channel = 0);
Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, Multiplayer::TransferMode p_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0);
Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);
Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len);
// 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, const StringName &p_method, const Variant **p_arg, int p_argcount);
// Called by Node._notification
void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);
// Called by replicator
bool send_confirm_path(Node *p_node, NodePath p_path, int p_target, int &p_id);
Node *get_cached_node(int p_from, uint32_t p_node_id);
bool is_cache_confirmed(NodePath p_path, int p_peer);
void _add_peer(int p_id);
void _del_peer(int p_id);
@ -162,7 +137,9 @@ public:
bool has_network_peer() const { return network_peer.is_valid(); }
Vector<int> get_network_connected_peers() const;
int get_rpc_sender_id() const { return rpc_sender_id; }
const Set<int> get_connected_peers() const { return connected_peers; }
int get_remote_sender_id() const { return remote_sender_override ? remote_sender_override : remote_sender_id; }
void set_remote_sender_override(int p_id) { remote_sender_override = p_id; }
int get_network_unique_id() const;
bool is_network_server() const;
void set_refuse_new_network_connections(bool p_refuse);
@ -171,12 +148,15 @@ public:
void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const;
MultiplayerReplicator *get_replicator() const;
MultiplayerReplicator *get_replicator() const { return replicator; }
RPCManager *get_rpc_manager() const { return rpc_manager; }
#ifdef DEBUG_ENABLED
void profile_bandwidth(const String &p_inout, int p_size);
#endif
MultiplayerAPI();
~MultiplayerAPI();
};
VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
#endif // MULTIPLAYER_API_H

View file

@ -75,10 +75,6 @@ void MultiplayerPeer::_bind_methods() {
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_ORDERED);
BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);

View file

@ -32,6 +32,7 @@
#define NETWORKED_MULTIPLAYER_PEER_H
#include "core/io/packet_peer.h"
#include "core/multiplayer/multiplayer.h"
class MultiplayerPeer : public PacketPeer {
GDCLASS(MultiplayerPeer, PacketPeer);
@ -44,11 +45,6 @@ public:
TARGET_PEER_BROADCAST = 0,
TARGET_PEER_SERVER = 1
};
enum TransferMode {
TRANSFER_MODE_UNRELIABLE,
TRANSFER_MODE_UNRELIABLE_ORDERED,
TRANSFER_MODE_RELIABLE,
};
enum ConnectionStatus {
CONNECTION_DISCONNECTED,
@ -58,8 +54,8 @@ public:
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 TransferMode get_transfer_mode() const = 0;
virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) = 0;
virtual Multiplayer::TransferMode get_transfer_mode() const = 0;
virtual void set_target_peer(int p_peer_id) = 0;
virtual int get_packet_peer() const = 0;
@ -79,7 +75,6 @@ public:
MultiplayerPeer() {}
};
VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode)
VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus)
#endif // NETWORKED_MULTIPLAYER_PEER_H

View file

@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "core/io/multiplayer_replicator.h"
#include "core/multiplayer/multiplayer_replicator.h"
#include "core/io/marshalls.h"
#include "scene/main/node.h"
@ -88,7 +88,7 @@ Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id
}
int ofs = 0;
uint8_t *ptr = packet_cache.ptrw();
ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC + ((same_size ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT);
ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC | (same_size ? BYTE_OR_ZERO_FLAG : 0);
ofs = 1;
ofs += encode_uint64(p_scene_id, &ptr[ofs]);
ptr[ofs] = cfg.sync_recv++;
@ -116,7 +116,7 @@ Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id
Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer();
network_peer->set_target_peer(p_peer);
network_peer->set_transfer_channel(0);
network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_UNRELIABLE);
network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE);
return network_peer->put_packet(ptr, ofs);
}
@ -125,7 +125,7 @@ void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, c
ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id));
SceneConfig &cfg = replications[p_id];
ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_network_server(), "The defualt implementation only allows sync packets from the server");
const bool same_size = ((p_packet[0] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1;
const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG;
int ofs = SYNC_CMD_OFFSET;
int time = p_packet[ofs];
// Skip old update.
@ -218,7 +218,7 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re
int nlen = encode_cstring(cname.get_data(), nullptr);
MAKE_ROOM(SPAWN_CMD_OFFSET + 4 + 4 + nlen + state_len);
uint8_t *ptr = packet_cache.ptrw();
ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT);
ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) | (is_raw ? BYTE_OR_ZERO_FLAG : 0);
ofs = 1;
ofs += encode_uint64(p_scene_id, &ptr[ofs]);
ofs += encode_uint32(path_id, &ptr[ofs]);
@ -236,7 +236,7 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re
Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer();
network_peer->set_target_peer(p_peer_id);
network_peer->set_transfer_channel(0);
network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
return network_peer->put_packet(ptr, ofs + state_len);
}
@ -262,7 +262,7 @@ void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const Res
if (cfg.mode == REPLICATION_MODE_SERVER && p_from == 1) {
String scene_path = ResourceUID::get_singleton()->get_id_path(p_scene_id);
if (p_spawn) {
const bool is_raw = ((p_packet[0] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1;
const bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name));
RES res = ResourceLoader::load(scene_path);
@ -308,7 +308,7 @@ void MultiplayerReplicator::process_spawn_despawn(int p_from, const uint8_t *p_p
const SceneConfig &cfg = replications[id];
if (cfg.on_spawn_despawn_receive.is_valid()) {
int ofs = SPAWN_CMD_OFFSET;
bool is_raw = ((p_packet[0] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1;
bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
Variant data;
int left = p_packet_len - ofs;
if (is_raw && left) {
@ -466,7 +466,7 @@ Error MultiplayerReplicator::spawn_config(const ResourceUID::ID &p_id, Replicati
SceneConfig cfg;
cfg.mode = p_mode;
for (int i = 0; i < p_props.size(); i++) {
cfg.properties.push_back(StringName(p_props[i]));
cfg.properties.push_back(p_props[i]);
}
cfg.on_spawn_despawn_send = p_on_send;
cfg.on_spawn_despawn_receive = p_on_recv;
@ -505,7 +505,7 @@ Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUI
}
MAKE_ROOM(SPAWN_CMD_OFFSET + data_size);
uint8_t *ptr = packet_cache.ptrw();
ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT);
ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << BYTE_OR_ZERO_SHIFT);
encode_uint64(p_scene_id, &ptr[1]);
if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {
const PackedByteArray pba = p_data;
@ -516,7 +516,7 @@ Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUI
Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer();
network_peer->set_target_peer(p_peer_id);
network_peer->set_transfer_channel(0);
network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
return network_peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size);
}
@ -740,7 +740,7 @@ Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_p
return OK;
}
Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel) {
Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
const SceneConfig &cfg = replications[p_scene_id];
@ -768,7 +768,7 @@ void MultiplayerReplicator::_bind_methods() {
ClassDB::bind_method(D_METHOD("spawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::spawn, DEFVAL(0));
ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_despawn, DEFVAL(Variant()), DEFVAL(NodePath()));
ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_spawn, DEFVAL(Variant()), DEFVAL(NodePath()));
ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("sync_all", "scene_id", "peer_id"), &MultiplayerReplicator::sync_all, DEFVAL(0));
ClassDB::bind_method(D_METHOD("track", "scene_id", "object"), &MultiplayerReplicator::track);
ClassDB::bind_method(D_METHOD("untrack", "scene_id", "object"), &MultiplayerReplicator::untrack);

View file

@ -31,8 +31,9 @@
#ifndef MULTIPLAYER_REPLICATOR_H
#define MULTIPLAYER_REPLICATOR_H
#include "core/io/multiplayer_api.h"
#include "core/multiplayer/multiplayer_api.h"
#include "core/io/resource_uid.h"
#include "core/templates/hash_map.h"
#include "core/variant/typed_array.h"
@ -68,6 +69,14 @@ protected:
static void _bind_methods();
private:
enum {
BYTE_OR_ZERO_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT,
};
enum {
BYTE_OR_ZERO_FLAG = 1 << BYTE_OR_ZERO_SHIFT,
};
MultiplayerAPI *multiplayer = nullptr;
Vector<uint8_t> packet_cache;
Map<ResourceUID::ID, SceneConfig> replications;
@ -108,7 +117,7 @@ public:
// Sync
Error sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());
Error sync_all(const ResourceUID::ID &p_scene_id, int p_peer);
Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, MultiplayerPeer::TransferMode p_mode, int p_channel);
Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_mode, int p_channel);
void track(const ResourceUID::ID &p_scene_id, Object *p_object);
void untrack(const ResourceUID::ID &p_scene_id, Object *p_object);

View file

@ -0,0 +1,525 @@
/*************************************************************************/
/* rpc_manager.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "core/multiplayer/rpc_manager.h"
#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
#include "core/multiplayer/multiplayer_api.h"
#include "scene/main/node.h"
#ifdef DEBUG_ENABLED
_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {
if (EngineDebugger::is_profiling("multiplayer")) {
Array values;
values.push_back("node");
values.push_back(p_id);
values.push_back(p_what);
EngineDebugger::profiler_add_frame_data("multiplayer", values);
}
}
#else
_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {}
#endif
// Returns the packet size stripping the node path added when the node is not yet cached.
int get_packet_len(uint32_t p_node_target, int p_packet_len) {
if (p_node_target & 0x80000000) {
int ofs = p_node_target & 0x7FFFFFFF;
return p_packet_len - (p_packet_len - ofs);
} else {
return p_packet_len;
}
}
const Multiplayer::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) {
const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods();
for (int i = 0; i < node_config.size(); i++) {
if (node_config[i].name == p_method) {
r_id = ((uint16_t)i) | (1 << 15);
return node_config[i];
}
}
if (p_node->get_script_instance()) {
const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
for (int i = 0; i < script_config.size(); i++) {
if (script_config[i].name == p_method) {
r_id = (uint16_t)i;
return script_config[i];
}
}
}
return Multiplayer::RPCConfig();
}
const Multiplayer::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) {
Vector<Multiplayer::RPCConfig> config;
uint16_t id = p_id;
if (id & (1 << 15)) {
id = id & ~(1 << 15);
config = p_node->get_node_rpc_methods();
} else if (p_node->get_script_instance()) {
config = p_node->get_script_instance()->get_rpc_methods();
}
if (id < config.size()) {
return config[id];
}
return Multiplayer::RPCConfig();
}
_FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int p_remote_id) {
switch (mode) {
case Multiplayer::RPC_MODE_DISABLED: {
return false;
} break;
case Multiplayer::RPC_MODE_ANY: {
return true;
} break;
case Multiplayer::RPC_MODE_AUTHORITY: {
return !p_node->is_network_authority() && p_remote_id == p_node->get_network_authority();
} break;
}
return false;
}
String RPCManager::get_rpc_md5(const Node *p_node) {
String rpc_list;
const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods();
for (int i = 0; i < node_config.size(); i++) {
rpc_list += String(node_config[i].name);
}
if (p_node->get_script_instance()) {
const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
for (int i = 0; i < script_config.size(); i++) {
rpc_list += String(script_config[i].name);
}
}
return rpc_list.md5_text();
}
Node *RPCManager::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
Node *node = nullptr;
if (p_node_target & 0x80000000) {
// Use full path (not cached yet).
int ofs = p_node_target & 0x7FFFFFFF;
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
String paths;
paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
NodePath np = paths;
node = multiplayer->get_root_node()->get_node(np);
if (!node) {
ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
}
return node;
} else {
// Use cached path.
return multiplayer->get_cached_node(p_from, p_node_target);
}
}
void RPCManager::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {
// Extract packet meta
int packet_min_size = 1;
int name_id_offset = 1;
ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
// Compute the meta size, which depends on the compression level.
int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT;
int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT;
switch (node_id_compression) {
case NETWORK_NODE_ID_COMPRESSION_8:
packet_min_size += 1;
name_id_offset += 1;
break;
case NETWORK_NODE_ID_COMPRESSION_16:
packet_min_size += 2;
name_id_offset += 2;
break;
case NETWORK_NODE_ID_COMPRESSION_32:
packet_min_size += 4;
name_id_offset += 4;
break;
default:
ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
}
switch (name_id_compression) {
case NETWORK_NAME_ID_COMPRESSION_8:
packet_min_size += 1;
break;
case NETWORK_NAME_ID_COMPRESSION_16:
packet_min_size += 2;
break;
default:
ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
}
ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
uint32_t node_target = 0;
switch (node_id_compression) {
case NETWORK_NODE_ID_COMPRESSION_8:
node_target = p_packet[1];
break;
case NETWORK_NODE_ID_COMPRESSION_16:
node_target = decode_uint16(p_packet + 1);
break;
case NETWORK_NODE_ID_COMPRESSION_32:
node_target = decode_uint32(p_packet + 1);
break;
default:
// Unreachable, checked before.
CRASH_NOW();
}
Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
uint16_t name_id = 0;
switch (name_id_compression) {
case NETWORK_NAME_ID_COMPRESSION_8:
name_id = p_packet[name_id_offset];
break;
case NETWORK_NAME_ID_COMPRESSION_16:
name_id = decode_uint16(p_packet + name_id_offset);
break;
default:
// Unreachable, checked before.
CRASH_NOW();
}
const int packet_len = get_packet_len(node_target, p_packet_len);
_process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
}
void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
// Check that remote can call the RPC on this node.
const Multiplayer::RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id);
ERR_FAIL_COND(config.name == StringName());
bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from);
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_network_authority()) + ".");
int argc = 0;
bool byte_only = false;
const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG;
if (byte_only_or_no_args) {
if (p_offset < p_packet_len) {
// This packet contains only bytes.
argc = 1;
byte_only = true;
} else {
// This rpc calls a method without parameters.
}
} else {
// Normal variant, takes the argument count from the packet.
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
argc = p_packet[p_offset];
p_offset += 1;
}
Vector<Variant> args;
Vector<const Variant *> argp;
args.resize(argc);
argp.resize(argc);
#ifdef DEBUG_ENABLED
_profile_node_data("in_rpc", p_node->get_instance_id());
#endif
if (byte_only) {
Vector<uint8_t> pure_data;
const int len = p_packet_len - p_offset;
pure_data.resize(len);
memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
args.write[0] = pure_data;
argp.write[0] = &args[0];
p_offset += len;
} else {
for (int i = 0; i < argc; i++) {
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
int vlen;
Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
argp.write[i] = &args[i];
p_offset += vlen;
}
}
Callable::CallError ce;
p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce);
if (ce.error != Callable::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce);
error = "RPC - " + error;
ERR_PRINT(error);
}
}
void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer();
ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree.");
ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree.");
ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected.");
ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255.");
if (p_to != 0 && !multiplayer->get_network_connected_peers().has(ABS(p_to))) {
ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + ".");
ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + ".");
}
NodePath from_path = (multiplayer->get_root_node()->get_path()).rel_path_to(p_from->get_path());
ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
// See if all peers have cached path (if so, call can be fast).
int psc_id;
const bool has_all_peers = multiplayer->send_confirm_path(p_from, from_path, p_to, psc_id);
// Create base packet, lots of hardcode because it must be tight.
int ofs = 0;
#define MAKE_ROOM(m_amount) \
if (packet_cache.size() < m_amount) \
packet_cache.resize(m_amount);
// Encode meta.
uint8_t command_type = MultiplayerAPI::NETWORK_COMMAND_REMOTE_CALL;
uint8_t node_id_compression = UINT8_MAX;
uint8_t name_id_compression = UINT8_MAX;
bool byte_only_or_no_args = false;
MAKE_ROOM(1);
// The meta is composed along the way, so just set 0 for now.
packet_cache.write[0] = 0;
ofs += 1;
// Encode Node ID.
if (has_all_peers) {
// Compress the node ID only if all the target peers already know it.
if (psc_id >= 0 && psc_id <= 255) {
// We can encode the id in 1 byte
node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
MAKE_ROOM(ofs + 1);
packet_cache.write[ofs] = static_cast<uint8_t>(psc_id);
ofs += 1;
} else if (psc_id >= 0 && psc_id <= 65535) {
// We can encode the id in 2 bytes
node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
MAKE_ROOM(ofs + 2);
encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs]));
ofs += 2;
} else {
// Too big, let's use 4 bytes.
node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
MAKE_ROOM(ofs + 4);
encode_uint32(psc_id, &(packet_cache.write[ofs]));
ofs += 4;
}
} else {
// The targets don't know the node yet, so we need to use 32 bits int.
node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
MAKE_ROOM(ofs + 4);
encode_uint32(psc_id, &(packet_cache.write[ofs]));
ofs += 4;
}
// Encode method ID
if (p_rpc_id <= UINT8_MAX) {
// The ID fits in 1 byte
name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
MAKE_ROOM(ofs + 1);
packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id);
ofs += 1;
} else {
// The ID is larger, let's use 2 bytes
name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
MAKE_ROOM(ofs + 2);
encode_uint16(p_rpc_id, &(packet_cache.write[ofs]));
ofs += 2;
}
if (p_argcount == 0) {
byte_only_or_no_args = true;
} else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
byte_only_or_no_args = true;
// Special optimization when only the byte vector is sent.
const Vector<uint8_t> data = *p_arg[0];
MAKE_ROOM(ofs + data.size());
memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
ofs += data.size();
} else {
// Arguments
MAKE_ROOM(ofs + 1);
packet_cache.write[ofs] = p_argcount;
ofs += 1;
for (int i = 0; i < p_argcount; i++) {
int len(0);
Error err = multiplayer->encode_and_compress_variant(*p_arg[i], nullptr, len);
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
MAKE_ROOM(ofs + len);
multiplayer->encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
ofs += len;
}
}
ERR_FAIL_COND(command_type > 7);
ERR_FAIL_COND(node_id_compression > 3);
ERR_FAIL_COND(name_id_compression > 1);
// We can now set the meta
packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0);
#ifdef DEBUG_ENABLED
multiplayer->profile_bandwidth("out", ofs);
#endif
// 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);
if (has_all_peers) {
// They all have verified paths, so send fast.
network_peer->set_target_peer(p_to); // To all of you.
network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
} 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);
// Not all verified path, so send one by one.
// Append path at the end, since we will need it for some packets.
CharString pname = String(from_path).utf8();
int path_len = encode_cstring(pname.get_data(), nullptr);
MAKE_ROOM(ofs + path_len);
encode_cstring(pname.get_data(), &(packet_cache.write[ofs]));
for (const int &P : multiplayer->get_connected_peers()) {
if (p_to < 0 && P == -p_to) {
continue; // Continue, excluded.
}
if (p_to > 0 && P != p_to) {
continue; // Continue, not for this peer.
}
bool confirmed = multiplayer->is_cache_confirmed(from_path, P);
network_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]));
network_peer->put_packet(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.
network_peer->put_packet(packet_cache.ptr(), ofs + path_len);
}
}
}
}
void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer();
ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active.");
ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected.");
int node_id = network_peer->get_unique_id();
bool call_local_native = false;
bool call_local_script = false;
uint16_t rpc_id = UINT16_MAX;
const Multiplayer::RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id);
ERR_FAIL_COND_MSG(config.name == StringName(),
vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path()));
if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
if (rpc_id & (1 << 15)) {
call_local_native = config.sync;
} else {
call_local_script = config.sync;
}
}
if (p_peer_id != node_id) {
#ifdef DEBUG_ENABLED
_profile_node_data("out_rpc", p_node->get_instance_id());
#endif
_send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
}
if (call_local_native) {
Callable::CallError ce;
multiplayer->set_remote_sender_override(network_peer->get_unique_id());
p_node->call(p_method, p_arg, p_argcount, ce);
multiplayer->set_remote_sender_override(0);
if (ce.error != Callable::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in local call: - " + error + ".";
ERR_PRINT(error);
return;
}
}
if (call_local_script) {
Callable::CallError ce;
ce.error = Callable::CallError::CALL_OK;
multiplayer->set_remote_sender_override(network_peer->get_unique_id());
p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
multiplayer->set_remote_sender_override(0);
if (ce.error != Callable::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in script local call: - " + error + ".";
ERR_PRINT(error);
return;
}
}
ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
}

View file

@ -0,0 +1,89 @@
/*************************************************************************/
/* rpc_manager.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MULTIPLAYER_RPC_H
#define MULTIPLAYER_RPC_H
#include "core/multiplayer/multiplayer.h"
#include "core/multiplayer/multiplayer_api.h"
#include "core/object/ref_counted.h"
class RPCManager : public RefCounted {
GDCLASS(RPCManager, RefCounted);
private:
enum NetworkNodeIdCompression {
NETWORK_NODE_ID_COMPRESSION_8 = 0,
NETWORK_NODE_ID_COMPRESSION_16,
NETWORK_NODE_ID_COMPRESSION_32,
};
enum NetworkNameIdCompression {
NETWORK_NAME_ID_COMPRESSION_8 = 0,
NETWORK_NAME_ID_COMPRESSION_16,
};
// The RPC meta is composed by a single byte that contains (starting from the least significant bit):
// - `NetworkCommands` in the first four bits.
// - `NetworkNodeIdCompression` in the next 2 bits.
// - `NetworkNameIdCompression` in the next 1 bit.
// - `byte_only_or_no_args` in the next 1 bit.
enum {
NODE_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, // 2 bits for this.
NAME_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_2_SHIFT,
BYTE_ONLY_OR_NO_ARGS_SHIFT = MultiplayerAPI::CMD_FLAG_3_SHIFT,
};
enum {
NODE_ID_COMPRESSION_FLAG = (1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)), // 2 bits for this.
NAME_ID_COMPRESSION_FLAG = (1 << NAME_ID_COMPRESSION_SHIFT),
BYTE_ONLY_OR_NO_ARGS_FLAG = (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT),
};
MultiplayerAPI *multiplayer = nullptr;
Vector<uint8_t> packet_cache;
protected:
_FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id);
void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
public:
// Called by Node.rpc
void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len);
String get_rpc_md5(const Node *p_node);
RPCManager(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; }
};
#endif // MULTIPLAYER_RPC_H

View file

@ -32,8 +32,8 @@
#define SCRIPT_LANGUAGE_H
#include "core/doc_data.h"
#include "core/io/multiplayer_api.h"
#include "core/io/resource.h"
#include "core/multiplayer/multiplayer.h"
#include "core/templates/map.h"
#include "core/templates/pair.h"
@ -159,7 +159,7 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;
Script() {}
};
@ -200,7 +200,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;
virtual ScriptLanguage *get_language() = 0;
virtual ~ScriptInstance();
@ -419,7 +419,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr);
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const { return Vector<MultiplayerAPI::RPCConfig>(); }
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const { return Vector<Multiplayer::RPCConfig>(); }
PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
~PlaceHolderScriptInstance();

View file

@ -48,9 +48,6 @@
#include "core/io/image_loader.h"
#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "core/io/multiplayer_api.h"
#include "core/io/multiplayer_peer.h"
#include "core/io/multiplayer_replicator.h"
#include "core/io/packed_data_container.h"
#include "core/io/packet_peer.h"
#include "core/io/packet_peer_dtls.h"
@ -70,6 +67,9 @@
#include "core/math/geometry_3d.h"
#include "core/math/random_number_generator.h"
#include "core/math/triangle_mesh.h"
#include "core/multiplayer/multiplayer_api.h"
#include "core/multiplayer/multiplayer_peer.h"
#include "core/multiplayer/multiplayer_replicator.h"
#include "core/object/class_db.h"
#include "core/object/undo_redo.h"
#include "core/os/main_loop.h"

View file

@ -2463,6 +2463,24 @@
<constant name="METHOD_FLAGS_DEFAULT" value="1" enum="MethodFlags">
Default method flags.
</constant>
<constant name="RPC_MODE_DISABLED" value="0" enum="RPCMode">
Used with [method Node.rpc_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods.
</constant>
<constant name="RPC_MODE_ANY" value="1" enum="RPCMode">
Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc(any)[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not.
</constant>
<constant name="RPC_MODE_AUTH" value="2" enum="RPCMode">
Used with [method Node.rpc_config] to set a method to be callable remotely only by the current network authority (which is the server by default). Analogous to the [code]@rpc(auth)[/code] annotation. See [method Node.set_network_authority].
</constant>
<constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode">
Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [constant TRANSFER_MODE_ORDERED]. Use for non-critical data, and always consider whether the order matters.
</constant>
<constant name="TRANSFER_MODE_ORDERED" value="1" enum="TransferMode">
Packets are not acknowledged, no resend attempts are made for lost packets. Packets are received in the order they were sent in. Potentially faster than [constant TRANSFER_MODE_RELIABLE]. Use for non-critical data or data that would be outdated if received late due to resend attempt(s) anyway, for example movement and positional data.
</constant>
<constant name="TRANSFER_MODE_RELIABLE" value="2" enum="TransferMode">
Packets must be received and resend attempts should be made until the packets are acknowledged. Packets must be received in the order they were sent in. Most reliable transfer mode, but potentially the slowest due to the overhead. Use for critical data that must be transmitted and arrive in order, for example an ability being triggered or a chat message. Consider carefully if the information really is critical, and use sparingly.
</constant>
<constant name="TYPE_NIL" value="0" enum="Variant.Type">
Variable is [code]null[/code].
</constant>

View file

@ -30,7 +30,7 @@
Returns the unique peer ID of this MultiplayerAPI's [member network_peer].
</description>
</method>
<method name="get_rpc_sender_id" qualifiers="const">
<method name="get_remote_sender_id" qualifiers="const">
<return type="int" />
<description>
Returns the sender's peer ID for the RPC currently being executed.
@ -60,7 +60,7 @@
<return type="int" enum="Error" />
<argument index="0" name="bytes" type="PackedByteArray" />
<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="TransferMode" default="2" />
<argument index="3" name="channel" type="int" default="0" />
<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.
@ -122,14 +122,5 @@
</signal>
</signals>
<constants>
<constant name="RPC_MODE_DISABLED" value="0" enum="RPCMode">
Used with [method Node.rpc_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods.
</constant>
<constant name="RPC_MODE_ANY" value="1" enum="RPCMode">
Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc(any)[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not.
</constant>
<constant name="RPC_MODE_AUTHORITY" value="2" enum="RPCMode">
Used with [method Node.rpc_config] to set a method to be callable remotely only by the current network authority (which is the server by default). Analogous to the [code]@rpc(auth)[/code] annotation. See [method Node.set_network_authority].
</constant>
</constants>
</class>

View file

@ -57,9 +57,9 @@
</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.
[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_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="TransferMode" default="0">
The manner in which to send packets to the [code]target_peer[/code]. See [enum TransferMode].
</member>
</members>
@ -93,15 +93,6 @@
</signal>
</signals>
<constants>
<constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode">
Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [constant TRANSFER_MODE_UNRELIABLE_ORDERED]. Use for non-critical data, and always consider whether the order matters.
</constant>
<constant name="TRANSFER_MODE_UNRELIABLE_ORDERED" value="1" enum="TransferMode">
Packets are not acknowledged, no resend attempts are made for lost packets. Packets are received in the order they were sent in. Potentially faster than [constant TRANSFER_MODE_RELIABLE]. Use for non-critical data or data that would be outdated if received late due to resend attempt(s) anyway, for example movement and positional data.
</constant>
<constant name="TRANSFER_MODE_RELIABLE" value="2" enum="TransferMode">
Packets must be received and resend attempts should be made until the packets are acknowledged. Packets must be received in the order they were sent in. Most reliable transfer mode, but potentially the slowest due to the overhead. Use for critical data that must be transmitted and arrive in order, for example an ability being triggered or a chat message. Consider carefully if the information really is critical, and use sparingly.
</constant>
<constant name="CONNECTION_DISCONNECTED" value="0" enum="ConnectionStatus">
The ongoing connection disconnected.
</constant>

View file

@ -62,7 +62,7 @@
<argument index="0" name="peer_id" type="int" />
<argument index="1" name="scene_id" type="int" />
<argument index="2" name="data" type="PackedByteArray" />
<argument index="3" name="transfer_mode" type="int" enum="MultiplayerPeer.TransferMode" default="2" />
<argument index="3" name="transfer_mode" type="int" enum="TransferMode" default="2" />
<argument index="4" name="channel" type="int" default="0" />
<description>
Sends a sync request for the instances of the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). This function can only be called manually when overriding the send and receive sync functions (see [method sync_config]).

View file

@ -584,11 +584,11 @@
<method name="rpc_config">
<return type="int" />
<argument index="0" name="method" type="StringName" />
<argument index="1" name="rpc_mode" type="int" enum="MultiplayerAPI.RPCMode" />
<argument index="2" name="transfer_mode" type="int" enum="MultiplayerPeer.TransferMode" default="2" />
<argument index="1" name="rpc_mode" type="int" enum="RPCMode" />
<argument index="2" name="transfer_mode" type="int" enum="TransferMode" default="2" />
<argument index="3" name="channel" type="int" default="0" />
<description>
Changes the RPC mode for the given [code]method[/code] to the given [code]rpc_mode[/code], optionally specifying the [code]transfer_mode[/code] and [code]channel[/code] (on supported peers). See [enum MultiplayerAPI.RPCMode] and [enum MultiplayerPeer.TransferMode]. An alternative is annotating methods and properties with the corresponding annotation ([code]@rpc(any)[/code], [code]@rpc(auth)[/code]). By default, methods are not exposed to networking (and RPCs).
Changes the RPC mode for the given [code]method[/code] to the given [code]rpc_mode[/code], optionally specifying the [code]transfer_mode[/code] and [code]channel[/code] (on supported peers). See [enum RPCMode] and [enum TransferMode]. An alternative is annotating methods and properties with the corresponding annotation ([code]@rpc(any)[/code], [code]@rpc(auth)[/code]). By default, methods are not exposed to networking (and RPCs).
</description>
</method>
<method name="rpc_id" qualifiers="vararg">

View file

@ -81,7 +81,7 @@
<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>
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" />
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" />
</members>
<constants>
</constants>

View file

@ -41,11 +41,11 @@ int ENetMultiplayerPeer::get_transfer_channel() const {
return transfer_channel;
}
void ENetMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
void ENetMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) {
transfer_mode = p_mode;
}
MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_transfer_mode() const {
Multiplayer::TransferMode ENetMultiplayerPeer::get_transfer_mode() const {
return transfer_mode;
}
@ -455,15 +455,15 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
channel = SYSCH_MAX + transfer_channel - 1;
} else {
switch (transfer_mode) {
case TRANSFER_MODE_UNRELIABLE: {
case Multiplayer::TRANSFER_MODE_UNRELIABLE: {
packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
channel = SYSCH_UNRELIABLE;
} break;
case TRANSFER_MODE_UNRELIABLE_ORDERED: {
case Multiplayer::TRANSFER_MODE_ORDERED: {
packet_flags = 0;
channel = SYSCH_UNRELIABLE;
} break;
case TRANSFER_MODE_RELIABLE: {
case Multiplayer::TRANSFER_MODE_RELIABLE: {
packet_flags = ENET_PACKET_FLAG_RELIABLE;
channel = SYSCH_RELIABLE;
} break;

View file

@ -32,7 +32,7 @@
#define NETWORKED_MULTIPLAYER_ENET_H
#include "core/crypto/crypto.h"
#include "core/io/multiplayer_peer.h"
#include "core/multiplayer/multiplayer_peer.h"
#include "enet_connection.h"
#include <enet/enet.h>
@ -66,7 +66,7 @@ private:
int target_peer = 0;
int transfer_channel = 0;
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
bool refuse_connections = false;
bool server_relay = true;
@ -104,8 +104,8 @@ 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 TransferMode get_transfer_mode() const override;
virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override;
virtual Multiplayer::TransferMode get_transfer_mode() const override;
virtual void set_target_peer(int p_peer) override;
virtual int get_packet_peer() const override;

View file

@ -127,9 +127,9 @@ void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const cha
E->get().methods.insert(p_function_name, method);
if (p_attr.rpc_type != GODOT_METHOD_RPC_MODE_DISABLED) {
MultiplayerAPI::RPCConfig nd;
Multiplayer::RPCConfig nd;
nd.name = String(p_name);
nd.rpc_mode = MultiplayerAPI::RPCMode(p_attr.rpc_type);
nd.rpc_mode = Multiplayer::RPCMode(p_attr.rpc_type);
E->get().rpc_methods.push_back(nd);
}
}

View file

@ -431,9 +431,9 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const {
}
}
const Vector<MultiplayerAPI::RPCConfig> NativeScript::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> NativeScript::get_rpc_methods() const {
NativeScriptDesc *script_data = get_script_desc();
ERR_FAIL_COND_V(!script_data, Vector<MultiplayerAPI::RPCConfig>());
ERR_FAIL_COND_V(!script_data, Vector<Multiplayer::RPCConfig>());
return script_data->rpc_methods;
}
@ -828,7 +828,7 @@ Ref<Script> NativeScriptInstance::get_script() const {
return script;
}
const Vector<MultiplayerAPI::RPCConfig> NativeScriptInstance::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> NativeScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}

View file

@ -71,7 +71,7 @@ struct NativeScriptDesc {
};
Map<StringName, Method> methods;
Vector<MultiplayerAPI::RPCConfig> rpc_methods;
Vector<Multiplayer::RPCConfig> rpc_methods;
OrderedHashMap<StringName, Property> properties;
Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals
StringName base;
@ -175,7 +175,7 @@ public:
virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
String get_class_documentation() const;
String get_method_documentation(const StringName &p_method) const;
@ -213,7 +213,7 @@ public:
String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
virtual ScriptLanguage *get_language();

View file

@ -72,14 +72,14 @@ int MultiplayerPeerGDNative::get_transfer_channel() const {
return interface->get_transfer_channel(interface->data);
}
void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
void MultiplayerPeerGDNative::set_transfer_mode(Multiplayer::TransferMode p_mode) {
ERR_FAIL_COND(interface == nullptr);
interface->set_transfer_mode(interface->data, (godot_int)p_mode);
}
MultiplayerPeer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const {
ERR_FAIL_COND_V(interface == nullptr, TRANSFER_MODE_UNRELIABLE);
return (TransferMode)interface->get_transfer_mode(interface->data);
Multiplayer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const {
ERR_FAIL_COND_V(interface == nullptr, Multiplayer::TRANSFER_MODE_UNRELIABLE);
return (Multiplayer::TransferMode)interface->get_transfer_mode(interface->data);
}
void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) {
@ -124,7 +124,7 @@ MultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status
void MultiplayerPeerGDNative::_bind_methods() {
ADD_PROPERTY_DEFAULT("transfer_channel", 0);
ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_UNRELIABLE);
ADD_PROPERTY_DEFAULT("transfer_mode", Multiplayer::TRANSFER_MODE_UNRELIABLE);
ADD_PROPERTY_DEFAULT("refuse_new_connections", true);
}

View file

@ -31,7 +31,7 @@
#ifndef MULTIPLAYER_PEER_GDNATIVE_H
#define MULTIPLAYER_PEER_GDNATIVE_H
#include "core/io/multiplayer_peer.h"
#include "core/multiplayer/multiplayer_peer.h"
#include "modules/gdnative/gdnative.h"
#include "modules/gdnative/include/net/godot_net.h"
@ -58,8 +58,8 @@ public:
/* 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 TransferMode get_transfer_mode() const override;
virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override;
virtual Multiplayer::TransferMode get_transfer_mode() const override;
virtual void set_target_peer(int p_peer_id) override;
virtual int get_packet_peer() const override;

View file

@ -100,7 +100,7 @@ String PluginScriptInstance::to_string(bool *r_valid) {
return str_ret;
}
const Vector<MultiplayerAPI::RPCConfig> PluginScriptInstance::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> PluginScriptInstance::get_rpc_methods() const {
return _script->get_rpc_methods();
}

View file

@ -71,7 +71,7 @@ public:
void set_path(const String &p_path);
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
virtual void refcount_incremented();
virtual bool refcount_decremented();

View file

@ -334,9 +334,9 @@ Error PluginScript::reload(bool p_keep_state) {
// rpc_mode is passed as an optional field and is not part of MethodInfo
Variant var = v["rpc_mode"];
if (var != Variant()) {
MultiplayerAPI::RPCConfig nd;
Multiplayer::RPCConfig nd;
nd.name = mi.name;
nd.rpc_mode = MultiplayerAPI::RPCMode(int(var));
nd.rpc_mode = Multiplayer::RPCMode(int(var));
// TODO Transfer Channel
if (_rpc_methods.find(nd) == -1) {
_rpc_methods.push_back(nd);
@ -345,7 +345,7 @@ Error PluginScript::reload(bool p_keep_state) {
}
// Sort so we are 100% that they are always the same.
_rpc_methods.sort_custom<MultiplayerAPI::SortRPCConfig>();
_rpc_methods.sort_custom<Multiplayer::SortRPCConfig>();
Array *signals = (Array *)&manifest.signals;
for (int i = 0; i < signals->size(); ++i) {
@ -484,7 +484,7 @@ int PluginScript::get_member_line(const StringName &p_member) const {
return -1;
}
const Vector<MultiplayerAPI::RPCConfig> PluginScript::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> PluginScript::get_rpc_methods() const {
return _rpc_methods;
}

View file

@ -61,7 +61,7 @@ private:
Map<StringName, PropertyInfo> _properties_info;
Map<StringName, MethodInfo> _signals_info;
Map<StringName, MethodInfo> _methods_info;
Vector<MultiplayerAPI::RPCConfig> _rpc_methods;
Vector<Multiplayer::RPCConfig> _rpc_methods;
Set<Object *> _instances;
//exported members
@ -136,7 +136,7 @@ public:
virtual int get_member_line(const StringName &p_member) const override;
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
PluginScript();
void init(PluginScriptLanguage *language);

View file

@ -900,7 +900,7 @@ void GDScript::get_members(Set<StringName> *p_members) {
}
}
const Vector<MultiplayerAPI::RPCConfig> GDScript::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const {
return rpc_functions;
}
@ -1164,8 +1164,8 @@ void GDScript::_init_rpc_methods_properties() {
while (cscript) {
// RPC Methods
for (Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.front(); E; E = E->next()) {
MultiplayerAPI::RPCConfig config = E->get()->get_rpc_config();
if (config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
Multiplayer::RPCConfig config = E->get()->get_rpc_config();
if (config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
config.name = E->get()->get_name();
if (rpc_functions.find(config) == -1) {
rpc_functions.push_back(config);
@ -1185,7 +1185,7 @@ void GDScript::_init_rpc_methods_properties() {
}
// Sort so we are 100% that they are always the same.
rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>();
rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
GDScript::~GDScript() {
@ -1541,7 +1541,7 @@ ScriptLanguage *GDScriptInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
const Vector<MultiplayerAPI::RPCConfig> GDScriptInstance::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> GDScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}

View file

@ -85,7 +85,7 @@ class GDScript : public Script {
Map<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
Map<StringName, Ref<GDScript>> subclasses;
Map<StringName, Vector<StringName>> _signals;
Vector<MultiplayerAPI::RPCConfig> rpc_functions;
Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
@ -245,7 +245,7 @@ public:
virtual void get_constants(Map<StringName, Variant> *p_constants) override;
virtual void get_members(Set<StringName> *p_members) override;
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
@ -298,7 +298,7 @@ public:
void reload_members();
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
GDScriptInstance();
~GDScriptInstance();

View file

@ -155,7 +155,7 @@ void GDScriptByteCodeGenerator::end_parameters() {
function->default_arguments.reverse();
}
void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) {
void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) {
function = memnew(GDScriptFunction);
debug_stack = EngineDebugger::is_active();

View file

@ -419,7 +419,7 @@ public:
virtual void start_block() override;
virtual void end_block() override;
virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override;
virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override;
virtual GDScriptFunction *write_end() override;
#ifdef DEBUG_ENABLED

View file

@ -31,7 +31,7 @@
#ifndef GDSCRIPT_CODEGEN
#define GDSCRIPT_CODEGEN
#include "core/io/multiplayer_api.h"
#include "core/multiplayer/multiplayer.h"
#include "core/string/string_name.h"
#include "core/variant/variant.h"
#include "gdscript_function.h"
@ -80,7 +80,7 @@ public:
virtual void start_block() = 0;
virtual void end_block() = 0;
virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0;
virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0;
virtual GDScriptFunction *write_end() = 0;
#ifdef DEBUG_ENABLED

View file

@ -1857,7 +1857,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
StringName func_name;
bool is_static = false;
MultiplayerAPI::RPCConfig rpc_config;
Multiplayer::RPCConfig rpc_config;
GDScriptDataType return_type;
return_type.has_type = true;
return_type.kind = GDScriptDataType::BUILTIN;
@ -2086,7 +2086,7 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script);
}
codegen.generator->write_start(p_script, func_name, false, MultiplayerAPI::RPCConfig(), return_type);
codegen.generator->write_start(p_script, func_name, false, Multiplayer::RPCConfig(), return_type);
if (p_is_setter) {
uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype()));

View file

@ -468,7 +468,7 @@ private:
int _initial_line = 0;
bool _static = false;
MultiplayerAPI::RPCConfig rpc_config;
Multiplayer::RPCConfig rpc_config;
GDScript *_script = nullptr;
@ -588,7 +588,7 @@ public:
void disassemble(const Vector<String> &p_code_lines) const;
#endif
_FORCE_INLINE_ MultiplayerAPI::RPCConfig get_rpc_config() const { return rpc_config; }
_FORCE_INLINE_ Multiplayer::RPCConfig get_rpc_config() const { return rpc_config; }
GDScriptFunction();
~GDScriptFunction();
};

View file

@ -133,7 +133,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
// Networking.
register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_AUTHORITY>, 4, true);
register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true);
// TODO: Warning annotations.
}
@ -3393,11 +3393,11 @@ bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Nod
ERR_FAIL_V_MSG(false, "Not implemented.");
}
template <MultiplayerAPI::RPCMode t_mode>
template <Multiplayer::RPCMode t_mode>
bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Node *p_node) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE && p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to variables and functions.)", p_annotation->name));
MultiplayerAPI::RPCConfig rpc_config;
Multiplayer::RPCConfig rpc_config;
rpc_config.rpc_mode = t_mode;
if (p_annotation->resolved_arguments.size()) {
int last = p_annotation->resolved_arguments.size() - 1;
@ -3412,19 +3412,19 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod
for (int i = last; i >= 0; i--) {
String mode = p_annotation->resolved_arguments[i].operator String();
if (mode == "any") {
rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_ANY;
rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY;
} else if (mode == "auth") {
rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_AUTHORITY;
rpc_config.rpc_mode = Multiplayer::RPC_MODE_AUTHORITY;
} else if (mode == "sync") {
rpc_config.sync = true;
} else if (mode == "nosync") {
rpc_config.sync = false;
} else if (mode == "reliable") {
rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
} else if (mode == "unreliable") {
rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE;
rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE;
} else if (mode == "ordered") {
rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED;
rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_ORDERED;
} else {
push_error(R"(Invalid RPC argument. Must be one of: 'sync'/'nosync' (local calls), 'any'/'auth' (permission), 'reliable'/'unreliable'/'ordered' (transfer mode).)", p_annotation);
}
@ -3433,7 +3433,7 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod
switch (p_node->type) {
case Node::FUNCTION: {
FunctionNode *function = static_cast<FunctionNode *>(p_node);
if (function->rpc_config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
if (function->rpc_config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
return false;
}

View file

@ -31,8 +31,8 @@
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
#include "core/io/multiplayer_api.h"
#include "core/io/resource.h"
#include "core/multiplayer/multiplayer.h"
#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
#include "core/string/string_name.h"
@ -729,7 +729,7 @@ public:
SuiteNode *body = nullptr;
bool is_static = false;
bool is_coroutine = false;
MultiplayerAPI::RPCConfig rpc_config;
Multiplayer::RPCConfig rpc_config;
MethodInfo info;
LambdaNode *source_lambda = nullptr;
#ifdef TOOLS_ENABLED
@ -1340,7 +1340,7 @@ private:
template <PropertyHint t_hint, Variant::Type t_type>
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
template <MultiplayerAPI::RPCMode t_mode>
template <Multiplayer::RPCMode t_mode>
bool network_annotations(const AnnotationNode *p_annotation, Node *p_target);
// Statements.
Node *parse_statement();

View file

@ -2124,7 +2124,7 @@ bool CSharpInstance::refcount_decremented() {
return ref_dying;
}
const Vector<MultiplayerAPI::RPCConfig> CSharpInstance::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> CSharpInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
@ -3034,13 +3034,13 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
Vector<GDMonoMethod *> methods = top->get_all_methods();
for (int i = 0; i < methods.size(); i++) {
if (!methods[i]->is_static()) {
MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
MultiplayerAPI::RPCConfig nd;
Multiplayer::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
if (Multiplayer::RPC_MODE_DISABLED != mode) {
Multiplayer::RPCConfig nd;
nd.name = methods[i]->get_name();
nd.rpc_mode = mode;
// TODO Transfer mode, channel
nd.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
nd.channel = 0;
if (-1 == p_script->rpc_functions.find(nd)) {
p_script->rpc_functions.push_back(nd);
@ -3054,7 +3054,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
}
// Sort so we are 100% that they are always the same.
p_script->rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>();
p_script->rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
p_script->load_script_signals(p_script->script_class, p_script->native);
}
@ -3464,18 +3464,18 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
return -1;
}
MultiplayerAPI::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
Multiplayer::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) {
return MultiplayerAPI::RPC_MODE_ANY;
return Multiplayer::RPC_MODE_ANY;
}
if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) {
return MultiplayerAPI::RPC_MODE_AUTHORITY;
return Multiplayer::RPC_MODE_AUTHORITY;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
return Multiplayer::RPC_MODE_DISABLED;
}
const Vector<MultiplayerAPI::RPCConfig> CSharpScript::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> CSharpScript::get_rpc_methods() const {
return rpc_functions;
}

View file

@ -136,7 +136,7 @@ private:
Map<StringName, EventSignal> event_signals;
bool signals_invalidated = true;
Vector<MultiplayerAPI::RPCConfig> rpc_functions;
Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
@ -179,7 +179,7 @@ private:
static void update_script_class_info(Ref<CSharpScript> p_script);
static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
Multiplayer::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
protected:
static void _bind_methods();
@ -234,7 +234,7 @@ public:
int get_member_line(const StringName &p_member) const override;
const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
@ -311,7 +311,7 @@ public:
void refcount_incremented() override;
bool refcount_decremented() override;
const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
void notification(int p_notification) override;
void _call_notification(int p_notification);

View file

@ -955,7 +955,7 @@ bool VisualScript::are_subnodes_edited() const {
}
#endif
const Vector<MultiplayerAPI::RPCConfig> VisualScript::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> VisualScript::get_rpc_methods() const {
return rpc_functions;
}
@ -1022,11 +1022,11 @@ void VisualScript::_set_data(const Dictionary &p_data) {
if (functions[E].func_id >= 0 && nodes.has(functions[E].func_id)) {
Ref<VisualScriptFunction> vsf = nodes[functions[E].func_id].node;
if (vsf.is_valid()) {
if (vsf->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
MultiplayerAPI::RPCConfig nd;
if (vsf->get_rpc_mode() != Multiplayer::RPC_MODE_DISABLED) {
Multiplayer::RPCConfig nd;
nd.name = E;
nd.rpc_mode = vsf->get_rpc_mode();
nd.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; // TODO
nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; // TODO
if (rpc_functions.find(nd) == -1) {
rpc_functions.push_back(nd);
}
@ -1036,7 +1036,7 @@ void VisualScript::_set_data(const Dictionary &p_data) {
}
// Sort so we are 100% that they are always the same.
rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>();
rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
Dictionary VisualScript::_get_data() const {
@ -1833,7 +1833,7 @@ Ref<Script> VisualScriptInstance::get_script() const {
return script;
}
const Vector<MultiplayerAPI::RPCConfig> VisualScriptInstance::get_rpc_methods() const {
const Vector<Multiplayer::RPCConfig> VisualScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}

View file

@ -234,7 +234,7 @@ private:
HashMap<StringName, Function> functions;
HashMap<StringName, Variable> variables;
Map<StringName, Vector<Argument>> custom_signals;
Vector<MultiplayerAPI::RPCConfig> rpc_functions;
Vector<Multiplayer::RPCConfig> rpc_functions;
Map<Object *, VisualScriptInstance *> instances;
@ -362,7 +362,7 @@ public:
virtual int get_member_line(const StringName &p_member) const override;
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
virtual bool are_subnodes_edited() const;
@ -443,7 +443,7 @@ public:
virtual ScriptLanguage *get_language();
virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
VisualScriptInstance();
~VisualScriptInstance();

View file

@ -90,7 +90,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
}
if (p_name == "rpc/mode") {
rpc_mode = MultiplayerAPI::RPCMode(int(p_value));
rpc_mode = Multiplayer::RPCMode(int(p_value));
return true;
}
@ -261,11 +261,11 @@ int VisualScriptFunction::get_argument_count() const {
return arguments.size();
}
void VisualScriptFunction::set_rpc_mode(MultiplayerAPI::RPCMode p_mode) {
void VisualScriptFunction::set_rpc_mode(Multiplayer::RPCMode p_mode) {
rpc_mode = p_mode;
}
MultiplayerAPI::RPCMode VisualScriptFunction::get_rpc_mode() const {
Multiplayer::RPCMode VisualScriptFunction::get_rpc_mode() const {
return rpc_mode;
}
@ -311,14 +311,14 @@ void VisualScriptFunction::reset_state() {
stack_size = 256;
stack_less = false;
sequenced = true;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
rpc_mode = Multiplayer::RPC_MODE_DISABLED;
}
VisualScriptFunction::VisualScriptFunction() {
stack_size = 256;
stack_less = false;
sequenced = true;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
rpc_mode = Multiplayer::RPC_MODE_DISABLED;
}
void VisualScriptFunction::set_stack_less(bool p_enable) {

View file

@ -49,7 +49,7 @@ class VisualScriptFunction : public VisualScriptNode {
bool stack_less;
int stack_size;
MultiplayerAPI::RPCMode rpc_mode;
Multiplayer::RPCMode rpc_mode;
bool sequenced;
protected:
@ -96,8 +96,8 @@ public:
void set_return_type(Variant::Type p_type);
Variant::Type get_return_type() const;
void set_rpc_mode(MultiplayerAPI::RPCMode p_mode);
MultiplayerAPI::RPCMode get_rpc_mode() const;
void set_rpc_mode(Multiplayer::RPCMode p_mode);
Multiplayer::RPCMode get_rpc_mode() const;
virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;

View file

@ -56,7 +56,7 @@
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).
You can optionally specify a [code]channels_config[/code] array of [enum TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="remove_peer">
@ -69,7 +69,7 @@
</methods>
<members>
<member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" />
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" />
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" />
</members>
<constants>
</constants>

View file

@ -51,11 +51,11 @@ int WebRTCMultiplayerPeer::get_transfer_channel() const {
return transfer_channel;
}
void WebRTCMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
void WebRTCMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) {
transfer_mode = p_mode;
}
MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_transfer_mode() const {
Multiplayer::TransferMode WebRTCMultiplayerPeer::get_transfer_mode() const {
return transfer_mode;
}
@ -204,7 +204,7 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
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'");
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'");
int mode = p_channels_config[i].operator int();
// Initialize data channel configurations.
Dictionary cfg;
@ -213,17 +213,17 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
cfg["ordered"] = true;
switch (mode) {
case TRANSFER_MODE_UNRELIABLE_ORDERED:
case Multiplayer::TRANSFER_MODE_ORDERED:
cfg["maxPacketLifetime"] = 1;
break;
case TRANSFER_MODE_UNRELIABLE:
case Multiplayer::TRANSFER_MODE_UNRELIABLE:
cfg["maxPacketLifetime"] = 1;
cfg["ordered"] = false;
break;
case TRANSFER_MODE_RELIABLE:
case Multiplayer::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));
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'. Got: %d", mode));
}
channels_config.push_back(cfg);
}
@ -355,13 +355,13 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
int ch = transfer_channel;
if (ch == 0) {
switch (transfer_mode) {
case TRANSFER_MODE_RELIABLE:
case Multiplayer::TRANSFER_MODE_RELIABLE:
ch = CH_RELIABLE;
break;
case TRANSFER_MODE_UNRELIABLE_ORDERED:
case Multiplayer::TRANSFER_MODE_ORDERED:
ch = CH_ORDERED;
break;
case TRANSFER_MODE_UNRELIABLE:
case Multiplayer::TRANSFER_MODE_UNRELIABLE:
ch = CH_UNRELIABLE;
break;
}

View file

@ -31,7 +31,7 @@
#ifndef WEBRTC_MULTIPLAYER_H
#define WEBRTC_MULTIPLAYER_H
#include "core/io/multiplayer_peer.h"
#include "core/multiplayer/multiplayer_peer.h"
#include "webrtc_peer_connection.h"
class WebRTCMultiplayerPeer : public MultiplayerPeer {
@ -68,7 +68,7 @@ private:
bool refuse_connections = false;
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
int transfer_channel = 0;
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
int next_packet_peer = 0;
bool server_compat = false;
@ -99,8 +99,8 @@ public:
// MultiplayerPeer
void set_transfer_channel(int p_channel) override;
int get_transfer_channel() const override;
void set_transfer_mode(TransferMode p_mode) override;
TransferMode get_transfer_mode() const override;
void set_transfer_mode(Multiplayer::TransferMode p_mode) override;
Multiplayer::TransferMode get_transfer_mode() const override;
void set_target_peer(int p_peer_id) override;
int get_unique_id() const override;

View file

@ -32,7 +32,7 @@
</methods>
<members>
<member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" />
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" />
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" />
</members>
<signals>
<signal name="peer_packet">

View file

@ -113,13 +113,13 @@ int WebSocketMultiplayerPeer::get_transfer_channel() const {
return 0;
}
void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
void WebSocketMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) {
// Websocket uses TCP, reliable
}
MultiplayerPeer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const {
Multiplayer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const {
// Websocket uses TCP, reliable
return TRANSFER_MODE_RELIABLE;
return Multiplayer::TRANSFER_MODE_RELIABLE;
}
void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) {

View file

@ -32,7 +32,7 @@
#define WEBSOCKET_MULTIPLAYER_PEER_H
#include "core/error/error_list.h"
#include "core/io/multiplayer_peer.h"
#include "core/multiplayer/multiplayer_peer.h"
#include "core/templates/list.h"
#include "websocket_peer.h"
@ -80,8 +80,8 @@ public:
/* MultiplayerPeer */
void set_transfer_channel(int p_channel) override;
int get_transfer_channel() const override;
void set_transfer_mode(TransferMode p_mode) override;
TransferMode get_transfer_mode() const override;
void set_transfer_mode(Multiplayer::TransferMode p_mode) override;
Multiplayer::TransferMode get_transfer_mode() const override;
void set_target_peer(int p_target_peer) override;
int get_packet_peer() const override;
int get_unique_id() const override;

View file

@ -539,10 +539,10 @@ bool Node::is_network_authority() const {
/***** RPC CONFIG ********/
uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel) {
uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
for (int i = 0; i < data.rpc_methods.size(); i++) {
if (data.rpc_methods[i].name == p_method) {
MultiplayerAPI::RPCConfig &nd = data.rpc_methods.write[i];
Multiplayer::RPCConfig &nd = data.rpc_methods.write[i];
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
nd.channel = p_channel;
@ -550,7 +550,7 @@ uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_
}
}
// New method
MultiplayerAPI::RPCConfig nd;
Multiplayer::RPCConfig nd;
nd.name = p_method;
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
@ -643,7 +643,7 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal
void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND(!is_inside_tree());
get_multiplayer()->rpcp(this, p_peer_id, true, p_method, p_arg, p_argcount);
get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
@ -664,7 +664,7 @@ void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
multiplayer = p_multiplayer;
}
Vector<MultiplayerAPI::RPCConfig> Node::get_node_rpc_methods() const {
Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
return data.rpc_methods;
}
@ -2746,7 +2746,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer);
ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);

View file

@ -128,7 +128,7 @@ private:
Node *process_owner = nullptr;
int network_authority = 1; // Server by default.
Vector<MultiplayerAPI::RPCConfig> rpc_methods;
Vector<Multiplayer::RPCConfig> rpc_methods;
// Variables used to properly sort the node when processing, ignored otherwise.
// TODO: Should move all the stuff below to bits.
@ -466,8 +466,8 @@ public:
int get_network_authority() const;
bool is_network_authority() const;
uint16_t rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC
Vector<MultiplayerAPI::RPCConfig> get_node_rpc_methods() const;
uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC
Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const;
void rpc(const StringName &p_method, VARIANT_ARG_LIST); // RPC, honors RPCMode, TransferMode, channel
void rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); // RPC to specific peer(s), honors RPCMode, TransferMode, channel

View file

@ -31,7 +31,7 @@
#ifndef SCENE_TREE_H
#define SCENE_TREE_H
#include "core/io/multiplayer_api.h"
#include "core/multiplayer/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
#include "core/templates/self_list.h"

View file

@ -34,6 +34,7 @@
#include "core/config/engine.h"
#include "core/math/audio_frame.h"
#include "core/object/ref_counted.h"
#include "core/templates/ring_buffer.h"
#include "core/templates/vector.h"
#include "servers/audio/audio_effect.h"
#include "servers/audio_server.h"

View file

@ -93,8 +93,8 @@ public:
Ref<Script> get_script() const override {
return Ref<Script>();
}
const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override {
return Vector<MultiplayerAPI::RPCConfig>();
const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override {
return Vector<Multiplayer::RPCConfig>();
}
ScriptLanguage *get_language() override {
return nullptr;