diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/io/file_access_encrypted.cpp | 1 | ||||
-rw-r--r-- | core/io/file_access_memory.cpp | 1 | ||||
-rw-r--r-- | core/io/multiplayer_api.cpp | 314 | ||||
-rw-r--r-- | core/io/multiplayer_api.h | 45 | ||||
-rw-r--r-- | core/object/class_db.cpp | 62 | ||||
-rw-r--r-- | core/object/class_db.h | 9 | ||||
-rw-r--r-- | core/object/object.cpp | 47 | ||||
-rw-r--r-- | core/object/object.h | 78 | ||||
-rw-r--r-- | core/object/reference.cpp | 6 | ||||
-rw-r--r-- | core/object/script_language.cpp | 8 | ||||
-rw-r--r-- | core/object/script_language.h | 51 | ||||
-rw-r--r-- | core/os/file_access.cpp | 1 |
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]); } |