summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/io/file_access_encrypted.cpp1
-rw-r--r--core/io/file_access_memory.cpp1
-rw-r--r--core/io/multiplayer_api.cpp314
-rw-r--r--core/io/multiplayer_api.h45
-rw-r--r--core/object/class_db.cpp62
-rw-r--r--core/object/class_db.h9
-rw-r--r--core/object/object.cpp47
-rw-r--r--core/object/object.h78
-rw-r--r--core/object/reference.cpp6
-rw-r--r--core/object/script_language.cpp8
-rw-r--r--core/object/script_language.h51
-rw-r--r--core/os/file_access.cpp1
12 files changed, 339 insertions, 284 deletions
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index b9514c8c8b..9e316291e8 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -257,6 +257,7 @@ Error FileAccessEncrypted::get_error() const {
void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
+ ERR_FAIL_COND(!p_src && p_length > 0);
if (pos < get_length()) {
for (uint64_t i = 0; i < p_length; i++) {
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 0114ab1765..d9be2a4a75 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -168,6 +168,7 @@ void FileAccessMemory::store_8(uint8_t p_byte) {
}
void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND(!p_src && p_length > 0);
uint64_t left = length - pos;
uint64_t write = MIN(p_length, left);
if (write < p_length) {
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index fda4083804..78ec7ea21a 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -44,6 +44,56 @@
#include "core/os/os.h"
#endif
+String _get_rpc_md5(const Node *p_node) {
+ String rpc_list;
+ const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods();
+ for (int i = 0; i < node_config.size(); i++) {
+ rpc_list += String(node_config[i].name);
+ }
+ if (p_node->get_script_instance()) {
+ const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
+ for (int i = 0; i < script_config.size(); i++) {
+ rpc_list += String(script_config[i].name);
+ }
+ }
+ return rpc_list.md5_text();
+}
+
+const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) {
+ const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods();
+ for (int i = 0; i < node_config.size(); i++) {
+ if (node_config[i].name == p_method) {
+ r_id = ((uint16_t)i) & (1 << 15);
+ return node_config[i];
+ }
+ }
+ if (p_node->get_script_instance()) {
+ const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
+ for (int i = 0; i < script_config.size(); i++) {
+ if (script_config[i].name == p_method) {
+ r_id = (uint16_t)i;
+ return script_config[i];
+ }
+ }
+ }
+ return MultiplayerAPI::RPCConfig();
+}
+
+const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) {
+ Vector<MultiplayerAPI::RPCConfig> config;
+ uint16_t id = p_id;
+ if (id & (1 << 15)) {
+ id = id & ~(1 << 15);
+ config = p_node->get_node_rpc_methods();
+ } else {
+ config = p_node->get_script_instance()->get_rpc_methods();
+ }
+ if (id < config.size()) {
+ return config[p_id];
+ }
+ return MultiplayerAPI::RPCConfig();
+}
+
_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) {
switch (mode) {
case MultiplayerAPI::RPC_MODE_DISABLED: {
@@ -231,8 +281,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
_process_confirm_path(p_from, p_packet, p_packet_len);
} break;
- case NETWORK_COMMAND_REMOTE_CALL:
- case NETWORK_COMMAND_REMOTE_SET: {
+ case NETWORK_COMMAND_REMOTE_CALL: {
// Extract packet meta
int packet_min_size = 1;
int name_id_offset = 1;
@@ -302,13 +351,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
}
const int packet_len = get_packet_len(node_target, p_packet_len);
- if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
- _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
-
- } else {
- _process_rset(node, name_id, p_from, p_packet, packet_len, packet_min_size);
- }
-
+ _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
} break;
case NETWORK_COMMAND_RAW: {
@@ -362,16 +405,11 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id,
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.
- 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());
+ const RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id);
+ ERR_FAIL_COND(config.name == StringName());
- bool can_call = _can_call_mode(p_node, rpc_mode, p_from);
- 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()) + ".");
+ bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from);
+ ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
int argc = 0;
bool byte_only = false;
@@ -424,47 +462,14 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id,
Callable::CallError ce;
- p_node->call(name, (const Variant **)argp.ptr(), argc, ce);
+ p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, name, (const Variant **)argp.ptr(), argc, ce);
+ String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce);
error = "RPC - " + error;
ERR_PRINT(error);
}
}
-void MultiplayerAPI::_process_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.
- 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(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
- _profile_node_data("in_rset", p_node->get_instance_id());
-#endif
-
- Variant value;
- Error err = _decode_and_decompress_variant(value, &p_packet[p_offset], p_packet_len - p_offset, nullptr);
-
- ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value.");
-
- bool valid;
-
- p_node->set(name, value, &valid);
- if (!valid) {
- 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 < 38, "Invalid packet received. Size too small.");
int ofs = 1;
@@ -487,7 +492,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
Node *node = root_node->get_node(path);
ERR_FAIL_COND(node == nullptr);
- const bool valid_rpc_checksum = node->get_rpc_md5() == methods_md5;
+ const bool valid_rpc_checksum = _get_rpc_md5(node) == methods_md5;
if (valid_rpc_checksum == false) {
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
}
@@ -569,7 +574,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC
const int path_len = encode_cstring(path.get_data(), nullptr);
// Extract MD5 from rpc methods list.
- const String methods_md5 = p_node->get_rpc_md5();
+ const String methods_md5 = _get_rpc_md5(p_node);
const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
Vector<uint8_t> packet;
@@ -752,7 +757,7 @@ Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const u
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) {
+void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree.");
ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree.");
@@ -797,7 +802,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
// - `NetworkNameIdCompression` in the next 1 bit.
// - `byte_only_or_no_args` in the next 1 bit.
// - So we still have the last bit free!
- uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
+ uint8_t command_type = NETWORK_COMMAND_REMOTE_CALL;
uint8_t node_id_compression = UINT8_MAX;
uint8_t name_id_compression = UINT8_MAX;
bool byte_only_or_no_args = false;
@@ -837,81 +842,42 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
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 only happen 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.
- int len(0);
- Error err = _encode_and_compress_variant(*p_arg[0], nullptr, 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_and_compress_variant(*p_arg[0], &(packet_cache.write[ofs]), len);
- ofs += len;
-
+ // Encode method ID
+ if (p_rpc_id <= UINT8_MAX) {
+ // The ID fits in 1 byte
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id);
+ ofs += 1;
} else {
- // 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,
- vformat("Unable to take the `method_id` for the function \"%s\" at path: \"%s\". This happens when the method is not marked as `remote`.", p_name, p_from->get_path()));
-
- 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;
- }
+ // The ID is larger, let's use 2 bytes
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(p_rpc_id, &(packet_cache.write[ofs]));
+ ofs += 2;
+ }
- if (p_argcount == 0) {
- byte_only_or_no_args = true;
- } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
- byte_only_or_no_args = true;
- // Special optimization when only the byte vector is sent.
- const Vector<uint8_t> data = *p_arg[0];
- MAKE_ROOM(ofs + data.size());
- memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
- ofs += data.size();
- } else {
- // Arguments
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = p_argcount;
- ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- int len(0);
- Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len);
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
- MAKE_ROOM(ofs + len);
- _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
- ofs += len;
- }
+ if (p_argcount == 0) {
+ byte_only_or_no_args = true;
+ } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
+ byte_only_or_no_args = true;
+ // Special optimization when only the byte vector is sent.
+ const Vector<uint8_t> data = *p_arg[0];
+ MAKE_ROOM(ofs + data.size());
+ memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
+ ofs += data.size();
+ } else {
+ // Arguments
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = p_argcount;
+ ofs += 1;
+ for (int i = 0; i < p_argcount; i++) {
+ int len(0);
+ Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len);
+ ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
+ MAKE_ROOM(ofs + len);
+ _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
+ ofs += len;
}
}
@@ -927,7 +893,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
#endif
// 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);
+ network_peer->set_transfer_mode(p_config.transfer_mode);
if (has_all_peers) {
// They all have verified paths, so send fast.
@@ -1015,19 +981,15 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
bool call_local_native = false;
bool call_local_script = false;
bool is_master = p_node->is_network_master();
-
+ uint16_t rpc_id = UINT16_MAX;
+ const RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id);
+ ERR_FAIL_COND_MSG(config.name == StringName(),
+ vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path()));
if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
- // Check that send mode can use local call.
-
- 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.
- rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
- call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc);
+ if (rpc_id & (1 << 15)) {
+ call_local_native = _should_call_local(config.rpc_mode, is_master, skip_rpc);
+ } else {
+ call_local_script = _should_call_local(config.rpc_mode, is_master, skip_rpc);
}
}
@@ -1036,7 +998,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
_profile_node_data("out_rpc", p_node->get_instance_id());
#endif
- _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
+ _send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
}
if (call_local_native) {
@@ -1071,70 +1033,6 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
ERR_FAIL_COND_MSG(skip_rpc && !(call_local_native || call_local_script), "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
}
-void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
- ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active.");
- ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree.");
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected.");
-
- int node_id = network_peer->get_unique_id();
- bool is_master = p_node->is_network_master();
- bool skip_rset = node_id == p_peer_id;
- bool set_local = false;
-
- 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.
- 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;
- int temp_id = rpc_sender_id;
-
- rpc_sender_id = get_network_unique_id();
- p_node->set(p_property, p_value, &valid);
- rpc_sender_id = temp_id;
-
- if (!valid) {
- String error = "rset() aborted in local set, property not found: - " + String(p_property) + ".";
- ERR_PRINT(error);
- return;
- }
- } else if (p_node->get_script_instance()) {
- // Attempt with script.
- rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
-
- set_local = _should_call_local(rpc_mode, is_master, skip_rset);
-
- if (set_local) {
- int temp_id = rpc_sender_id;
-
- rpc_sender_id = get_network_unique_id();
- bool valid = p_node->get_script_instance()->set(p_property, p_value);
- rpc_sender_id = temp_id;
-
- if (!valid) {
- String error = "rset() aborted in local script set, property not found: - " + String(p_property) + ".";
- ERR_PRINT(error);
- return;
- }
- }
- }
- }
-
- if (skip_rset) {
- ERR_FAIL_COND_MSG(!set_local, "RSET for '" + p_property + "' on yourself is not allowed by selected mode.");
- return;
- }
-
-#ifdef DEBUG_ENABLED
- _profile_node_data("out_rset", p_node->get_instance_id());
-#endif
-
- const Variant *vptr = &p_value;
-
- _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
-}
-
Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) {
ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet.");
ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active.");
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
index 7f88b53a27..6a251cf77b 100644
--- a/core/io/multiplayer_api.h
+++ b/core/io/multiplayer_api.h
@@ -37,6 +37,35 @@
class MultiplayerAPI : public Reference {
GDCLASS(MultiplayerAPI, Reference);
+public:
+ enum RPCMode {
+ RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_REMOTE, // Using rpc() on it will call method in all remote peers
+ RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote
+ RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets
+ RPC_MODE_REMOTESYNC, // Using rpc() on it will call method in all remote peers and locally
+ RPC_MODE_MASTERSYNC, // Using rpc() on it will call method in the master peer and locally
+ RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method in all puppets peers and locally
+ };
+
+ struct RPCConfig {
+ StringName name;
+ RPCMode rpc_mode = RPC_MODE_DISABLED;
+ NetworkedMultiplayerPeer::TransferMode transfer_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE;
+ int channel = 0;
+
+ bool operator==(RPCConfig const &p_other) const {
+ return name == p_other.name;
+ }
+ };
+
+ struct SortRPCConfig {
+ StringName::AlphCompare compare;
+ bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const {
+ return compare(p_a.name, p_b.name);
+ }
+ };
+
private:
//path sent caches
struct PathSentCache {
@@ -72,10 +101,9 @@ protected:
void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
- void _process_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);
+ void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
Error _encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);
@@ -84,7 +112,6 @@ protected:
public:
enum NetworkCommands {
NETWORK_COMMAND_REMOTE_CALL = 0,
- NETWORK_COMMAND_REMOTE_SET,
NETWORK_COMMAND_SIMPLIFY_PATH,
NETWORK_COMMAND_CONFIRM_PATH,
NETWORK_COMMAND_RAW,
@@ -101,16 +128,6 @@ public:
NETWORK_NAME_ID_COMPRESSION_16,
};
- enum RPCMode {
- RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
- RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers
- RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote
- RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets
- RPC_MODE_REMOTESYNC, // Using rpc() on it will call method / set property in all remote peers and locally
- RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally
- RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally
- };
-
void poll();
void clear();
void set_root_node(Node *p_node);
@@ -121,8 +138,6 @@ public:
// Called by Node.rpc
void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
- // Called by Node.rset
- void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
void _add_peer(int p_id);
void _del_peer(int p_id);
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index fb7eb42738..5bf874ccae 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -501,12 +501,27 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam
compat_classes[p_class] = p_fallback;
}
+thread_local bool initializing_with_extension = false;
+thread_local ObjectNativeExtension *initializing_extension = nullptr;
+thread_local void *initializing_extension_instance = nullptr;
+
+void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, void **r_extension_instance) {
+ if (initializing_with_extension) {
+ *r_extension = initializing_extension;
+ *r_extension_instance = initializing_extension_instance;
+ initializing_with_extension = false;
+ } else {
+ *r_extension = nullptr;
+ *r_extension_instance = nullptr;
+ }
+}
+
Object *ClassDB::instance(const StringName &p_class) {
ClassInfo *ti;
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func) {
+ if (!ti || ti->disabled || !ti->creation_func || (ti->native_extension && !ti->native_extension->create_instance)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -521,6 +536,11 @@ Object *ClassDB::instance(const StringName &p_class) {
return nullptr;
}
#endif
+ if (ti->native_extension) {
+ initializing_with_extension = true;
+ initializing_extension = ti->native_extension;
+ initializing_extension_instance = ti->native_extension->create_instance(ti->native_extension->create_instance_userdata);
+ }
return ti->creation_func();
}
@@ -534,7 +554,7 @@ bool ClassDB::can_instance(const StringName &p_class) {
return false;
}
#endif
- return (!ti->disabled && ti->creation_func != nullptr);
+ return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance));
}
void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
@@ -1310,6 +1330,24 @@ bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inhe
return false;
}
+void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) {
+ ClassInfo *type = classes.getptr(p_class);
+ if (!type) {
+ ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'.");
+ }
+
+ if (type->method_map.has(p_method->get_name())) {
+ // overloading not supported
+ ERR_FAIL_MSG("Method already bound '" + p_class + "::" + p_method->get_name() + "'.");
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+ type->method_order.push_back(p_method->get_name());
+#endif
+
+ type->method_map[p_method->get_name()] = p_method;
+}
+
#ifdef DEBUG_METHODS_ENABLED
MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) {
StringName mdname = method_name.name;
@@ -1545,6 +1583,26 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
return var;
}
+void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) {
+ GLOBAL_LOCK_FUNCTION;
+
+ ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name));
+ ERR_FAIL_COND_MSG(classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name));
+
+ ClassInfo *parent = classes.getptr(p_extension->parent_class_name);
+
+ ClassInfo c;
+ c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION;
+ c.native_extension = p_extension;
+ c.name = p_extension->class_name;
+ c.creation_func = parent->creation_func;
+ c.inherits = parent->name;
+ c.class_ptr = parent->class_ptr;
+ c.inherits_ptr = parent;
+
+ classes[p_extension->class_name] = c;
+}
+
RWLock ClassDB::lock;
void ClassDB::cleanup_defaults() {
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 6fd5748dbb..4355c9b0ea 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -97,6 +97,8 @@ public:
enum APIType {
API_CORE,
API_EDITOR,
+ API_EXTENSION,
+ API_EDITOR_EXTENSION,
API_NONE
};
@@ -115,6 +117,8 @@ public:
ClassInfo *inherits_ptr = nullptr;
void *class_ptr = nullptr;
+ ObjectNativeExtension *native_extension = nullptr;
+
HashMap<StringName, MethodBind *> method_map;
HashMap<StringName, int> constant_map;
HashMap<StringName, List<StringName>> enum_map;
@@ -199,6 +203,8 @@ public:
//nothing
}
+ static void register_extension_class(ObjectNativeExtension *p_extension);
+
template <class T>
static Object *_create_ptr_func() {
return T::create();
@@ -226,6 +232,7 @@ public:
static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
static bool can_instance(const StringName &p_class);
static Object *instance(const StringName &p_class);
+ static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, void **r_extension_instance);
static APIType get_api_type(const StringName &p_class);
static uint64_t get_api_hash(APIType p_api);
@@ -334,6 +341,8 @@ public:
return bind;
}
+ static void bind_method_custom(const StringName &p_class, MethodBind *p_method);
+
static void add_signal(StringName p_class, const MethodInfo &p_signal);
static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false);
static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal);
diff --git a/core/object/object.cpp b/core/object/object.cpp
index a8b2c4a939..7e1c3855c0 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -385,6 +385,15 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
}
}
+ if (_extension && _extension->set) {
+ if (_extension->set(_extension_instance, &p_name, &p_value)) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return;
+ }
+ }
+
//try built-in setgetter
{
if (ClassDB::set_property(this, p_name, p_value, r_valid)) {
@@ -451,6 +460,15 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
}
}
+ if (_extension && _extension->get) {
+ if (_extension->get(_extension_instance, &p_name, &ret)) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+ }
+ }
+
//try built-in setgetter
{
if (ClassDB::get_property(const_cast<Object *>(this), p_name, ret)) {
@@ -596,6 +614,17 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
_get_property_listv(p_list, p_reversed);
+ if (_extension && _extension->get_property_list) {
+ uint32_t pcount;
+ const ObjectNativeExtension::PInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount);
+ for (uint32_t i = 0; i < pcount; i++) {
+ p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name));
+ }
+ if (_extension->free_property_list) {
+ _extension->free_property_list(_extension_instance, pinfo);
+ }
+ }
+
if (!is_class("Script")) { // can still be set, but this is for user-friendliness
p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
}
@@ -761,6 +790,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
Variant ret;
OBJ_DEBUG_LOCK
+
if (script_instance) {
ret = script_instance->call(p_method, p_args, p_argcount, r_error);
//force jumptable
@@ -778,6 +808,8 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
}
}
+ //extension does not need this, because all methods are registered in MethodBind
+
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
if (method) {
@@ -795,6 +827,10 @@ void Object::notification(int p_notification, bool p_reversed) {
if (script_instance) {
script_instance->notification(p_notification);
}
+
+ if (_extension && _extension->notification) {
+ _extension->notification(_extension_instance, p_notification);
+ }
}
String Object::to_string() {
@@ -805,6 +841,9 @@ String Object::to_string() {
return ret;
}
}
+ if (_extension && _extension->to_string) {
+ return _extension->to_string(_extension_instance);
+ }
return "[" + get_class() + ":" + itos(get_instance_id()) + "]";
}
@@ -1751,6 +1790,8 @@ void Object::_construct_object(bool p_reference) {
_instance_id = ObjectDB::add_instance(this);
memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
+ ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance);
+
#ifdef DEBUG_ENABLED
_lock_index.init(1);
#endif
@@ -1770,6 +1811,12 @@ Object::~Object() {
}
script_instance = nullptr;
+ if (_extension && _extension->free_instance) {
+ _extension->free_instance(_extension->create_instance_userdata, _extension_instance);
+ _extension = nullptr;
+ _extension_instance = nullptr;
+ }
+
const StringName *S = nullptr;
if (_emitting) {
diff --git a/core/object/object.h b/core/object/object.h
index 448a33d3bc..137025f323 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -238,6 +238,50 @@ struct MethodInfo {
////else
//return nullptr;
+// API used to extend in GDNative and other C compatible compiled languages
+class MethodBind;
+
+struct ObjectNativeExtension {
+ ObjectNativeExtension *parent = nullptr;
+ StringName parent_class_name;
+ StringName class_name;
+ bool editor_class = false;
+ bool (*set)(void *instance, const void *name, const void *value) = nullptr;
+ bool (*get)(void *instance, const void *name, void *ret_variant) = nullptr;
+ struct PInfo {
+ uint32_t type;
+ const char *name;
+ const char *class_name;
+ uint32_t hint;
+ const char *hint_string;
+ uint32_t usage;
+ };
+ const PInfo *(*get_property_list)(void *instance, uint32_t *count) = nullptr;
+ void (*free_property_list)(void *instance, const PInfo *) = nullptr;
+
+ //call is not used, as all methods registered in ClassDB
+
+ void (*notification)(void *instance, int32_t what) = nullptr;
+ const char *(*to_string)(void *instance) = nullptr;
+
+ void (*reference)(void *instance) = nullptr;
+ void (*unreference)(void *instance) = nullptr;
+
+ _FORCE_INLINE_ bool is_class(const String &p_class) const {
+ const ObjectNativeExtension *e = this;
+ while (e) {
+ if (p_class == e->class_name.operator String()) {
+ return true;
+ }
+ e = e->parent;
+ }
+ return false;
+ }
+ void *create_instance_userdata = nullptr;
+ void *(*create_instance)(void *create_instance_userdata) = nullptr;
+ void (*free_instance)(void *create_instance_userdata, void *instance) = nullptr;
+};
+
/*
the following is an incomprehensible blob of hacks and workarounds to compensate for many of the fallencies in C++. As a plus, this macro pretty much alone defines the object model.
*/
@@ -262,9 +306,15 @@ private:
\
public: \
virtual String get_class() const override { \
+ if (_get_extension()) { \
+ return _get_extension()->class_name.operator String(); \
+ } \
return String(#m_class); \
} \
virtual const StringName *_get_class_namev() const override { \
+ if (_get_extension()) { \
+ return &_get_extension()->class_name; \
+ } \
if (!_class_name) { \
_class_name = get_class_static(); \
} \
@@ -297,7 +347,12 @@ public:
static String inherits_static() { \
return String(#m_inherits); \
} \
- virtual bool is_class(const String &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \
+ virtual bool is_class(const String &p_class) const override { \
+ if (_get_extension() && _get_extension()->is_class(p_class)) { \
+ return true; \
+ } \
+ return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); \
+ } \
virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \
\
static void get_valid_parents_static(List<String> *p_parents) { \
@@ -440,6 +495,9 @@ private:
friend bool predelete_handler(Object *);
friend void postinitialize_handler(Object *);
+ ObjectNativeExtension *_extension = nullptr;
+ void *_extension_instance = nullptr;
+
struct SignalData {
struct Slot {
int reference_count = 0;
@@ -495,6 +553,8 @@ private:
Object(bool p_reference);
protected:
+ _ALWAYS_INLINE_ const ObjectNativeExtension *_get_extension() const { return _extension; }
+ _ALWAYS_INLINE_ void *_get_extension_instance() const { return _extension_instance; }
virtual void _initialize_classv() { initialize_class(); }
virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; };
virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; };
@@ -610,13 +670,25 @@ public:
static String get_parent_class_static() { return String(); }
static String get_category_static() { return String(); }
- virtual String get_class() const { return "Object"; }
+ virtual String get_class() const {
+ if (_extension)
+ return _extension->class_name.operator String();
+ return "Object";
+ }
virtual String get_save_class() const { return get_class(); } //class stored when saving
- virtual bool is_class(const String &p_class) const { return (p_class == "Object"); }
+ virtual bool is_class(const String &p_class) const {
+ if (_extension && _extension->is_class(p_class)) {
+ return true;
+ }
+ return (p_class == "Object");
+ }
virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; }
_FORCE_INLINE_ const StringName &get_class_name() const {
+ if (_extension) {
+ return _extension->class_name;
+ }
if (!_class_ptr) {
return *_get_class_namev();
} else {
diff --git a/core/object/reference.cpp b/core/object/reference.cpp
index 22e4e8a336..086b761e95 100644
--- a/core/object/reference.cpp
+++ b/core/object/reference.cpp
@@ -62,6 +62,9 @@ bool Reference::reference() {
if (get_script_instance()) {
get_script_instance()->refcount_incremented();
}
+ if (_get_extension() && _get_extension()->reference) {
+ _get_extension()->reference(_get_extension_instance());
+ }
if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
if (_script_instance_bindings[i]) {
@@ -83,6 +86,9 @@ bool Reference::unreference() {
bool script_ret = get_script_instance()->refcount_decremented();
die = die && script_ret;
}
+ if (_get_extension() && _get_extension()->unreference) {
+ _get_extension()->unreference(_get_extension_instance());
+ }
if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
if (_script_instance_bindings[i]) {
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 42fb0a0caf..aa91c6cbf7 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -585,14 +585,6 @@ 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),
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 9ed3c7e80f..a22e91870e 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -41,21 +41,6 @@ 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 {
MAX_LANGUAGES = 16
@@ -174,17 +159,7 @@ 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;
+ virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0;
Script() {}
};
@@ -225,17 +200,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
- virtual 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 const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0;
virtual ScriptLanguage *get_language() = 0;
virtual ~ScriptInstance();
@@ -447,17 +412,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr);
- virtual 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; }
+ virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const { return Vector<MultiplayerAPI::RPCConfig>(); }
PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
~PlaceHolderScriptInstance();
diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp
index 3d04e4e619..d21c0bd9a2 100644
--- a/core/os/file_access.cpp
+++ b/core/os/file_access.cpp
@@ -551,6 +551,7 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_
}
void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND(!p_src && p_length > 0);
for (uint64_t i = 0; i < p_length; i++) {
store_8(p_src[i]);
}