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:
parent
70dd7f4e1a
commit
eb07e87981
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() :
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue