diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index f5dbacd62d0..e7ddbc690f1 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1908,18 +1908,18 @@ bool _File::file_exists(const String &p_name) const { return FileAccess::exists(p_name); } -void _File::store_var(const Variant &p_var) { +void _File::store_var(const Variant &p_var, bool p_full_objects) { ERR_FAIL_COND(!f); int len; - Error err = encode_variant(p_var, NULL, len); + Error err = encode_variant(p_var, NULL, len, p_full_objects); ERR_FAIL_COND(err != OK); PoolVector buff; buff.resize(len); PoolVector::Write w = buff.write(); - err = encode_variant(p_var, &w[0], len); + err = encode_variant(p_var, &w[0], len, p_full_objects); ERR_FAIL_COND(err != OK); w = PoolVector::Write(); @@ -1927,7 +1927,7 @@ void _File::store_var(const Variant &p_var) { store_buffer(buff); } -Variant _File::get_var() const { +Variant _File::get_var(bool p_allow_objects) const { ERR_FAIL_COND_V(!f, Variant()); uint32_t len = get_32(); @@ -1937,7 +1937,7 @@ Variant _File::get_var() const { PoolVector::Read r = buff.read(); Variant v; - Error err = decode_variant(v, &r[0], len); + Error err = decode_variant(v, &r[0], len, NULL, p_allow_objects); ERR_FAIL_COND_V(err != OK, Variant()); return v; @@ -1980,7 +1980,7 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("get_endian_swap"), &_File::get_endian_swap); ClassDB::bind_method(D_METHOD("set_endian_swap", "enable"), &_File::set_endian_swap); ClassDB::bind_method(D_METHOD("get_error"), &_File::get_error); - ClassDB::bind_method(D_METHOD("get_var"), &_File::get_var); + ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &_File::get_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("store_8", "value"), &_File::store_8); ClassDB::bind_method(D_METHOD("store_16", "value"), &_File::store_16); @@ -1993,7 +1993,7 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("store_line", "line"), &_File::store_line); ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &_File::store_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("store_string", "string"), &_File::store_string); - ClassDB::bind_method(D_METHOD("store_var", "value"), &_File::store_var); + ClassDB::bind_method(D_METHOD("store_var", "value", "full_objects"), &_File::store_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("store_pascal_string", "string"), &_File::store_pascal_string); ClassDB::bind_method(D_METHOD("get_pascal_string"), &_File::get_pascal_string); @@ -2223,17 +2223,17 @@ _Marshalls *_Marshalls::get_singleton() { return singleton; } -String _Marshalls::variant_to_base64(const Variant &p_var) { +String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) { int len; - Error err = encode_variant(p_var, NULL, len); + Error err = encode_variant(p_var, NULL, len, p_full_objects); ERR_FAIL_COND_V(err != OK, ""); PoolVector buff; buff.resize(len); PoolVector::Write w = buff.write(); - err = encode_variant(p_var, &w[0], len); + err = encode_variant(p_var, &w[0], len, p_full_objects); ERR_FAIL_COND_V(err != OK, ""); int b64len = len / 3 * 4 + 4 + 1; @@ -2249,7 +2249,7 @@ String _Marshalls::variant_to_base64(const Variant &p_var) { return ret; }; -Variant _Marshalls::base64_to_variant(const String &p_str) { +Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) { int strlen = p_str.length(); CharString cstr = p_str.ascii(); @@ -2261,7 +2261,7 @@ Variant _Marshalls::base64_to_variant(const String &p_str) { int len = base64_decode((char *)(&w[0]), (char *)cstr.get_data(), strlen); Variant v; - Error err = decode_variant(v, &w[0], len); + Error err = decode_variant(v, &w[0], len, NULL, p_allow_objects); ERR_FAIL_COND_V(err != OK, Variant()); return v; @@ -2340,8 +2340,8 @@ String _Marshalls::base64_to_utf8(const String &p_str) { void _Marshalls::_bind_methods() { - ClassDB::bind_method(D_METHOD("variant_to_base64", "variant"), &_Marshalls::variant_to_base64); - ClassDB::bind_method(D_METHOD("base64_to_variant", "base64_str"), &_Marshalls::base64_to_variant); + ClassDB::bind_method(D_METHOD("variant_to_base64", "variant", "full_objects"), &_Marshalls::variant_to_base64, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("base64_to_variant", "base64_str", "allow_objects"), &_Marshalls::base64_to_variant, DEFVAL(false)); ClassDB::bind_method(D_METHOD("raw_to_base64", "array"), &_Marshalls::raw_to_base64); ClassDB::bind_method(D_METHOD("base64_to_raw", "base64_str"), &_Marshalls::base64_to_raw); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 803743bc935..1c26d9b1441 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -463,7 +463,7 @@ public: double get_double() const; real_t get_real() const; - Variant get_var() const; + Variant get_var(bool p_allow_objects = false) const; PoolVector get_buffer(int p_length) const; ///< get an array of bytes String get_line() const; @@ -500,7 +500,7 @@ public: void store_buffer(const PoolVector &p_buffer); ///< store an array of bytes - void store_var(const Variant &p_var); + void store_var(const Variant &p_var, bool p_full_objects = false); bool file_exists(const String &p_name) const; ///< return true if a file exists @@ -569,8 +569,8 @@ protected: public: static _Marshalls *get_singleton(); - String variant_to_base64(const Variant &p_var); - Variant base64_to_variant(const String &p_str); + String variant_to_base64(const Variant &p_var, bool p_full_objects = false); + Variant base64_to_variant(const String &p_str, bool p_allow_objects = false); String raw_to_base64(const PoolVector &p_arr); PoolVector base64_to_raw(const String &p_str); diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index a9fc4e6b841..99aa1cca80c 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -299,7 +299,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ ERR_FAIL_COND(p_offset >= p_packet_len); int vlen; - Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen, network_peer->is_object_decoding_allowed()); + 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()); ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument."); ERR_FAIL_COND(err != OK); @@ -335,7 +335,7 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from)); Variant value; - Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, network_peer->is_object_decoding_allowed()); + Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed()); ERR_EXPLAIN("Invalid packet received. Unable to decode RSET value."); ERR_FAIL_COND(err != OK); @@ -526,11 +526,11 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p if (p_set) { // Set argument. - Error err = encode_variant(*p_arg[0], NULL, len, network_peer->is_object_decoding_allowed()); + Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); ERR_EXPLAIN("Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, network_peer->is_object_decoding_allowed()); + encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); ofs += len; } else { @@ -539,11 +539,11 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p 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, network_peer->is_object_decoding_allowed()); + Error err = encode_variant(*p_arg[i], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); ERR_EXPLAIN("Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, network_peer->is_object_decoding_allowed()); + encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); ofs += len; } } @@ -818,6 +818,16 @@ Vector MultiplayerAPI::get_network_connected_peers() const { return ret; } +void MultiplayerAPI::set_allow_object_decoding(bool p_enable) { + + allow_object_decoding = p_enable; +} + +bool MultiplayerAPI::is_object_decoding_allowed() const { + + return allow_object_decoding; +} + void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); @@ -838,6 +848,10 @@ void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers); ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections); ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); + ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); + ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer"); @@ -859,7 +873,8 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } -MultiplayerAPI::MultiplayerAPI() { +MultiplayerAPI::MultiplayerAPI() : + allow_object_decoding(false) { rpc_sender_id = 0; root_node = NULL; clear(); diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index a9cf77aaba0..779dd043bd6 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -63,6 +63,7 @@ private: int last_send_cache_id; Vector packet_cache; Node *root_node; + bool allow_object_decoding; protected: static void _bind_methods(); @@ -126,6 +127,9 @@ public: void set_refuse_new_network_connections(bool p_refuse); bool is_refusing_new_network_connections() const; + void set_allow_object_decoding(bool p_enable); + bool is_object_decoding_allowed() const; + MultiplayerAPI(); ~MultiplayerAPI(); }; diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 609a60c6ff4..c77c81f9e2a 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -79,7 +79,7 @@ Error PacketPeer::put_packet_buffer(const PoolVector &p_buffer) { return put_packet(&r[0], len); } -Error PacketPeer::get_var(Variant &r_variant) { +Error PacketPeer::get_var(Variant &r_variant, bool p_allow_objects) { const uint8_t *buffer; int buffer_size; @@ -87,13 +87,13 @@ Error PacketPeer::get_var(Variant &r_variant) { if (err) return err; - return decode_variant(r_variant, buffer, buffer_size, NULL, allow_object_decoding); + return decode_variant(r_variant, buffer, buffer_size, NULL, p_allow_objects || allow_object_decoding); } -Error PacketPeer::put_var(const Variant &p_packet) { +Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { int len; - Error err = encode_variant(p_packet, NULL, len, allow_object_decoding); // compute len first + Error err = encode_variant(p_packet, NULL, len, p_full_objects || allow_object_decoding); // compute len first if (err) return err; @@ -102,15 +102,15 @@ Error PacketPeer::put_var(const Variant &p_packet) { uint8_t *buf = (uint8_t *)alloca(len); ERR_FAIL_COND_V(!buf, ERR_OUT_OF_MEMORY); - err = encode_variant(p_packet, buf, len, allow_object_decoding); + err = encode_variant(p_packet, buf, len, p_full_objects || allow_object_decoding); ERR_FAIL_COND_V(err, err); return put_packet(buf, len); } -Variant PacketPeer::_bnd_get_var() { +Variant PacketPeer::_bnd_get_var(bool p_allow_objects) { Variant var; - get_var(var); + get_var(var, p_allow_objects); return var; }; @@ -132,8 +132,8 @@ Error PacketPeer::_get_packet_error() const { void PacketPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_var"), &PacketPeer::_bnd_get_var); - ClassDB::bind_method(D_METHOD("put_var", "var"), &PacketPeer::put_var); + ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet); ClassDB::bind_method(D_METHOD("put_packet", "buffer"), &PacketPeer::_put_packet); ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 48c50eb76be..6475e4fed94 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -39,8 +39,7 @@ class PacketPeer : public Reference { GDCLASS(PacketPeer, Reference); - Variant _bnd_get_var(); - void _bnd_put_var(const Variant &p_var); + Variant _bnd_get_var(bool p_allow_objects = false); static void _bind_methods(); @@ -64,8 +63,8 @@ public: virtual Error get_packet_buffer(PoolVector &r_buffer); virtual Error put_packet_buffer(const PoolVector &p_buffer); - virtual Error get_var(Variant &r_variant); - virtual Error put_var(const Variant &p_packet); + virtual Error get_var(Variant &r_variant, bool p_allow_objects = false); + virtual Error put_var(const Variant &p_packet, bool p_full_objects = false); void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 3042c0f60a8..6ad24a5f3a7 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -221,14 +221,14 @@ void StreamPeer::put_utf8_string(const String &p_string) { put_u32(cs.length()); put_data((const uint8_t *)cs.get_data(), cs.length()); } -void StreamPeer::put_var(const Variant &p_variant) { +void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { int len = 0; Vector buf; - encode_variant(p_variant, NULL, len); + encode_variant(p_variant, NULL, len, p_full_objects); buf.resize(len); put_32(len); - encode_variant(p_variant, buf.ptrw(), len); + encode_variant(p_variant, buf.ptrw(), len, p_full_objects); put_data(buf.ptr(), buf.size()); } @@ -359,7 +359,7 @@ String StreamPeer::get_utf8_string(int p_bytes) { ret.parse_utf8((const char *)buf.ptr(), buf.size()); return ret; } -Variant StreamPeer::get_var() { +Variant StreamPeer::get_var(bool p_allow_objects) { int len = get_32(); Vector var; @@ -369,7 +369,7 @@ Variant StreamPeer::get_var() { ERR_FAIL_COND_V(err != OK, Variant()); Variant ret; - decode_variant(ret, var.ptr(), len); + decode_variant(ret, var.ptr(), len, NULL, p_allow_objects); return ret; } @@ -398,7 +398,7 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("put_double", "value"), &StreamPeer::put_double); ClassDB::bind_method(D_METHOD("put_string", "value"), &StreamPeer::put_string); ClassDB::bind_method(D_METHOD("put_utf8_string", "value"), &StreamPeer::put_utf8_string); - ClassDB::bind_method(D_METHOD("put_var", "value"), &StreamPeer::put_var); + ClassDB::bind_method(D_METHOD("put_var", "value", "full_objects"), &StreamPeer::put_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_8"), &StreamPeer::get_8); ClassDB::bind_method(D_METHOD("get_u8"), &StreamPeer::get_u8); @@ -412,7 +412,7 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_double"), &StreamPeer::get_double); ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_utf8_string", "bytes"), &StreamPeer::get_utf8_string, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_var"), &StreamPeer::get_var); + ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &StreamPeer::get_var, DEFVAL(false)); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian_enabled"); } diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 059ccd016c6..65e70995ad2 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -73,7 +73,7 @@ public: void put_double(double p_val); void put_string(const String &p_string); void put_utf8_string(const String &p_string); - void put_var(const Variant &p_variant); + void put_var(const Variant &p_variant, bool p_full_objects = false); uint8_t get_u8(); int8_t get_8(); @@ -87,7 +87,7 @@ public: double get_double(); String get_string(int p_bytes = -1); String get_utf8_string(int p_bytes = -1); - Variant get_var(); + Variant get_var(bool p_allow_objects = false); StreamPeer() { big_endian = false; } }; diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 99251d80e30..708054e4abb 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -164,10 +164,10 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) { case TEXT_PRINTRAW: case VAR_TO_STR: case STR_TO_VAR: - case VAR_TO_BYTES: - case BYTES_TO_VAR: case TYPE_EXISTS: return 1; + case VAR_TO_BYTES: + case BYTES_TO_VAR: case MATH_ATAN2: case MATH_FMOD: case MATH_FPOSMOD: @@ -696,8 +696,9 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant case VAR_TO_BYTES: { PoolByteArray barr; + bool full_objects = *p_inputs[1]; int len; - Error err = encode_variant(*p_inputs[0], NULL, len); + Error err = encode_variant(*p_inputs[0], NULL, len, full_objects); if (err) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; @@ -709,7 +710,7 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(*p_inputs[0], w.ptr(), len); + encode_variant(*p_inputs[0], w.ptr(), len, full_objects); } *r_return = barr; } break; @@ -724,10 +725,11 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant } PoolByteArray varr = *p_inputs[0]; + bool allow_objects = *p_inputs[1]; Variant ret; { PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, allow_objects); if (err != OK) { r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index 6c17f42b136..99bed829c10 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -114,7 +114,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b } else { Variant v; - Error rerr = decode_variant(v, p_buf + p_ofs, datalen - p_ofs, NULL); + Error rerr = decode_variant(v, p_buf + p_ofs, datalen - p_ofs, NULL, false); if (rerr != OK) { @@ -249,9 +249,9 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector &tmpd uint32_t pos = tmpdata.size(); int len; - encode_variant(p_data, NULL, len); + encode_variant(p_data, NULL, len, false); tmpdata.resize(tmpdata.size() + len); - encode_variant(p_data, &tmpdata.write[pos], len); + encode_variant(p_data, &tmpdata.write[pos], len, false); return pos; } break; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 02c7c9e0295..8c9a3dbcd47 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -501,7 +501,7 @@ Error ProjectSettings::_load_settings_binary(const String p_path) { d.resize(vlen); f->get_buffer(d.ptrw(), vlen); Variant value; - err = decode_variant(value, d.ptr(), d.size()); + err = decode_variant(value, d.ptr(), d.size(), NULL, false); ERR_EXPLAIN("Error decoding property: " + key); ERR_CONTINUE(err != OK); set(key, value); @@ -656,7 +656,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Mapstore_string(key); int len; - err = encode_variant(p_custom_features, NULL, len); + err = encode_variant(p_custom_features, NULL, len, false); if (err != OK) { memdelete(file); ERR_FAIL_V(err); @@ -665,7 +665,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map buff; buff.resize(len); - err = encode_variant(p_custom_features, buff.ptrw(), len); + err = encode_variant(p_custom_features, buff.ptrw(), len, false); if (err != OK) { memdelete(file); ERR_FAIL_V(err); @@ -694,7 +694,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Mapstore_string(key); int len; - err = encode_variant(value, NULL, len); + err = encode_variant(value, NULL, len, false); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); @@ -702,7 +702,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map buff; buff.resize(len); - err = encode_variant(value, buff.ptrw(), len); + err = encode_variant(value, buff.ptrw(), len, false); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index 0a430fea4d1..e5d4256617b 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -136,8 +136,11 @@ + + - Decodes a byte array back to a value. + Decodes a byte array back to a value. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). @@ -1112,8 +1115,10 @@ + + - Encodes a variable value to a byte array. + Encodes a variable value to a byte array. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code). diff --git a/doc/classes/File.xml b/doc/classes/File.xml index c9a8f18116d..e8aa1caabd1 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -204,8 +204,11 @@ + + - Returns the next [Variant] value from the file. + Returns the next [Variant] value from the file. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). @@ -398,8 +401,10 @@ + + - Stores any Variant value in the file. + Stores any Variant value in the file. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code). diff --git a/doc/classes/Marshalls.xml b/doc/classes/Marshalls.xml index 687f81eec78..c041cfb00a4 100644 --- a/doc/classes/Marshalls.xml +++ b/doc/classes/Marshalls.xml @@ -34,8 +34,11 @@ + + - Return [Variant] of a given base64 encoded String. + Return [Variant] of a given base64 encoded String. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). @@ -61,8 +64,10 @@ + + - Return base64 encoded String of a given [Variant]. + Return base64 encoded String of a given [Variant]. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code). diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index f3e26a3bcb3..59e0168f2c2 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -89,6 +89,10 @@ + + If [code]true[/code] (or if the [member network_peer] [member PacketPeer.allow_object_decoding] the MultiplayerAPI will allow encoding and decoding of object during RPCs/RSETs. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). + The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the MultiplayerAPI will become a network server (check with [method is_network_server]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to puppet. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to MultiplayerAPI's signals. diff --git a/doc/classes/PacketPeer.xml b/doc/classes/PacketPeer.xml index 41f5eaf555a..44d942e344c 100644 --- a/doc/classes/PacketPeer.xml +++ b/doc/classes/PacketPeer.xml @@ -35,8 +35,11 @@ + + - Get a Variant. + Get a Variant. When [code]allow_objects[/code] (or [member allow_object_decoding]) is [code]true[/code] decoding objects is allowed. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). @@ -53,13 +56,16 @@ + + - Send a Variant as a packet. + Send a Variant as a packet. When [code]full_objects[/code] (or [member allow_object_decoding]) is [code]true[/code] encoding objects is allowed (and can potentially include code). + Deprecated. Use [code]get_var[/code] and [code]put_var[/code] parameters instead. If [code]true[/code] the PacketPeer will allow encoding and decoding of object via [method get_var] and [method put_var]. [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index 74ac8a79c02..87a69be0589 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -127,8 +127,11 @@ + + - Get a Variant from the stream. + Get a Variant from the stream. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). @@ -262,8 +265,10 @@ + + - Put a Variant into the stream. + Put a Variant into the stream. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code). diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 44d44462ca0..46c9efd54f2 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -768,11 +768,30 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ (void)VariantParser::parse(&ss, r_ret, errs, line); } break; case VAR_TO_BYTES: { - VALIDATE_ARG_COUNT(1); + bool full_objects = false; + if (p_arg_count < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_ret = Variant(); + return; + } else if (p_arg_count > 2) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 2; + r_ret = Variant(); + } else if (p_arg_count == 2) { + if (p_args[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + r_ret = Variant(); + return; + } + full_objects = *p_args[1]; + } PoolByteArray barr; int len; - Error err = encode_variant(*p_args[0], NULL, len); + Error err = encode_variant(*p_args[0], NULL, len, full_objects); if (err) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; @@ -784,15 +803,35 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(*p_args[0], w.ptr(), len); + encode_variant(*p_args[0], w.ptr(), len, full_objects); } r_ret = barr; } break; case BYTES_TO_VAR: { - VALIDATE_ARG_COUNT(1); + bool allow_objects = false; + if (p_arg_count < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_ret = Variant(); + return; + } else if (p_arg_count > 2) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 2; + r_ret = Variant(); + } else if (p_arg_count == 2) { + if (p_args[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + r_ret = Variant(); + return; + } + allow_objects = *p_args[1]; + } + if (p_args[0]->get_type() != Variant::POOL_BYTE_ARRAY) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; + r_error.argument = 1; r_error.expected = Variant::POOL_BYTE_ARRAY; r_ret = Variant(); return; @@ -802,7 +841,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ Variant ret; { PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, allow_objects); if (err != OK) { r_ret = RTR("Not enough bytes for decoding bytes, or invalid format."); r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; @@ -1805,13 +1844,15 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case VAR_TO_BYTES: { - MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); + MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects")); + mi.default_arguments.push_back(false); mi.return_val.type = Variant::POOL_BYTE_ARRAY; return mi; } break; case BYTES_TO_VAR: { - MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes")); + MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects")); + mi.default_arguments.push_back(false); mi.return_val.type = Variant::NIL; mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 8b22d6f0858..8962e3bb340 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -1199,7 +1199,8 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector &p_buffer) Variant v; int len; - Error err = decode_variant(v, b, total_len, &len); + // An object cannot be constant, never decode objects + Error err = decode_variant(v, b, total_len, &len, false); if (err) return err; b += len; @@ -1367,11 +1368,12 @@ Vector GDScriptTokenizerBuffer::parse_code_string(const String &p_code) for (Map::Element *E = rev_constant_map.front(); E; E = E->next()) { int len; - Error err = encode_variant(E->get(), NULL, len); + // Objects cannot be constant, never encode objects + Error err = encode_variant(E->get(), NULL, len, false); ERR_FAIL_COND_V(err != OK, Vector()); int pos = buf.size(); buf.resize(pos + len); - encode_variant(E->get(), &buf.write[pos], len); + encode_variant(E->get(), &buf.write[pos], len, false); } for (Map::Element *E = rev_line_map.front(); E; E = E->next()) { diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs index 3afaf5d08b8..aaae72fbe3a 100644 --- a/modules/mono/glue/Managed/Files/GD.cs +++ b/modules/mono/glue/Managed/Files/GD.cs @@ -13,9 +13,9 @@ namespace Godot { public static partial class GD { - public static object Bytes2Var(byte[] bytes) + public static object Bytes2Var(byte[] bytes, bool allow_objects = false) { - return godot_icall_GD_bytes2var(bytes); + return godot_icall_GD_bytes2var(bytes, allow_objects); } public static object Convert(object what, int type) @@ -186,9 +186,9 @@ namespace Godot return godot_icall_GD_type_exists(type); } - public static byte[] Var2Bytes(object var) + public static byte[] Var2Bytes(object var, bool full_objects = false) { - return godot_icall_GD_var2bytes(var); + return godot_icall_GD_var2bytes(var, full_objects); } public static string Var2Str(object var) @@ -197,7 +197,7 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_bytes2var(byte[] bytes); + internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static object godot_icall_GD_convert(object what, int type); @@ -251,7 +251,7 @@ namespace Godot internal extern static bool godot_icall_GD_type_exists(string type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_GD_var2bytes(object what); + internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static string godot_icall_GD_var2str(object var); diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index 5edf49d2bfb..552037b35cc 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -41,11 +41,11 @@ #include "../mono_gd/gd_mono_utils.h" -MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes) { +MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) { Variant ret; PoolByteArray varr = GDMonoMarshal::mono_array_to_PoolByteArray(p_bytes); PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, p_allow_objects); if (err != OK) { ret = RTR("Not enough bytes for decoding bytes, or invalid format."); } @@ -187,19 +187,19 @@ void godot_icall_GD_pushwarning(MonoString *p_str) { WARN_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str)); } -MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var) { +MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) { Variant var = GDMonoMarshal::mono_object_to_variant(p_var); PoolByteArray barr; int len; - Error err = encode_variant(var, NULL, len); + Error err = encode_variant(var, NULL, len, p_full_objects); ERR_EXPLAIN("Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); ERR_FAIL_COND_V(err != OK, NULL); barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(var, w.ptr(), len); + encode_variant(var, w.ptr(), len, p_full_objects); } return GDMonoMarshal::PoolByteArray_to_mono_array(barr); diff --git a/modules/mono/glue/gd_glue.h b/modules/mono/glue/gd_glue.h index ba75d85343c..2bce461aac8 100644 --- a/modules/mono/glue/gd_glue.h +++ b/modules/mono/glue/gd_glue.h @@ -35,7 +35,7 @@ #include "../mono_gd/gd_mono_marshal.h" -MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes); +MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects); MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type); @@ -71,7 +71,7 @@ MonoObject *godot_icall_GD_str2var(MonoString *p_str); bool godot_icall_GD_type_exists(MonoString *p_type); -MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var); +MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects); MonoString *godot_icall_GD_var2str(MonoObject *p_var); diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 9f2d1a49c0b..0d379205e48 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -183,10 +183,10 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case TEXT_PRINTRAW: case VAR_TO_STR: case STR_TO_VAR: - case VAR_TO_BYTES: - case BYTES_TO_VAR: case TYPE_EXISTS: return 1; + case VAR_TO_BYTES: + case BYTES_TO_VAR: case MATH_ATAN2: case MATH_FMOD: case MATH_FPOSMOD: @@ -491,12 +491,18 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const return PropertyInfo(Variant::STRING, "string"); } break; case VAR_TO_BYTES: { - return PropertyInfo(Variant::NIL, "var"); + if (p_idx == 0) + return PropertyInfo(Variant::NIL, "var"); + else + return PropertyInfo(Variant::BOOL, "full_objects"); } break; case BYTES_TO_VAR: { - return PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes"); + if (p_idx == 0) + return PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes"); + else + return PropertyInfo(Variant::BOOL, "allow_objects"); } break; case COLORN: { @@ -655,11 +661,15 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons } break; case VAR_TO_BYTES: { - t = Variant::POOL_BYTE_ARRAY; + if (p_idx == 0) + t = Variant::POOL_BYTE_ARRAY; + else + t = Variant::BOOL; } break; case BYTES_TO_VAR: { - + if (p_idx == 1) + t = Variant::BOOL; } break; case COLORN: { t = Variant::COLOR; @@ -1192,9 +1202,16 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in } break; case VisualScriptBuiltinFunc::VAR_TO_BYTES: { + if (p_inputs[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + return; + } PoolByteArray barr; int len; - Error err = encode_variant(*p_inputs[0], NULL, len); + bool full_objects = *p_inputs[1]; + Error err = encode_variant(*p_inputs[0], NULL, len, full_objects); if (err) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; @@ -1206,7 +1223,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(*p_inputs[0], w.ptr(), len); + encode_variant(*p_inputs[0], w.ptr(), len, full_objects); } *r_return = barr; } break; @@ -1216,15 +1233,21 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::POOL_BYTE_ARRAY; - + return; + } + if (p_inputs[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; return; } PoolByteArray varr = *p_inputs[0]; + bool allow_objects = *p_inputs[1]; Variant ret; { PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, allow_objects); if (err != OK) { r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;