Optmized data sent during RPC and RSet calls.

- Now is sent the method ID rather the full function name.
- The passed IDs (Node and Method) are compressed so to use less possible space.
- The variant (INT and BOOL) is now encoded and compressed so to use much less data.
- Optimized RPCMode retrieval for GDScript functions.
- Added checksum to assert the methods are the same across peers.

This work has been kindly sponsored by IMVU.
This commit is contained in:
Andrea Catania 2020-02-12 11:51:50 +01:00
parent 70dd7f4e1a
commit eb07e87981
19 changed files with 1580 additions and 292 deletions

View file

@ -32,6 +32,7 @@
#include "core/io/marshalls.h"
#include "scene/main/node.h"
#include <stdint.h>
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
@ -180,7 +181,8 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
}
#endif
uint8_t packet_type = p_packet[0];
// Extract the `packet_type` from the LSB three bits:
uint8_t packet_type = p_packet[0] & 7;
switch (packet_type) {
@ -197,31 +199,80 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
case NETWORK_COMMAND_REMOTE_CALL:
case NETWORK_COMMAND_REMOTE_SET: {
ERR_FAIL_COND_MSG(p_packet_len < 6, "Invalid packet received. Size too small.");
// 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) >> 3;
int name_id_compression = (p_packet[0] & 32) >> 5;
Node *node = _process_get_node(p_from, p_packet, p_packet_len);
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 == NULL, "Invalid packet received. Requested node was not found.");
// Detect cstring end.
int len_end = 5;
for (; len_end < p_packet_len; len_end++) {
if (p_packet[len_end] == 0) {
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();
}
ERR_FAIL_COND_MSG(len_end >= p_packet_len, "Invalid packet received. Size too small.");
StringName name = String::utf8((const char *)&p_packet[5]);
if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
_process_rpc(node, name, p_from, p_packet, p_packet_len, len_end + 1);
_process_rpc(node, name_id, p_from, p_packet, p_packet_len, packet_min_size);
} else {
_process_rset(node, name, p_from, p_packet, p_packet_len, len_end + 1);
_process_rset(node, name_id, p_from, p_packet, p_packet_len, packet_min_size);
}
} break;
@ -233,15 +284,14 @@ 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, int p_packet_len) {
Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
uint32_t target = decode_uint32(&p_packet[1]);
Node *node = NULL;
if (target & 0x80000000) {
if (p_node_target & 0x80000000) {
// Use full path (not cached yet).
int ofs = target & 0x7FFFFFFF;
int ofs = p_node_target & 0x7FFFFFFF;
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, NULL, "Invalid packet received. Size smaller than declared.");
@ -256,7 +306,7 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int
ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
} else {
// Use cached path.
int id = target;
int id = p_node_target;
Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
ERR_FAIL_COND_V_MSG(!E, NULL, "Invalid packet received. Requests invalid peer cache.");
@ -274,21 +324,21 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int
return node;
}
void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
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.
RPCMode rpc_mode = RPC_MODE_DISABLED;
const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name);
if (E) {
rpc_mode = E->get();
} else if (p_node->get_script_instance()) {
rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name);
StringName name = p_node->get_node_rpc_method(p_rpc_method_id);
RPCMode rpc_mode = p_node->get_node_rpc_mode_by_id(p_rpc_method_id);
if (name == StringName() && p_node->get_script_instance()) {
name = p_node->get_script_instance()->get_rpc_method(p_rpc_method_id);
rpc_mode = p_node->get_script_instance()->get_rpc_mode_by_id(p_rpc_method_id);
}
ERR_FAIL_COND(name == StringName());
bool can_call = _can_call_mode(p_node, rpc_mode, p_from);
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
int argc = p_packet[p_offset];
Vector<Variant> args;
@ -311,7 +361,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
int vlen;
Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen, allow_object_decoding || network_peer->is_object_decoding_allowed());
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];
@ -320,29 +370,29 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
Variant::CallError ce;
p_node->call(p_name, (const Variant **)argp.ptr(), argc, ce);
p_node->call(name, (const Variant **)argp.ptr(), argc, ce);
if (ce.error != Variant::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, p_name, (const Variant **)argp.ptr(), argc, ce);
String error = Variant::get_call_error_text(p_node, name, (const Variant **)argp.ptr(), argc, ce);
error = "RPC - " + error;
ERR_PRINT(error);
}
}
void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_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 RSET on this node.
RPCMode rset_mode = RPC_MODE_DISABLED;
const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name);
if (E) {
rset_mode = E->get();
} else if (p_node->get_script_instance()) {
rset_mode = p_node->get_script_instance()->get_rset_mode(p_name);
StringName name = p_node->get_node_rset_property(p_rpc_property_id);
RPCMode rset_mode = p_node->get_node_rset_mode_by_id(p_rpc_property_id);
if (name == StringName() && p_node->get_script_instance()) {
name = p_node->get_script_instance()->get_rset_property(p_rpc_property_id);
rset_mode = p_node->get_script_instance()->get_rset_mode_by_id(p_rpc_property_id);
}
ERR_FAIL_COND(name == StringName());
bool can_call = _can_call_mode(p_node, rset_mode, p_from);
ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
#ifdef DEBUG_ENABLED
if (profiling) {
@ -353,26 +403,33 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p
#endif
Variant value;
Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed());
Error err = _decode_and_decompress_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL);
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value.");
bool valid;
p_node->set(p_name, value, &valid);
p_node->set(name, value, &valid);
if (!valid) {
String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class() + ".";
String error = "Error setting remote property '" + String(name) + "', not found in object of type " + p_node->get_class() + ".";
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 < 5, "Invalid packet received. Size too small.");
int id = decode_uint32(&p_packet[1]);
ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
int ofs = 1;
String methods_md5;
methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
ofs += 33;
int id = decode_uint32(&p_packet[ofs]);
ofs += 4;
String paths;
paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5);
paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
NodePath path = paths;
@ -380,6 +437,13 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
path_get_cache[p_from] = PathGetCache();
}
Node *node = root_node->get_node(path);
ERR_FAIL_COND(node == NULL);
const bool valid_rpc_checksum = node->get_rpc_md5() == 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);
}
PathGetCache::NodeInfo ni;
ni.path = path;
ni.instance = 0;
@ -392,9 +456,10 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
Vector<uint8_t> packet;
packet.resize(1 + len);
packet.resize(1 + 1 + len);
packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH;
encode_cstring(pname.get_data(), &packet.write[1]);
packet.write[1] = valid_rpc_checksum;
encode_cstring(pname.get_data(), &packet.write[2]);
network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->set_target_peer(p_from);
@ -403,13 +468,19 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small.");
ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
const bool valid_rpc_checksum = p_packet[1];
String paths;
paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
NodePath path = paths;
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);
}
PathSentCache *psc = path_send_cache.getptr(path);
ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
@ -418,7 +489,7 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet,
E->get() = true;
}
bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) {
bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
bool has_all_peers = true;
List<int> peers_to_add; // If one is missing, take note to add it.
@ -443,31 +514,192 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int
}
}
// Those that need to be added, send a message for this.
if (peers_to_add.size() > 0) {
for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
// Those that need to be added, send a message for this.
// Encode function name.
CharString pname = String(p_path).utf8();
int len = encode_cstring(pname.get_data(), NULL);
const CharString path = String(p_path).utf8();
const int path_len = encode_cstring(path.get_data(), NULL);
// Extract MD5 from rpc methods list.
const String methods_md5 = p_node->get_rpc_md5();
const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
Vector<uint8_t> packet;
packet.resize(1 + 4 + path_len + methods_md5_len);
int ofs = 0;
packet.resize(1 + 4 + len);
packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH;
encode_uint32(psc->id, &packet.write[1]);
encode_cstring(pname.get_data(), &packet.write[5]);
packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH;
ofs += 1;
network_peer->set_target_peer(E->get()); // To all of you.
network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->put_packet(packet.ptr(), packet.size());
ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed.
ofs += encode_uint32(psc->id, &packet.write[ofs]);
ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
network_peer->set_target_peer(E->get()); // To all of you.
network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->put_packet(packet.ptr(), packet.size());
psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed.
}
}
return has_all_peers;
}
// The variant is compressed and encoded; The first byte contains all the meta
// information and the format is:
// - The first LSB 5 bits are used for the variant type.
// - The next two bits are used to store the encoding mode.
// - The most significant is used to store the boolean value.
#define VARIANT_META_TYPE_MASK 0x1F
#define VARIANT_META_EMODE_MASK 0x60
#define VARIANT_META_BOOL_MASK 0x80
#define ENCODE_8 0 << 5
#define ENCODE_16 1 << 5
#define ENCODE_32 2 << 5
#define ENCODE_64 3 << 5
Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
// Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
uint8_t *buf = r_buffer;
r_len = 0;
uint8_t encode_mode = 0;
switch (p_variant.get_type()) {
case Variant::BOOL: {
if (buf) {
// We still have 1 free bit in the meta, so let's use it.
buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
buf[0] |= encode_mode | p_variant.get_type();
}
r_len += 1;
} break;
case Variant::INT: {
if (buf) {
// Reserve the first byte for the meta.
buf += 1;
}
r_len += 1;
int64_t val = p_variant;
if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
// Use 8 bit
encode_mode = ENCODE_8;
if (buf) {
buf[0] = val;
}
r_len += 1;
} else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
// Use 16 bit
encode_mode = ENCODE_16;
if (buf) {
encode_uint16(val, buf);
}
r_len += 2;
} else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
// Use 32 bit
encode_mode = ENCODE_32;
if (buf) {
encode_uint32(val, buf);
}
r_len += 4;
} else {
// Use 64 bit
encode_mode = ENCODE_64;
if (buf) {
encode_uint64(val, buf);
}
r_len += 8;
}
// Store the meta
if (buf) {
buf -= 1;
buf[0] = encode_mode | p_variant.get_type();
}
} break;
default:
// Any other case is not yet compressed.
Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding || network_peer->is_object_decoding_allowed());
if (err != OK)
return err;
if (r_buffer) {
// The first byte is not used by the marshaling, so store the type
// so we know how to decompress and decode this variant.
r_buffer[0] = p_variant.get_type();
}
}
return OK;
}
Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
const uint8_t *buf = p_buffer;
int len = p_len;
ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
switch (type) {
case Variant::BOOL: {
bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
r_variant = val;
if (r_len)
*r_len = 1;
} break;
case Variant::INT: {
buf += 1;
len -= 1;
if (r_len)
*r_len = 1;
if (encode_mode == ENCODE_8) {
// 8 bits.
ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
int8_t val = buf[0];
r_variant = val;
if (r_len)
(*r_len) += 1;
} else if (encode_mode == ENCODE_16) {
// 16 bits.
ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
int16_t val = decode_uint16(buf);
r_variant = val;
if (r_len)
(*r_len) += 2;
} else if (encode_mode == ENCODE_32) {
// 32 bits.
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t val = decode_uint32(buf);
r_variant = val;
if (r_len)
(*r_len) += 4;
} else {
// 64 bits.
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
int64_t val = decode_uint64(buf);
r_variant = val;
if (r_len)
(*r_len) += 8;
}
} break;
default:
Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding || network_peer->is_object_decoding_allowed());
if (err != OK)
return err;
}
return OK;
}
void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, 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.");
@ -496,6 +728,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
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;
@ -503,45 +738,125 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
#define MAKE_ROOM(m_amount) \
if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
// Encode type.
// Encode meta.
// The meta is composed by a single byte that contains (starting from the least segnificant bit):
// - `NetworkCommands` in the first three bits.
// - `NetworkNodeIdCompression` in the next 2 bits.
// - `NetworkNameIdCompression` in the next 1 bit.
// - So we still have the last two bits free!
uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
uint8_t node_id_compression = UINT8_MAX;
uint8_t name_id_compression = UINT8_MAX;
MAKE_ROOM(1);
packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
// The meta is composed along the way, so just set 0 for now.
packet_cache.write[0] = 0;
ofs += 1;
// Encode ID.
MAKE_ROOM(ofs + 4);
encode_uint32(psc->id, &(packet_cache.write[ofs]));
ofs += 4;
// Encode function name.
CharString name = String(p_name).utf8();
int len = encode_cstring(name.get_data(), NULL);
MAKE_ROOM(ofs + len);
encode_cstring(name.get_data(), &(packet_cache.write[ofs]));
ofs += len;
// 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 doesn'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;
}
if (p_set) {
// Take the rpc property ID
uint16_t property_id = p_from->get_node_rset_property_id(p_name);
if (property_id == UINT16_MAX && p_from->get_script_instance()) {
property_id = p_from->get_script_instance()->get_rset_property_id(p_name);
}
ERR_FAIL_COND_MSG(property_id == UINT16_MAX, "Unable to take the `property_id` for the property:" + p_name + ". this can happen only if this property is not marked as `remote`.");
if (property_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>(property_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(property_id, &(packet_cache.write[ofs]));
ofs += 2;
}
// Set argument.
Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed());
int len(0);
Error err = _encode_and_compress_variant(*p_arg[0], NULL, len);
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!");
MAKE_ROOM(ofs + len);
encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
_encode_and_compress_variant(*p_arg[0], &(packet_cache.write[ofs]), len);
ofs += len;
} else {
// Take the rpc method ID
uint16_t method_id = p_from->get_node_rpc_method_id(p_name);
if (method_id == UINT16_MAX && p_from->get_script_instance()) {
method_id = p_from->get_script_instance()->get_rpc_method_id(p_name);
}
ERR_FAIL_COND_MSG(method_id == UINT16_MAX, "Unable to take the `method_id` for the function:" + p_name + ". this can happen only if this method is not marked as `remote`.");
if (method_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>(method_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(method_id, &(packet_cache.write[ofs]));
ofs += 2;
}
// Call arguments.
MAKE_ROOM(ofs + 1);
packet_cache.write[ofs] = p_argcount;
ofs += 1;
for (int i = 0; i < p_argcount; i++) {
Error err = encode_variant(*p_arg[i], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed());
int len(0);
Error err = _encode_and_compress_variant(*p_arg[i], NULL, 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_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
_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 << 3) + (name_id_compression << 5);
#ifdef DEBUG_ENABLED
if (profiling) {
bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
@ -550,9 +865,6 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
}
#endif
// See if all peers have cached path (is so, call can be fast).
bool has_all_peers = _send_confirm_path(from_path, psc, p_to);
// Take chance and set transfer mode, since all send methods will use it.
network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
@ -562,6 +874,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
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.
@ -647,16 +962,14 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
// Check that send mode can use local call.
const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method);
if (E) {
call_local_native = _should_call_local(E->get(), is_master, skip_rpc);
}
RPCMode rpc_mode = p_node->get_node_rpc_mode(p_method);
call_local_native = _should_call_local(rpc_mode, is_master, skip_rpc);
if (call_local_native) {
// Done below.
} else if (p_node->get_script_instance()) {
// Attempt with script.
RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc);
}
}
@ -719,11 +1032,8 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
// Check that send mode can use local call.
const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_property);
if (E) {
set_local = _should_call_local(E->get(), is_master, skip_rset);
}
RPCMode rpc_mode = p_node->get_node_rset_mode(p_property);
set_local = _should_call_local(rpc_mode, is_master, skip_rset);
if (set_local) {
bool valid;
@ -740,7 +1050,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
}
} else if (p_node->get_script_instance()) {
// Attempt with script.
RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
set_local = _should_call_local(rpc_mode, is_master, skip_rset);

View file

@ -98,23 +98,37 @@ protected:
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, int p_packet_len);
void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
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_rset(Node *p_node, const uint16_t p_rpc_property_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, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target);
bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
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);
public:
enum NetworkCommands {
NETWORK_COMMAND_REMOTE_CALL,
NETWORK_COMMAND_REMOTE_CALL = 0,
NETWORK_COMMAND_REMOTE_SET,
NETWORK_COMMAND_SIMPLIFY_PATH,
NETWORK_COMMAND_CONFIRM_PATH,
NETWORK_COMMAND_RAW,
};
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,
};
enum RPCMode {
RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)

View file

@ -32,6 +32,7 @@
#include "core/core_string_names.h"
#include "core/project_settings.h"
#include <stdint.h>
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
int ScriptServer::_language_count = 0;
@ -644,6 +645,14 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam
return Variant();
}
uint16_t PlaceHolderScriptInstance::get_rpc_method_id(const StringName &p_method) const {
return UINT16_MAX;
}
uint16_t PlaceHolderScriptInstance::get_rset_property_id(const StringName &p_method) const {
return UINT16_MAX;
}
PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) :
owner(p_owner),
language(p_language),

View file

@ -40,6 +40,21 @@ class ScriptLanguage;
typedef void (*ScriptEditRequestFunction)(const String &p_path);
struct ScriptNetData {
StringName name;
MultiplayerAPI::RPCMode mode;
bool operator==(ScriptNetData const &p_other) const {
return name == p_other.name;
}
};
struct SortNetData {
StringName::AlphCompare compare;
bool operator()(const ScriptNetData &p_a, const ScriptNetData &p_b) const {
return compare(p_a.name, p_b.name);
}
};
class ScriptServer {
enum {
@ -154,6 +169,18 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
virtual Vector<ScriptNetData> get_rpc_methods() const = 0;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
virtual Vector<ScriptNetData> get_rset_properties() const = 0;
virtual uint16_t get_rset_property_id(const StringName &p_property) const = 0;
virtual StringName get_rset_property(const uint16_t p_rset_property_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
Script() {}
};
@ -195,7 +222,16 @@ 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 Vector<ScriptNetData> get_rpc_methods() const = 0;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
virtual StringName get_rpc_method(uint16_t p_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
virtual Vector<ScriptNetData> get_rset_properties() const = 0;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const = 0;
virtual StringName get_rset_property(uint16_t p_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
virtual ScriptLanguage *get_language() = 0;
@ -409,7 +445,16 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = NULL);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = NULL);
virtual Vector<ScriptNetData> get_rpc_methods() const { return Vector<ScriptNetData>(); }
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(uint16_t p_id) const { return StringName(); }
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
virtual Vector<ScriptNetData> get_rset_properties() const { return Vector<ScriptNetData>(); }
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(uint16_t p_id) const { return StringName(); }
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);

View file

@ -36,6 +36,7 @@
#include "core/project_settings.h"
#include "core/variant.h"
#include "gdnative/gdnative.h"
#include <stdint.h>
#include "nativescript.h"
@ -67,6 +68,14 @@ void GDAPI godot_nativescript_register_class(void *p_gdnative_handle, const char
if (classes->has(p_base)) {
desc.base_data = &(*classes)[p_base];
desc.base_native_type = desc.base_data->base_native_type;
const NativeScriptDesc *b = desc.base_data;
while (b) {
desc.rpc_count += b->rpc_count;
desc.rset_count += b->rset_count;
b = b->base_data;
}
} else {
desc.base_data = NULL;
desc.base_native_type = p_base;
@ -87,10 +96,20 @@ void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const
desc.destroy_func = p_destroy_func;
desc.is_tool = true;
desc.base = p_base;
desc.rpc_count = 0;
desc.rset_count = 0;
if (classes->has(p_base)) {
desc.base_data = &(*classes)[p_base];
desc.base_native_type = desc.base_data->base_native_type;
const NativeScriptDesc *b = desc.base_data;
while (b) {
desc.rpc_count += b->rpc_count;
desc.rset_count += b->rset_count;
b = b->base_data;
}
} else {
desc.base_data = NULL;
desc.base_native_type = p_base;
@ -109,6 +128,11 @@ void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const cha
NativeScriptDesc::Method method;
method.method = p_method;
method.rpc_mode = p_attr.rpc_type;
method.rpc_method_id = UINT16_MAX;
if (p_attr.rpc_type != GODOT_METHOD_RPC_MODE_DISABLED) {
method.rpc_method_id = E->get().rpc_count;
E->get().rpc_count += 1;
}
method.info = MethodInfo(p_function_name);
E->get().methods.insert(p_function_name, method);
@ -125,6 +149,10 @@ void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const c
property.default_value = *(Variant *)&p_attr->default_value;
property.getter = p_get_func;
property.rset_mode = p_attr->rset_type;
if (p_attr->rset_type != GODOT_METHOD_RPC_MODE_DISABLED) {
property.rset_property_id = E->get().rset_count;
E->get().rset_count += 1;
}
property.setter = p_set_func;
property.info = PropertyInfo((Variant::Type)p_attr->type,
p_path,

View file

@ -30,6 +30,8 @@
#include "nativescript.h"
#include <stdint.h>
#include "gdnative/gdnative.h"
#include "core/core_string_names.h"
@ -402,6 +404,262 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const {
}
}
Vector<ScriptNetData> NativeScript::get_rpc_methods() const {
Vector<ScriptNetData> v;
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
for (Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.front(); E; E = E->next()) {
if (E->get().rpc_mode != GODOT_METHOD_RPC_MODE_DISABLED) {
ScriptNetData nd;
nd.name = E->key();
nd.mode = MultiplayerAPI::RPCMode(E->get().rpc_mode);
v.push_back(nd);
}
}
script_data = script_data->base_data;
}
return v;
}
uint16_t NativeScript::get_rpc_method_id(const StringName &p_method) const {
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
if (E) {
return E->get().rpc_method_id;
}
script_data = script_data->base_data;
}
return UINT16_MAX;
}
StringName NativeScript::get_rpc_method(uint16_t p_id) const {
ERR_FAIL_COND_V(p_id == UINT16_MAX, StringName());
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
for (Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.front(); E; E = E->next()) {
if (E->get().rpc_method_id == p_id) {
return E->key();
}
}
script_data = script_data->base_data;
}
return StringName();
}
MultiplayerAPI::RPCMode NativeScript::get_rpc_mode_by_id(uint16_t p_id) const {
ERR_FAIL_COND_V(p_id == UINT16_MAX, MultiplayerAPI::RPC_MODE_DISABLED);
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
for (Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.front(); E; E = E->next()) {
if (E->get().rpc_method_id == p_id) {
switch (E->get().rpc_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_MASTER:
return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_PUPPET:
return MultiplayerAPI::RPC_MODE_PUPPET;
case GODOT_METHOD_RPC_MODE_REMOTESYNC:
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
case GODOT_METHOD_RPC_MODE_MASTERSYNC:
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
default:
return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
}
script_data = script_data->base_data;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
}
MultiplayerAPI::RPCMode NativeScript::get_rpc_mode(const StringName &p_method) const {
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
if (E) {
switch (E->get().rpc_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_MASTER:
return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_PUPPET:
return MultiplayerAPI::RPC_MODE_PUPPET;
case GODOT_METHOD_RPC_MODE_REMOTESYNC:
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
case GODOT_METHOD_RPC_MODE_MASTERSYNC:
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
default:
return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
script_data = script_data->base_data;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
}
Vector<ScriptNetData> NativeScript::get_rset_properties() const {
Vector<ScriptNetData> v;
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
if (E.get().rset_mode != GODOT_METHOD_RPC_MODE_DISABLED) {
ScriptNetData nd;
nd.name = E.key();
nd.mode = MultiplayerAPI::RPCMode(E.get().rset_mode);
v.push_back(nd);
}
}
script_data = script_data->base_data;
}
return v;
}
uint16_t NativeScript::get_rset_property_id(const StringName &p_variable) const {
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.find(p_variable);
if (E) {
return E.get().rset_property_id;
}
script_data = script_data->base_data;
}
return UINT16_MAX;
}
StringName NativeScript::get_rset_property(uint16_t p_id) const {
ERR_FAIL_COND_V(p_id == UINT16_MAX, StringName());
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
if (E.get().rset_property_id == p_id) {
return E.key();
}
}
script_data = script_data->base_data;
}
return StringName();
}
MultiplayerAPI::RPCMode NativeScript::get_rset_mode_by_id(uint16_t p_id) const {
ERR_FAIL_COND_V(p_id == UINT16_MAX, MultiplayerAPI::RPC_MODE_DISABLED);
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
if (E.get().rset_property_id == p_id) {
switch (E.get().rset_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_MASTER:
return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_PUPPET:
return MultiplayerAPI::RPC_MODE_PUPPET;
case GODOT_METHOD_RPC_MODE_REMOTESYNC:
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
case GODOT_METHOD_RPC_MODE_MASTERSYNC:
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
default:
return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
}
script_data = script_data->base_data;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
}
MultiplayerAPI::RPCMode NativeScript::get_rset_mode(const StringName &p_variable) const {
NativeScriptDesc *script_data = get_script_desc();
while (script_data) {
OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.find(p_variable);
if (E) {
switch (E.get().rset_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_MASTER:
return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_PUPPET:
return MultiplayerAPI::RPC_MODE_PUPPET;
case GODOT_METHOD_RPC_MODE_REMOTESYNC:
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
case GODOT_METHOD_RPC_MODE_MASTERSYNC:
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
default:
return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
script_data = script_data->base_data;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
}
String NativeScript::get_class_documentation() const {
NativeScriptDesc *script_data = get_script_desc();
@ -803,72 +1061,44 @@ Ref<Script> NativeScriptInstance::get_script() const {
return script;
}
Vector<ScriptNetData> NativeScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
uint16_t NativeScriptInstance::get_rpc_method_id(const StringName &p_method) const {
return script->get_rpc_method_id(p_method);
}
StringName NativeScriptInstance::get_rpc_method(uint16_t p_id) const {
return script->get_rpc_method(p_id);
}
MultiplayerAPI::RPCMode NativeScriptInstance::get_rpc_mode_by_id(uint16_t p_id) const {
return script->get_rpc_mode_by_id(p_id);
}
MultiplayerAPI::RPCMode NativeScriptInstance::get_rpc_mode(const StringName &p_method) const {
return script->get_rpc_mode(p_method);
}
NativeScriptDesc *script_data = GET_SCRIPT_DESC();
Vector<ScriptNetData> NativeScriptInstance::get_rset_properties() const {
return script->get_rset_properties();
}
while (script_data) {
uint16_t NativeScriptInstance::get_rset_property_id(const StringName &p_variable) const {
return script->get_rset_property_id(p_variable);
}
Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
if (E) {
switch (E->get().rpc_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_MASTER:
return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_PUPPET:
return MultiplayerAPI::RPC_MODE_PUPPET;
case GODOT_METHOD_RPC_MODE_REMOTESYNC:
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
case GODOT_METHOD_RPC_MODE_MASTERSYNC:
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
default:
return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
StringName NativeScriptInstance::get_rset_property(uint16_t p_id) const {
return script->get_rset_property(p_id);
}
script_data = script_data->base_data;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
MultiplayerAPI::RPCMode NativeScriptInstance::get_rset_mode_by_id(uint16_t p_id) const {
return script->get_rset_mode_by_id(p_id);
}
MultiplayerAPI::RPCMode NativeScriptInstance::get_rset_mode(const StringName &p_variable) const {
NativeScriptDesc *script_data = GET_SCRIPT_DESC();
while (script_data) {
OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.find(p_variable);
if (E) {
switch (E.get().rset_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_MASTER:
return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_PUPPET:
return MultiplayerAPI::RPC_MODE_PUPPET;
case GODOT_METHOD_RPC_MODE_REMOTESYNC:
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
case GODOT_METHOD_RPC_MODE_MASTERSYNC:
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
default:
return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
script_data = script_data->base_data;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
return script->get_rset_mode(p_variable);
}
ScriptLanguage *NativeScriptInstance::get_language() {

View file

@ -54,6 +54,7 @@ struct NativeScriptDesc {
godot_instance_method method;
MethodInfo info;
int rpc_mode;
uint16_t rpc_method_id;
String documentation;
};
struct Property {
@ -62,6 +63,7 @@ struct NativeScriptDesc {
PropertyInfo info;
Variant default_value;
int rset_mode;
uint16_t rset_property_id;
String documentation;
};
@ -70,7 +72,9 @@ struct NativeScriptDesc {
String documentation;
};
uint16_t rpc_count;
Map<StringName, Method> methods;
uint16_t rset_count;
OrderedHashMap<StringName, Property> properties;
Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals
StringName base;
@ -86,7 +90,9 @@ struct NativeScriptDesc {
bool is_tool;
inline NativeScriptDesc() :
rpc_count(0),
methods(),
rset_count(0),
properties(),
signals_(),
base(),
@ -174,6 +180,18 @@ public:
virtual void get_script_method_list(List<MethodInfo> *p_list) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
String get_class_documentation() const;
String get_method_documentation(const StringName &p_method) const;
String get_signal_documentation(const StringName &p_signal_name) const;
@ -210,8 +228,19 @@ public:
virtual void notification(int p_notification);
String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual ScriptLanguage *get_language();
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);

View file

@ -93,10 +93,42 @@ void PluginScriptInstance::notification(int p_notification) {
_desc->notification(_data, p_notification);
}
Vector<ScriptNetData> PluginScriptInstance::get_rpc_methods() const {
return _script->get_rpc_methods();
}
uint16_t PluginScriptInstance::get_rpc_method_id(const StringName &p_variable) const {
return _script->get_rpc_method_id(p_variable);
}
StringName PluginScriptInstance::get_rpc_method(uint16_t p_id) const {
return _script->get_rpc_method(p_id);
}
MultiplayerAPI::RPCMode PluginScriptInstance::get_rpc_mode_by_id(uint16_t p_id) const {
return _script->get_rpc_mode_by_id(p_id);
}
MultiplayerAPI::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const {
return _script->get_rpc_mode(p_method);
}
Vector<ScriptNetData> PluginScriptInstance::get_rset_properties() const {
return _script->get_rset_properties();
}
uint16_t PluginScriptInstance::get_rset_property_id(const StringName &p_variable) const {
return _script->get_rset_property_id(p_variable);
}
StringName PluginScriptInstance::get_rset_property(uint16_t p_id) const {
return _script->get_rset_property(p_id);
}
MultiplayerAPI::RPCMode PluginScriptInstance::get_rset_mode_by_id(uint16_t p_id) const {
return _script->get_rset_mode_by_id(p_id);
}
MultiplayerAPI::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const {
return _script->get_rset_mode(p_variable);
}

View file

@ -76,7 +76,16 @@ public:
void set_path(const String &p_path);
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void refcount_incremented();

View file

@ -34,6 +34,8 @@
#include "pluginscript_instance.h"
#include "pluginscript_script.h"
#include <stdint.h>
#ifdef DEBUG_ENABLED
#define __ASSERT_SCRIPT_REASON "Cannot retrieve PluginScript class for this script, is your code correct?"
#define ASSERT_SCRIPT_VALID() \
@ -298,18 +300,31 @@ Error PluginScript::reload(bool p_keep_state) {
_member_lines[*key] = (*members)[*key];
}
Array *methods = (Array *)&manifest.methods;
_rpc_methods.clear();
_rpc_variables.clear();
if (_ref_base_parent.is_valid()) {
_rpc_methods = _ref_base_parent->get_rpc_methods();
_rpc_variables = _ref_base_parent->get_rset_properties();
}
for (int i = 0; i < methods->size(); ++i) {
Dictionary v = (*methods)[i];
MethodInfo mi = MethodInfo::from_dict(v);
_methods_info[mi.name] = mi;
// rpc_mode is passed as an optional field and is not part of MethodInfo
Variant var = v["rpc_mode"];
if (var == Variant()) {
_methods_rpc_mode[mi.name] = MultiplayerAPI::RPC_MODE_DISABLED;
} else {
_methods_rpc_mode[mi.name] = MultiplayerAPI::RPCMode(int(var));
if (var != Variant()) {
ScriptNetData nd;
nd.name = mi.name;
nd.mode = MultiplayerAPI::RPCMode(int(var));
if (_rpc_methods.find(nd) == -1) {
_rpc_methods.push_back(nd);
}
}
}
// Sort so we are 100% that they are always the same.
_rpc_methods.sort_custom<SortNetData>();
Array *signals = (Array *)&manifest.signals;
for (int i = 0; i < signals->size(); ++i) {
Variant v = (*signals)[i];
@ -324,13 +339,19 @@ Error PluginScript::reload(bool p_keep_state) {
_properties_default_values[pi.name] = v["default_value"];
// rset_mode is passed as an optional field and is not part of PropertyInfo
Variant var = v["rset_mode"];
if (var == Variant()) {
_methods_rpc_mode[pi.name] = MultiplayerAPI::RPC_MODE_DISABLED;
} else {
_methods_rpc_mode[pi.name] = MultiplayerAPI::RPCMode(int(var));
if (var != Variant()) {
ScriptNetData nd;
nd.name = pi.name;
nd.mode = MultiplayerAPI::RPCMode(int(var));
if (_rpc_variables.find(nd) == -1) {
_rpc_variables.push_back(nd);
}
}
}
// Sort so we are 100% that they are always the same.
_rpc_variables.sort_custom<SortNetData>();
#ifdef TOOLS_ENABLED
/*for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
@ -455,24 +476,70 @@ int PluginScript::get_member_line(const StringName &p_member) const {
return -1;
}
Vector<ScriptNetData> PluginScript::get_rpc_methods() const {
return _rpc_methods;
}
uint16_t PluginScript::get_rpc_method_id(const StringName &p_method) const {
ASSERT_SCRIPT_VALID_V(UINT16_MAX);
for (int i = 0; i < _rpc_methods.size(); i++) {
if (_rpc_methods[i].name == p_method) {
return i;
}
}
return UINT16_MAX;
}
StringName PluginScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
ASSERT_SCRIPT_VALID_V(StringName());
if (p_rpc_method_id >= _rpc_methods.size())
return StringName();
return _rpc_methods[p_rpc_method_id].name;
}
MultiplayerAPI::RPCMode PluginScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
if (p_rpc_method_id >= _rpc_methods.size())
return MultiplayerAPI::RPC_MODE_DISABLED;
return _rpc_methods[p_rpc_method_id].mode;
}
MultiplayerAPI::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const {
ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _methods_rpc_mode.find(p_method);
if (e != NULL) {
return e->get();
} else {
return MultiplayerAPI::RPC_MODE_DISABLED;
return get_rpc_mode_by_id(get_rpc_method_id(p_method));
}
Vector<ScriptNetData> PluginScript::get_rset_properties() const {
return _rpc_variables;
}
uint16_t PluginScript::get_rset_property_id(const StringName &p_property) const {
ASSERT_SCRIPT_VALID_V(UINT16_MAX);
for (int i = 0; i < _rpc_variables.size(); i++) {
if (_rpc_variables[i].name == p_property) {
return i;
}
}
return UINT16_MAX;
}
StringName PluginScript::get_rset_property(const uint16_t p_rset_property_id) const {
ASSERT_SCRIPT_VALID_V(StringName());
if (p_rset_property_id >= _rpc_variables.size())
return StringName();
return _rpc_variables[p_rset_property_id].name;
}
MultiplayerAPI::RPCMode PluginScript::get_rset_mode_by_id(const uint16_t p_rset_property_id) const {
ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
if (p_rset_property_id >= _rpc_variables.size())
return MultiplayerAPI::RPC_MODE_DISABLED;
return _rpc_variables[p_rset_property_id].mode;
}
MultiplayerAPI::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const {
ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _variables_rset_mode.find(p_variable);
if (e != NULL) {
return e->get();
} else {
return MultiplayerAPI::RPC_MODE_DISABLED;
}
return get_rset_mode_by_id(get_rset_property_id(p_variable));
}
PluginScript::PluginScript() :

View file

@ -60,8 +60,8 @@ private:
Map<StringName, PropertyInfo> _properties_info;
Map<StringName, MethodInfo> _signals_info;
Map<StringName, MethodInfo> _methods_info;
Map<StringName, MultiplayerAPI::RPCMode> _variables_rset_mode;
Map<StringName, MultiplayerAPI::RPCMode> _methods_rpc_mode;
Vector<ScriptNetData> _rpc_methods;
Vector<ScriptNetData> _rpc_variables;
Set<Object *> _instances;
//exported members
@ -118,8 +118,17 @@ public:
virtual int get_member_line(const StringName &p_member) const;
MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_property) const;
virtual StringName get_rset_property(const uint16_t p_rset_property_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
PluginScript();
void init(PluginScriptLanguage *language);

View file

@ -30,6 +30,8 @@
#include "gdscript.h"
#include <stdint.h>
#include "core/core_string_names.h"
#include "core/engine.h"
#include "core/global_constants.h"
@ -610,6 +612,53 @@ Error GDScript::reload(bool p_keep_state) {
_set_subclass_path(E->get(), path);
}
// Copy the base rpc methods so we don't mask their IDs.
rpc_functions.clear();
rpc_variables.clear();
if (base.is_valid()) {
rpc_functions = base->rpc_functions;
rpc_variables = base->rpc_variables;
}
GDScript *cscript = this;
Map<StringName, Ref<GDScript> >::Element *sub_E = subclasses.front();
while (cscript) {
// RPC Methods
for (Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.front(); E; E = E->next()) {
if (E->get()->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
ScriptNetData nd;
nd.name = E->key();
nd.mode = E->get()->get_rpc_mode();
if (-1 == rpc_functions.find(nd)) {
rpc_functions.push_back(nd);
}
}
}
// RSet
for (Map<StringName, MemberInfo>::Element *E = cscript->member_indices.front(); E; E = E->next()) {
if (E->get().rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
ScriptNetData nd;
nd.name = E->key();
nd.mode = E->get().rpc_mode;
if (-1 == rpc_variables.find(nd)) {
rpc_variables.push_back(nd);
}
}
}
if (cscript != this)
sub_E = sub_E->next();
if (sub_E)
cscript = sub_E->get().ptr();
else
cscript = NULL;
}
// Sort so we are 100% that they are always the same.
rpc_functions.sort_custom<SortNetData>();
rpc_variables.sort_custom<SortNetData>();
return OK;
}
@ -635,6 +684,60 @@ void GDScript::get_members(Set<StringName> *p_members) {
}
}
Vector<ScriptNetData> GDScript::get_rpc_methods() const {
return rpc_functions;
}
uint16_t GDScript::get_rpc_method_id(const StringName &p_method) const {
for (int i = 0; i < rpc_functions.size(); i++) {
if (rpc_functions[i].name == p_method) {
return i;
}
}
return UINT16_MAX;
}
StringName GDScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), StringName());
return rpc_functions[p_rpc_method_id].name;
}
MultiplayerAPI::RPCMode GDScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
return rpc_functions[p_rpc_method_id].mode;
}
MultiplayerAPI::RPCMode GDScript::get_rpc_mode(const StringName &p_method) const {
return get_rpc_mode_by_id(get_rpc_method_id(p_method));
}
Vector<ScriptNetData> GDScript::get_rset_properties() const {
return rpc_variables;
}
uint16_t GDScript::get_rset_property_id(const StringName &p_variable) const {
for (int i = 0; i < rpc_variables.size(); i++) {
if (rpc_variables[i].name == p_variable) {
return i;
}
}
return UINT16_MAX;
}
StringName GDScript::get_rset_property(const uint16_t p_rset_member_id) const {
ERR_FAIL_COND_V(p_rset_member_id >= rpc_variables.size(), StringName());
return rpc_variables[p_rset_member_id].name;
}
MultiplayerAPI::RPCMode GDScript::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
ERR_FAIL_COND_V(p_rset_member_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
return rpc_functions[p_rset_member_id].mode;
}
MultiplayerAPI::RPCMode GDScript::get_rset_mode(const StringName &p_variable) const {
return get_rset_mode_by_id(get_rset_property_id(p_variable));
}
Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
GDScript *top = this;
@ -1291,40 +1394,44 @@ ScriptLanguage *GDScriptInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
Vector<ScriptNetData> GDScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
uint16_t GDScriptInstance::get_rpc_method_id(const StringName &p_method) const {
return script->get_rpc_method_id(p_method);
}
StringName GDScriptInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
return script->get_rpc_method(p_rpc_method_id);
}
MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
return script->get_rpc_mode_by_id(p_rpc_method_id);
}
MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_method) const {
return script->get_rpc_mode(p_method);
}
const GDScript *cscript = script.ptr();
Vector<ScriptNetData> GDScriptInstance::get_rset_properties() const {
return script->get_rset_properties();
}
while (cscript) {
const Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.find(p_method);
if (E) {
uint16_t GDScriptInstance::get_rset_property_id(const StringName &p_variable) const {
return script->get_rset_property_id(p_variable);
}
if (E->get()->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
return E->get()->get_rpc_mode();
}
}
cscript = cscript->_base;
}
StringName GDScriptInstance::get_rset_property(const uint16_t p_rset_member_id) const {
return script->get_rset_property(p_rset_member_id);
}
return MultiplayerAPI::RPC_MODE_DISABLED;
MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
return script->get_rset_mode_by_id(p_rset_member_id);
}
MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_variable) const {
const GDScript *cscript = script.ptr();
while (cscript) {
const Map<StringName, GDScript::MemberInfo>::Element *E = cscript->member_indices.find(p_variable);
if (E) {
if (E->get().rpc_mode) {
return E->get().rpc_mode;
}
}
cscript = cscript->_base;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
return script->get_rset_mode(p_variable);
}
void GDScriptInstance::reload_members() {

View file

@ -85,6 +85,8 @@ class GDScript : public Script {
Map<StringName, MemberInfo> member_indices; //members are just indices to the instanced script.
Map<StringName, Ref<GDScript> > subclasses;
Map<StringName, Vector<StringName> > _signals;
Vector<ScriptNetData> rpc_functions;
Vector<ScriptNetData> rpc_variables;
#ifdef TOOLS_ENABLED
@ -213,6 +215,18 @@ public:
virtual void get_constants(Map<StringName, Variant> *p_constants);
virtual void get_members(Set<StringName> *p_members);
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; }
#endif
@ -264,7 +278,16 @@ public:
void reload_members();
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
GDScriptInstance();

View file

@ -31,6 +31,7 @@
#include "csharp_script.h"
#include <mono/metadata/threads.h>
#include <stdint.h>
#include "core/io/json.h"
#include "core/os/file_access.h"
@ -1979,67 +1980,44 @@ bool CSharpInstance::refcount_decremented() {
return ref_dying;
}
MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(IMonoClassMember *p_member) const {
Vector<ScriptNetData> CSharpInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute)))
return MultiplayerAPI::RPC_MODE_REMOTE;
if (p_member->has_attribute(CACHED_CLASS(MasterAttribute)))
return MultiplayerAPI::RPC_MODE_MASTER;
if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute)))
return MultiplayerAPI::RPC_MODE_PUPPET;
if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute)))
return MultiplayerAPI::RPC_MODE_PUPPET;
if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute)))
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
if (p_member->has_attribute(CACHED_CLASS(SyncAttribute)))
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute)))
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
if (p_member->has_attribute(CACHED_CLASS(PuppetSyncAttribute)))
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
uint16_t CSharpInstance::get_rpc_method_id(const StringName &p_method) const {
return script->get_rpc_method_id(p_method);
}
return MultiplayerAPI::RPC_MODE_DISABLED;
StringName CSharpInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
return script->get_rpc_method(p_rpc_method_id);
}
MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
return script->get_rpc_mode_by_id(p_rpc_method_id);
}
MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const {
return script->get_rpc_mode(p_method);
}
GD_MONO_SCOPE_THREAD_ATTACH;
Vector<ScriptNetData> CSharpInstance::get_rset_properties() const {
return script->get_rset_properties();
}
GDMonoClass *top = script->script_class;
uint16_t CSharpInstance::get_rset_property_id(const StringName &p_variable) const {
return script->get_rset_property_id(p_variable);
}
while (top && top != script->native) {
GDMonoMethod *method = top->get_fetched_method_unknown_params(p_method);
StringName CSharpInstance::get_rset_property(const uint16_t p_rset_member_id) const {
return script->get_rset_property(p_rset_member_id);
}
if (method && !method->is_static())
return _member_get_rpc_mode(method);
top = top->get_parent_class();
}
return MultiplayerAPI::RPC_MODE_DISABLED;
MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
return script->get_rset_mode_by_id(p_rset_member_id);
}
MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const {
GD_MONO_SCOPE_THREAD_ATTACH;
GDMonoClass *top = script->script_class;
while (top && top != script->native) {
GDMonoField *field = top->get_field(p_variable);
if (field && !field->is_static())
return _member_get_rpc_mode(field);
GDMonoProperty *property = top->get_property(p_variable);
if (property && !property->is_static())
return _member_get_rpc_mode(property);
top = top->get_parent_class();
}
return MultiplayerAPI::RPC_MODE_DISABLED;
return script->get_rset_mode(p_variable);
}
void CSharpInstance::notification(int p_notification) {
@ -3251,6 +3229,69 @@ Error CSharpScript::reload(bool p_keep_state) {
_update_exports();
}
rpc_functions.clear();
rpc_variables.clear();
GDMonoClass *top = script_class;
while (top && top != native) {
{
Vector<GDMonoMethod *> methods = top->get_all_methods();
for (int i = 0; i < methods.size(); i++) {
if (!methods[i]->is_static()) {
MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(methods[i]);
if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
ScriptNetData nd;
nd.name = methods[i]->get_name();
nd.mode = mode;
if (-1 == rpc_functions.find(nd)) {
rpc_functions.push_back(nd);
}
}
}
}
}
{
Vector<GDMonoField *> fields = top->get_all_fields();
for (int i = 0; i < fields.size(); i++) {
if (!fields[i]->is_static()) {
MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(fields[i]);
if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
ScriptNetData nd;
nd.name = fields[i]->get_name();
nd.mode = mode;
if (-1 == rpc_variables.find(nd)) {
rpc_variables.push_back(nd);
}
}
}
}
}
{
Vector<GDMonoProperty *> properties = top->get_all_properties();
for (int i = 0; i < properties.size(); i++) {
if (!properties[i]->is_static()) {
MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(properties[i]);
if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
ScriptNetData nd;
nd.name = properties[i]->get_name();
nd.mode = mode;
if (-1 == rpc_variables.find(nd)) {
rpc_variables.push_back(nd);
}
}
}
}
}
top = top->get_parent_class();
}
// Sort so we are 100% that they are always the same.
rpc_functions.sort_custom<SortNetData>();
rpc_variables.sort_custom<SortNetData>();
return OK;
}
@ -3324,6 +3365,82 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
return -1;
}
MultiplayerAPI::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute)))
return MultiplayerAPI::RPC_MODE_REMOTE;
if (p_member->has_attribute(CACHED_CLASS(MasterAttribute)))
return MultiplayerAPI::RPC_MODE_MASTER;
if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute)))
return MultiplayerAPI::RPC_MODE_PUPPET;
if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute)))
return MultiplayerAPI::RPC_MODE_PUPPET;
if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute)))
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
if (p_member->has_attribute(CACHED_CLASS(SyncAttribute)))
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute)))
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
if (p_member->has_attribute(CACHED_CLASS(PuppetSyncAttribute)))
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
return MultiplayerAPI::RPC_MODE_DISABLED;
}
Vector<ScriptNetData> CSharpScript::get_rpc_methods() const {
return rpc_functions;
}
uint16_t CSharpScript::get_rpc_method_id(const StringName &p_method) const {
for (int i = 0; i < rpc_functions.size(); i++) {
if (rpc_functions[i].name == p_method) {
return i;
}
}
return UINT16_MAX;
}
StringName CSharpScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), StringName());
return rpc_functions[p_rpc_method_id].name;
}
MultiplayerAPI::RPCMode CSharpScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
return rpc_functions[p_rpc_method_id].mode;
}
MultiplayerAPI::RPCMode CSharpScript::get_rpc_mode(const StringName &p_method) const {
return get_rpc_mode_by_id(get_rpc_method_id(p_method));
}
Vector<ScriptNetData> CSharpScript::get_rset_properties() const {
return rpc_variables;
}
uint16_t CSharpScript::get_rset_property_id(const StringName &p_variable) const {
for (int i = 0; i < rpc_variables.size(); i++) {
if (rpc_variables[i].name == p_variable) {
return i;
}
}
return UINT16_MAX;
}
StringName CSharpScript::get_rset_property(const uint16_t p_rset_member_id) const {
ERR_FAIL_COND_V(p_rset_member_id >= rpc_variables.size(), StringName());
return rpc_variables[p_rset_member_id].name;
}
MultiplayerAPI::RPCMode CSharpScript::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
ERR_FAIL_COND_V(p_rset_member_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
return rpc_functions[p_rset_member_id].mode;
}
MultiplayerAPI::RPCMode CSharpScript::get_rset_mode(const StringName &p_variable) const {
return get_rset_mode_by_id(get_rset_property_id(p_variable));
}
Error CSharpScript::load_source_code(const String &p_path) {
Error ferr = read_all_file_utf8(p_path, source);

View file

@ -113,6 +113,9 @@ class CSharpScript : public Script {
Map<StringName, Vector<Argument> > _signals;
bool signals_invalidated;
Vector<ScriptNetData> rpc_functions;
Vector<ScriptNetData> rpc_variables;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
@ -146,6 +149,8 @@ class CSharpScript : public Script {
static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
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;
protected:
static void _bind_methods();
@ -187,6 +192,18 @@ public:
virtual int get_member_line(const StringName &p_member) const;
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; }
#endif
@ -232,8 +249,6 @@ class CSharpInstance : public ScriptInstance {
void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
void get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state);
public:
@ -265,7 +280,16 @@ public:
virtual void refcount_incremented();
virtual bool refcount_decremented();
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
virtual StringName get_rset_property(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void notification(int p_notification);

View file

@ -30,6 +30,8 @@
#include "visual_script.h"
#include <stdint.h>
#include "core/core_string_names.h"
#include "core/os/os.h"
#include "core/project_settings.h"
@ -1102,6 +1104,60 @@ bool VisualScript::are_subnodes_edited() const {
}
#endif
Vector<ScriptNetData> VisualScript::get_rpc_methods() const {
return rpc_functions;
}
uint16_t VisualScript::get_rpc_method_id(const StringName &p_method) const {
for (int i = 0; i < rpc_functions.size(); i++) {
if (rpc_functions[i].name == p_method) {
return i;
}
}
return UINT16_MAX;
}
StringName VisualScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), StringName());
return rpc_functions[p_rpc_method_id].name;
}
MultiplayerAPI::RPCMode VisualScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
return rpc_functions[p_rpc_method_id].mode;
}
MultiplayerAPI::RPCMode VisualScript::get_rpc_mode(const StringName &p_method) const {
return get_rpc_mode_by_id(get_rpc_method_id(p_method));
}
Vector<ScriptNetData> VisualScript::get_rset_properties() const {
return rpc_variables;
}
uint16_t VisualScript::get_rset_property_id(const StringName &p_variable) const {
for (int i = 0; i < rpc_variables.size(); i++) {
if (rpc_variables[i].name == p_variable) {
return i;
}
}
return UINT16_MAX;
}
StringName VisualScript::get_rset_property(const uint16_t p_rset_property_id) const {
ERR_FAIL_COND_V(p_rset_property_id >= rpc_variables.size(), StringName());
return rpc_variables[p_rset_property_id].name;
}
MultiplayerAPI::RPCMode VisualScript::get_rset_mode_by_id(const uint16_t p_rset_variable_id) const {
ERR_FAIL_COND_V(p_rset_variable_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
return rpc_functions[p_rset_variable_id].mode;
}
MultiplayerAPI::RPCMode VisualScript::get_rset_mode(const StringName &p_variable) const {
return get_rset_mode_by_id(get_rset_property_id(p_variable));
}
void VisualScript::_set_data(const Dictionary &p_data) {
Dictionary d = p_data;
@ -1206,6 +1262,30 @@ void VisualScript::_set_data(const Dictionary &p_data) {
is_tool_script = d["is_tool_script"];
else
is_tool_script = false;
// Takes all the rpc methods
rpc_functions.clear();
rpc_variables.clear();
for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
if (E->get().function_id >= 0 && E->get().nodes.find(E->get().function_id)) {
Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node;
if (vsf.is_valid()) {
if (vsf->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
ScriptNetData nd;
nd.name = E->key();
nd.mode = vsf->get_rpc_mode();
if (rpc_functions.find(nd) == -1) {
rpc_functions.push_back(nd);
}
}
}
}
}
// Visual script doesn't have rset :(
// Sort so we are 100% that they are always the same.
rpc_functions.sort_custom<SortNetData>();
}
Dictionary VisualScript::_get_data() const {
@ -2043,31 +2123,44 @@ Ref<Script> VisualScriptInstance::get_script() const {
return script;
}
Vector<ScriptNetData> VisualScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
uint16_t VisualScriptInstance::get_rpc_method_id(const StringName &p_method) const {
return script->get_rpc_method_id(p_method);
}
StringName VisualScriptInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
return script->get_rpc_method(p_rpc_method_id);
}
MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
return script->get_rpc_mode_by_id(p_rpc_method_id);
}
MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const {
return script->get_rpc_mode(p_method);
}
if (p_method == script->get_default_func())
return MultiplayerAPI::RPC_MODE_DISABLED;
Vector<ScriptNetData> VisualScriptInstance::get_rset_properties() const {
return script->get_rset_properties();
}
const Map<StringName, VisualScript::Function>::Element *E = script->functions.find(p_method);
if (!E) {
return MultiplayerAPI::RPC_MODE_DISABLED;
}
uint16_t VisualScriptInstance::get_rset_property_id(const StringName &p_variable) const {
return script->get_rset_property_id(p_variable);
}
if (E->get().function_id >= 0 && E->get().nodes.has(E->get().function_id)) {
StringName VisualScriptInstance::get_rset_property(const uint16_t p_rset_property_id) const {
return script->get_rset_property(p_rset_property_id);
}
Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node;
if (vsf.is_valid()) {
return vsf->get_rpc_mode();
}
}
return MultiplayerAPI::RPC_MODE_DISABLED;
MultiplayerAPI::RPCMode VisualScriptInstance::get_rset_mode_by_id(const uint16_t p_rset_variable_id) const {
return script->get_rset_mode_by_id(p_rset_variable_id);
}
MultiplayerAPI::RPCMode VisualScriptInstance::get_rset_mode(const StringName &p_variable) const {
return MultiplayerAPI::RPC_MODE_DISABLED;
return script->get_rset_mode(p_variable);
}
void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_owner) {

View file

@ -245,6 +245,8 @@ private:
Map<StringName, Function> functions;
Map<StringName, Variable> variables;
Map<StringName, Vector<Argument> > custom_signals;
Vector<ScriptNetData> rpc_functions;
Vector<ScriptNetData> rpc_variables;
Map<Object *, VisualScriptInstance *> instances;
@ -362,6 +364,18 @@ public:
virtual int get_member_line(const StringName &p_member) const;
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_property) const;
virtual StringName get_rset_property(const uint16_t p_rset_property_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
#ifdef TOOLS_ENABLED
virtual bool are_subnodes_edited() const;
#endif
@ -441,7 +455,16 @@ public:
virtual ScriptLanguage *get_language();
virtual Vector<ScriptNetData> get_rpc_methods() const;
virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual Vector<ScriptNetData> get_rset_properties() const;
virtual uint16_t get_rset_property_id(const StringName &p_property) const;
virtual StringName get_rset_property(const uint16_t p_rset_property_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
VisualScriptInstance();

View file

@ -30,6 +30,8 @@
#include "node.h"
#include <stdint.h>
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
#include "core/message_queue.h"
@ -498,22 +500,38 @@ bool Node::is_network_master() const {
/***** RPC CONFIG ********/
void Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_mode) {
uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_mode) {
if (p_mode == MultiplayerAPI::RPC_MODE_DISABLED) {
data.rpc_methods.erase(p_method);
uint16_t mid = get_node_rpc_method_id(p_method);
if (mid == UINT16_MAX) {
// It's new
NetData nd;
nd.name = p_method;
nd.mode = p_mode;
data.rpc_methods.push_back(nd);
return ((uint16_t)data.rpc_properties.size() - 1) | (1 << 15);
} else {
data.rpc_methods[p_method] = p_mode;
};
int c_mid = (~(1 << 15)) & mid;
data.rpc_methods.write[c_mid].mode = p_mode;
return mid;
}
}
void Node::rset_config(const StringName &p_property, MultiplayerAPI::RPCMode p_mode) {
uint16_t Node::rset_config(const StringName &p_property, MultiplayerAPI::RPCMode p_mode) {
if (p_mode == MultiplayerAPI::RPC_MODE_DISABLED) {
data.rpc_properties.erase(p_property);
uint16_t pid = get_node_rset_property_id(p_property);
if (pid == UINT16_MAX) {
// It's new
NetData nd;
nd.name = p_property;
nd.mode = p_mode;
data.rpc_properties.push_back(nd);
return ((uint16_t)data.rpc_properties.size() - 1) | (1 << 15);
} else {
data.rpc_properties[p_property] = p_mode;
};
int c_pid = (~(1 << 15)) & pid;
data.rpc_properties.write[c_pid].mode = p_mode;
return pid;
}
}
/***** RPC FUNCTIONS ********/
@ -731,12 +749,94 @@ void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
multiplayer = p_multiplayer;
}
const Map<StringName, MultiplayerAPI::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) {
return data.rpc_methods.find(p_method);
uint16_t Node::get_node_rpc_method_id(const StringName &p_method) const {
for (int i = 0; i < data.rpc_methods.size(); i++) {
if (data.rpc_methods[i].name == p_method) {
// Returns `i` with the high bit set to 1 so we know that this id comes
// from the node and not the script.
return i | (1 << 15);
}
}
return UINT16_MAX;
}
const Map<StringName, MultiplayerAPI::RPCMode>::Element *Node::get_node_rset_mode(const StringName &p_property) {
return data.rpc_properties.find(p_property);
StringName Node::get_node_rpc_method(const uint16_t p_rpc_method_id) const {
// Make sure this is a node generated ID.
if (((1 << 15) & p_rpc_method_id) > 0) {
int mid = (~(1 << 15)) & p_rpc_method_id;
if (mid < data.rpc_methods.size())
return data.rpc_methods[mid].name;
}
return StringName();
}
MultiplayerAPI::RPCMode Node::get_node_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
// Make sure this is a node generated ID.
if (((1 << 15) & p_rpc_method_id) > 0) {
int mid = (~(1 << 15)) & p_rpc_method_id;
if (mid < data.rpc_methods.size())
return data.rpc_methods[mid].mode;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
}
MultiplayerAPI::RPCMode Node::get_node_rpc_mode(const StringName &p_method) const {
return get_node_rpc_mode_by_id(get_node_rpc_method_id(p_method));
}
uint16_t Node::get_node_rset_property_id(const StringName &p_property) const {
for (int i = 0; i < data.rpc_properties.size(); i++) {
if (data.rpc_properties[i].name == p_property) {
// Returns `i` with the high bit set to 1 so we know that this id comes
// from the node and not the script.
return i | (1 << 15);
}
}
return UINT16_MAX;
}
StringName Node::get_node_rset_property(const uint16_t p_rset_property_id) const {
// Make sure this is a node generated ID.
if (((1 << 15) & p_rset_property_id) > 0) {
int mid = (~(1 << 15)) & p_rset_property_id;
if (mid < data.rpc_properties.size())
return data.rpc_properties[mid].name;
}
return StringName();
}
MultiplayerAPI::RPCMode Node::get_node_rset_mode_by_id(const uint16_t p_rset_property_id) const {
if (((1 << 15) & p_rset_property_id) > 0) {
int mid = (~(1 << 15)) & p_rset_property_id;
if (mid < data.rpc_properties.size())
return data.rpc_properties[mid].mode;
}
return MultiplayerAPI::RPC_MODE_DISABLED;
}
MultiplayerAPI::RPCMode Node::get_node_rset_mode(const StringName &p_property) const {
return get_node_rset_mode_by_id(get_node_rset_property_id(p_property));
}
String Node::get_rpc_md5() const {
String rpc_list;
for (int i = 0; i < data.rpc_methods.size(); i += 1) {
rpc_list += String(data.rpc_methods[i].name);
}
for (int i = 0; i < data.rpc_properties.size(); i += 1) {
rpc_list += String(data.rpc_properties[i].name);
}
if (get_script_instance()) {
Vector<ScriptNetData> rpc = get_script_instance()->get_rpc_methods();
for (int i = 0; i < rpc.size(); i += 1) {
rpc_list += String(rpc[i].name);
}
rpc = get_script_instance()->get_rset_properties();
for (int i = 0; i < rpc.size(); i += 1) {
rpc_list += String(rpc[i].name);
}
}
return rpc_list.md5_text();
}
bool Node::can_process_notification(int p_what) const {

View file

@ -85,6 +85,11 @@ private:
GroupData() { persistent = false; }
};
struct NetData {
StringName name;
MultiplayerAPI::RPCMode mode;
};
struct Data {
String filename;
@ -118,8 +123,8 @@ private:
Node *pause_owner;
int network_master;
Map<StringName, MultiplayerAPI::RPCMode> rpc_methods;
Map<StringName, MultiplayerAPI::RPCMode> rpc_properties;
Vector<NetData> rpc_methods;
Vector<NetData> rpc_properties;
// variables used to properly sort the node when processing, ignored otherwise
//should move all the stuff below to bits
@ -427,8 +432,8 @@ public:
int get_network_master() const;
bool is_network_master() const;
void rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_mode); // config a local method for RPC
void rset_config(const StringName &p_property, MultiplayerAPI::RPCMode p_mode); // config a local property for RPC
uint16_t rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_mode); // config a local method for RPC
uint16_t rset_config(const StringName &p_property, MultiplayerAPI::RPCMode p_mode); // config a local property for RPC
void rpc(const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
void rpc_unreliable(const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
@ -446,8 +451,22 @@ public:
Ref<MultiplayerAPI> get_multiplayer() const;
Ref<MultiplayerAPI> get_custom_multiplayer() const;
void set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
const Map<StringName, MultiplayerAPI::RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
const Map<StringName, MultiplayerAPI::RPCMode>::Element *get_node_rset_mode(const StringName &p_property);
/// Returns the rpc method ID, otherwise UINT32_MAX
uint16_t get_node_rpc_method_id(const StringName &p_method) const;
StringName get_node_rpc_method(const uint16_t p_rpc_method_id) const;
MultiplayerAPI::RPCMode get_node_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
MultiplayerAPI::RPCMode get_node_rpc_mode(const StringName &p_method) const;
/// Returns the rpc property ID, otherwise UINT32_MAX
uint16_t get_node_rset_property_id(const StringName &p_property) const;
StringName get_node_rset_property(const uint16_t p_rset_property_id) const;
MultiplayerAPI::RPCMode get_node_rset_mode_by_id(const uint16_t p_rpc_method_id) const;
MultiplayerAPI::RPCMode get_node_rset_mode(const StringName &p_property) const;
/// Can be used to check if the rpc methods and the rset properties are the
/// same across the peers.
String get_rpc_md5() const;
Node();
~Node();