summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub2
-rw-r--r--core/debugger/remote_debugger_peer.cpp3
-rw-r--r--core/extension/gdextension.cpp10
-rw-r--r--core/extension/gdextension_interface.cpp2
-rw-r--r--core/extension/gdextension_interface.h2
-rw-r--r--core/extension/gdextension_manager.cpp93
-rw-r--r--core/extension/gdextension_manager.h3
-rw-r--r--core/io/file_access.cpp111
-rw-r--r--core/io/file_access.h8
-rw-r--r--core/io/file_access_compressed.cpp39
-rw-r--r--core/io/file_access_compressed.h3
-rw-r--r--core/io/file_access_encrypted.cpp46
-rw-r--r--core/io/file_access_encrypted.h2
-rw-r--r--core/io/file_access_memory.cpp18
-rw-r--r--core/io/file_access_memory.h3
-rw-r--r--core/io/file_access_pack.cpp15
-rw-r--r--core/io/file_access_pack.h4
-rw-r--r--core/io/file_access_zip.cpp8
-rw-r--r--core/io/file_access_zip.h3
-rw-r--r--core/io/image.cpp2
-rw-r--r--core/io/json.cpp748
-rw-r--r--core/io/json.h3
-rw-r--r--core/io/packed_data_container.h2
-rw-r--r--core/io/plist.cpp4
-rw-r--r--core/io/resource.cpp99
-rw-r--r--core/io/resource_importer.cpp23
-rw-r--r--core/io/resource_importer.h7
-rw-r--r--core/io/resource_loader.cpp64
-rw-r--r--core/math/math_funcs.h3
-rw-r--r--core/object/class_db.cpp26
-rw-r--r--core/object/class_db.h1
-rw-r--r--core/object/object.cpp37
-rw-r--r--core/object/object.h7
-rw-r--r--core/object/worker_thread_pool.cpp39
-rw-r--r--core/os/os.h6
-rw-r--r--core/string/string_name.cpp3
-rw-r--r--core/string/translation_server.cpp5
-rw-r--r--core/variant/callable.cpp2
-rw-r--r--core/variant/dictionary.cpp17
-rw-r--r--core/variant/variant.cpp2
-rw-r--r--core/variant/variant_construct.h9
-rw-r--r--core/variant/variant_setget.cpp4
42 files changed, 1090 insertions, 398 deletions
diff --git a/core/SCsub b/core/SCsub
index 1bd4eae16c..c8267ae960 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -140,7 +140,7 @@ if env["builtin_zstd"]:
"decompress/zstd_decompress_block.c",
"decompress/zstd_decompress.c",
]
- if env["platform"] in ["android", "ios", "linuxbsd", "macos"]:
+ if env["platform"] in ["android", "ios", "linuxbsd", "macos"] and env["arch"] == "x86_64":
# Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h
thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S")
thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources]
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index 21a9014626..9dca47a0b4 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -144,9 +144,8 @@ void RemoteDebuggerPeerTCP::_read_in() {
Error err = decode_variant(var, buf, in_pos, &read);
ERR_CONTINUE(read != in_pos || err != OK);
ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
- mutex.lock();
+ MutexLock lock(mutex);
in_queue.push_back(var);
- mutex.unlock();
}
}
}
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index c9e609cddc..d4b50facb2 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -675,15 +675,13 @@ GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const String
}
Error GDExtension::open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
- ERR_FAIL_NULL_V_MSG(p_loader, FAILED, "Can't open GDExtension without a loader.");
+ ERR_FAIL_COND_V_MSG(p_loader.is_null(), FAILED, "Can't open GDExtension without a loader.");
loader = p_loader;
- String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
+ Error err = loader->open_library(p_path);
- Error err = loader->open_library(abs_path);
-
- ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
+ ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + p_path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + p_path);
err = loader->initialize(&gdextension_get_proc_address, this, &initialization);
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index a5a0fc906a..0ebe86d0a7 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1299,7 +1299,7 @@ static void gdextension_object_call_script_method(GDExtensionObjectPtr p_object,
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
- Callable::CallError error;
+ Callable::CallError error; // TODO: Check `error`?
memnew_placement(r_return, Variant);
*(Variant *)r_return = o->callp(method, args, p_argument_count, error);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index cac76d39bd..9057e04bf3 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -268,7 +268,7 @@ typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
-typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_userdata, bool p_notify_postinitialize);
+typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_userdata, GDExtensionBool p_notify_postinitialize);
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index eeae6b1996..01efe0d96e 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -32,14 +32,18 @@
#include "core/extension/gdextension_compat_hashes.h"
#include "core/extension/gdextension_library_loader.h"
+#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/object/script_language.h"
-GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension) {
+GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) {
if (level >= 0) { // Already initialized up to some level.
- int32_t minimum_level = p_extension->get_minimum_library_initialization_level();
- if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
- return LOAD_STATUS_NEEDS_RESTART;
+ int32_t minimum_level = 0;
+ if (!p_first_load) {
+ minimum_level = p_extension->get_minimum_library_initialization_level();
+ if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
+ return LOAD_STATUS_NEEDS_RESTART;
+ }
}
// Initialize up to current level.
for (int32_t i = minimum_level; i <= level; i++) {
@@ -51,10 +55,20 @@ GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(cons
gdextension_class_icon_paths[kv.key] = kv.value;
}
+#ifdef TOOLS_ENABLED
+ // Signals that a new extension is loaded so GDScript can register new class names.
+ emit_signal("extension_loaded", p_extension);
+#endif
+
return LOAD_STATUS_OK;
}
GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) {
+#ifdef TOOLS_ENABLED
+ // Signals that a new extension is unloading so GDScript can unregister class names.
+ emit_signal("extension_unloading", p_extension);
+#endif
+
if (level >= 0) { // Already initialized up to some level.
// Deinitialize down from current level.
for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) {
@@ -89,7 +103,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(co
return LOAD_STATUS_FAILED;
}
- LoadStatus status = _load_extension_internal(extension);
+ LoadStatus status = _load_extension_internal(extension, true);
if (status != LOAD_STATUS_OK) {
return status;
}
@@ -135,7 +149,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
return LOAD_STATUS_FAILED;
}
- status = _load_extension_internal(extension);
+ status = _load_extension_internal(extension, false);
if (status != LOAD_STATUS_OK) {
return status;
}
@@ -274,6 +288,71 @@ void GDExtensionManager::reload_extensions() {
#endif
}
+bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_extensions) {
+ Vector<String> extensions_added;
+ Vector<String> extensions_removed;
+
+ for (const String &E : p_extensions) {
+ if (!is_extension_loaded(E)) {
+ extensions_added.push_back(E);
+ }
+ }
+
+ Vector<String> loaded_extensions = get_loaded_extensions();
+ for (const String &loaded_extension : loaded_extensions) {
+ if (!p_extensions.has(loaded_extension)) {
+ // The extension may not have a .gdextension file.
+ if (!FileAccess::exists(loaded_extension)) {
+ extensions_removed.push_back(loaded_extension);
+ }
+ }
+ }
+
+ String extension_list_config_file = GDExtension::get_extension_list_config_file();
+ if (p_extensions.size()) {
+ if (extensions_added.size() || extensions_removed.size()) {
+ // Extensions were added or removed.
+ Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE);
+ for (const String &E : p_extensions) {
+ f->store_line(E);
+ }
+ }
+ } else {
+ if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) {
+ // Extensions were removed.
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ da->remove(extension_list_config_file);
+ }
+ }
+
+ bool needs_restart = false;
+ for (const String &extension : extensions_added) {
+ GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extension);
+ if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+ needs_restart = true;
+ }
+ }
+
+ for (const String &extension : extensions_removed) {
+ GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extension);
+ if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+ needs_restart = true;
+ }
+ }
+
+#ifdef TOOLS_ENABLED
+ if (extensions_added.size() || extensions_removed.size()) {
+ // Emitting extensions_reloaded so EditorNode can reload Inspector and regenerate documentation.
+ emit_signal("extensions_reloaded");
+
+ // Reload all scripts to clear out old references.
+ callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred();
+ }
+#endif
+
+ return needs_restart;
+}
+
GDExtensionManager *GDExtensionManager::get_singleton() {
return singleton;
}
@@ -294,6 +373,8 @@ void GDExtensionManager::_bind_methods() {
BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
ADD_SIGNAL(MethodInfo("extensions_reloaded"));
+ ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
+ ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
}
GDExtensionManager *GDExtensionManager::singleton = nullptr;
diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h
index b488189604..39a600474c 100644
--- a/core/extension/gdextension_manager.h
+++ b/core/extension/gdextension_manager.h
@@ -54,7 +54,7 @@ public:
};
private:
- LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension);
+ LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load);
LoadStatus _unload_extension_internal(const Ref<GDExtension> &p_extension);
#ifdef TOOLS_ENABLED
@@ -85,6 +85,7 @@ public:
void load_extensions();
void reload_extensions();
+ bool ensure_extensions_loaded(const HashSet<String> &p_extensions);
GDExtensionManager();
~GDExtensionManager();
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index c857d54925..d919243e6b 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -223,59 +223,44 @@ String FileAccess::fix_path(const String &p_path) const {
}
/* these are all implemented for ease of porting, then can later be optimized */
+uint8_t FileAccess::get_8() const {
+ uint8_t data = 0;
+ get_buffer(&data, sizeof(uint8_t));
-uint16_t FileAccess::get_16() const {
- uint16_t res;
- uint8_t a, b;
+ return data;
+}
- a = get_8();
- b = get_8();
+uint16_t FileAccess::get_16() const {
+ uint16_t data = 0;
+ get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint16_t));
if (big_endian) {
- SWAP(a, b);
+ data = BSWAP16(data);
}
- res = b;
- res <<= 8;
- res |= a;
-
- return res;
+ return data;
}
uint32_t FileAccess::get_32() const {
- uint32_t res;
- uint16_t a, b;
-
- a = get_16();
- b = get_16();
+ uint32_t data = 0;
+ get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint32_t));
if (big_endian) {
- SWAP(a, b);
+ data = BSWAP32(data);
}
- res = b;
- res <<= 16;
- res |= a;
-
- return res;
+ return data;
}
uint64_t FileAccess::get_64() const {
- uint64_t res;
- uint32_t a, b;
-
- a = get_32();
- b = get_32();
+ uint64_t data = 0;
+ get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint64_t));
if (big_endian) {
- SWAP(a, b);
+ data = BSWAP64(data);
}
- res = b;
- res <<= 32;
- res |= a;
-
- return res;
+ return data;
}
float FileAccess::get_float() const {
@@ -465,17 +450,6 @@ String FileAccess::get_as_text(bool p_skip_cr) const {
return text;
}
-uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
- ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
-
- uint64_t i = 0;
- for (i = 0; i < p_length && !eof_reached(); i++) {
- p_dst[i] = get_8();
- }
-
- return i;
-}
-
Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
Vector<uint8_t> data;
@@ -488,7 +462,7 @@ Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements.");
uint8_t *w = data.ptrw();
- int64_t len = get_buffer(&w[0], p_length);
+ int64_t len = get_buffer(w, p_length);
if (len < p_length) {
data.resize(len);
@@ -512,46 +486,32 @@ String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
return s;
}
-void FileAccess::store_16(uint16_t p_dest) {
- uint8_t a, b;
-
- a = p_dest & 0xFF;
- b = p_dest >> 8;
+void FileAccess::store_8(uint8_t p_dest) {
+ store_buffer(&p_dest, sizeof(uint8_t));
+}
+void FileAccess::store_16(uint16_t p_dest) {
if (big_endian) {
- SWAP(a, b);
+ p_dest = BSWAP16(p_dest);
}
- store_8(a);
- store_8(b);
+ store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint16_t));
}
void FileAccess::store_32(uint32_t p_dest) {
- uint16_t a, b;
-
- a = p_dest & 0xFFFF;
- b = p_dest >> 16;
-
if (big_endian) {
- SWAP(a, b);
+ p_dest = BSWAP32(p_dest);
}
- store_16(a);
- store_16(b);
+ store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint32_t));
}
void FileAccess::store_64(uint64_t p_dest) {
- uint32_t a, b;
-
- a = p_dest & 0xFFFFFFFF;
- b = p_dest >> 32;
-
if (big_endian) {
- SWAP(a, b);
+ p_dest = BSWAP64(p_dest);
}
- store_32(a);
- store_32(b);
+ store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint64_t));
}
void FileAccess::store_real(real_t p_real) {
@@ -708,22 +668,11 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_
store_line(line);
}
-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]);
- }
-}
-
void FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) {
uint64_t len = p_buffer.size();
- if (len == 0) {
- return;
- }
-
const uint8_t *r = p_buffer.ptr();
- store_buffer(&r[0], len);
+ store_buffer(r, len);
}
void FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
diff --git a/core/io/file_access.h b/core/io/file_access.h
index 2ab84db4b6..2f4d1a8604 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -137,7 +137,7 @@ public:
virtual bool eof_reached() const = 0; ///< reading passed EOF
- virtual uint8_t get_8() const = 0; ///< get a byte
+ virtual uint8_t get_8() const; ///< get a byte
virtual uint16_t get_16() const; ///< get 16 bits uint
virtual uint32_t get_32() const; ///< get 32 bits uint
virtual uint64_t get_64() const; ///< get 64 bits uint
@@ -148,7 +148,7 @@ public:
Variant get_var(bool p_allow_objects = false) const;
- virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const = 0; ///< get an array of bytes, needs to be overwritten by children.
Vector<uint8_t> get_buffer(int64_t p_length) const;
virtual String get_line() const;
virtual String get_token() const;
@@ -168,7 +168,7 @@ public:
virtual Error resize(int64_t p_length) = 0;
virtual void flush() = 0;
- virtual void store_8(uint8_t p_dest) = 0; ///< store a byte
+ virtual void store_8(uint8_t p_dest); ///< store a byte
virtual void store_16(uint16_t p_dest); ///< store 16 bits uint
virtual void store_32(uint32_t p_dest); ///< store 32 bits uint
virtual void store_64(uint64_t p_dest); ///< store 64 bits uint
@@ -184,7 +184,7 @@ public:
virtual void store_pascal_string(const String &p_string);
virtual String get_pascal_string();
- virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) = 0; ///< store an array of bytes, needs to be overwritten by children.
void store_buffer(const Vector<uint8_t> &p_buffer);
void store_var(const Variant &p_var, bool p_full_objects = false);
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index 0f00bd292c..3602baf8c5 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -260,38 +260,6 @@ bool FileAccessCompressed::eof_reached() const {
}
}
-uint8_t FileAccessCompressed::get_8() const {
- ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use.");
- ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
-
- if (at_end) {
- read_eof = true;
- return 0;
- }
-
- uint8_t ret = read_ptr[read_pos];
-
- read_pos++;
- if (read_pos >= read_block_size) {
- read_block++;
-
- if (read_block < read_block_count) {
- //read another block of compressed data
- f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize);
- int total = Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode);
- ERR_FAIL_COND_V_MSG(total == -1, 0, "Compressed file is corrupt.");
- read_block_size = read_block == read_block_count - 1 ? read_total % block_size : block_size;
- read_pos = 0;
-
- } else {
- read_block--;
- at_end = true;
- }
- }
-
- return ret;
-}
-
uint64_t FileAccessCompressed::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use.");
@@ -341,12 +309,13 @@ void FileAccessCompressed::flush() {
// compressed files keep data in memory till close()
}
-void FileAccessCompressed::store_8(uint8_t p_dest) {
+void FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use.");
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
- WRITE_FIT(1);
- write_ptr[write_pos++] = p_dest;
+ WRITE_FIT(p_length);
+ memcpy(write_ptr + write_pos, p_src, p_length);
+ write_pos += p_length;
}
bool FileAccessCompressed::file_exists(const String &p_name) {
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index f706c82f8e..ea9837dd03 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -83,14 +83,13 @@ public:
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index b689f5b628..13d1e0c8fc 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -37,7 +37,7 @@
#include <stdio.h>
Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) {
- ERR_FAIL_COND_V_MSG(file != nullptr, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open.");
+ ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open.");
ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER);
pos = 0;
@@ -162,7 +162,7 @@ void FileAccessEncrypted::_close() {
}
bool FileAccessEncrypted::is_open() const {
- return file != nullptr;
+ return file.is_valid();
}
String FileAccessEncrypted::get_path() const {
@@ -206,26 +206,13 @@ bool FileAccessEncrypted::eof_reached() const {
return eofed;
}
-uint8_t FileAccessEncrypted::get_8() const {
- ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
- if (pos >= get_length()) {
- eofed = true;
- return 0;
- }
-
- uint8_t b = data[pos];
- pos++;
- return b;
-}
-
uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode.");
uint64_t to_copy = MIN(p_length, get_length() - pos);
- for (uint64_t i = 0; i < to_copy; i++) {
- p_dst[i] = data[pos++];
- }
+ memcpy(p_dst, data.ptr() + pos, to_copy);
+ pos += to_copy;
if (to_copy < p_length) {
eofed = true;
@@ -242,17 +229,12 @@ 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++) {
- store_8(p_src[i]);
- }
- } else if (pos == get_length()) {
+ if (pos + p_length >= get_length()) {
data.resize(pos + p_length);
- for (uint64_t i = 0; i < p_length; i++) {
- data.write[pos + i] = p_src[i];
- }
- pos += p_length;
}
+
+ memcpy(data.ptrw() + pos, p_src, p_length);
+ pos += p_length;
}
void FileAccessEncrypted::flush() {
@@ -261,18 +243,6 @@ void FileAccessEncrypted::flush() {
// encrypted files keep data in memory till close()
}
-void FileAccessEncrypted::store_8(uint8_t p_dest) {
- ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
-
- if (pos < get_length()) {
- data.write[pos] = p_dest;
- pos++;
- } else if (pos == get_length()) {
- data.push_back(p_dest);
- pos++;
- }
-}
-
bool FileAccessEncrypted::file_exists(const String &p_name) {
Ref<FileAccess> fa = FileAccess::open(p_name, FileAccess::READ);
if (fa.is_null()) {
diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h
index 42afe49a5e..5f8c803d60 100644
--- a/core/io/file_access_encrypted.h
+++ b/core/io/file_access_encrypted.h
@@ -73,14 +73,12 @@ public:
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override; ///< store a byte
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 9521a4f666..1541a5ed4a 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -122,16 +122,6 @@ bool FileAccessMemory::eof_reached() const {
return pos >= length;
}
-uint8_t FileAccessMemory::get_8() const {
- uint8_t ret = 0;
- if (pos < length) {
- ret = data[pos];
- }
- ++pos;
-
- return ret;
-}
-
uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_NULL_V(data, -1);
@@ -157,16 +147,12 @@ void FileAccessMemory::flush() {
ERR_FAIL_NULL(data);
}
-void FileAccessMemory::store_8(uint8_t p_byte) {
- ERR_FAIL_NULL(data);
- ERR_FAIL_COND(pos >= length);
- data[pos++] = 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) {
WARN_PRINT("Writing less data than requested");
}
diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h
index e9fbc26d75..39e1528d97 100644
--- a/core/io/file_access_memory.h
+++ b/core/io/file_access_memory.h
@@ -55,15 +55,12 @@ public:
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
-
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; ///< get an array of bytes
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_byte) override; ///< store a byte
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 02bf0a6039..eec27ce0aa 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -313,17 +313,6 @@ bool FileAccessPack::eof_reached() const {
return eof;
}
-uint8_t FileAccessPack::get_8() const {
- ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use.");
- if (pos >= pf.size) {
- eof = true;
- return 0;
- }
-
- pos++;
- return f->get_8();
-}
-
uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
@@ -366,10 +355,6 @@ void FileAccessPack::flush() {
ERR_FAIL();
}
-void FileAccessPack::store_8(uint8_t p_dest) {
- ERR_FAIL();
-}
-
void FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL();
}
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 594ac8f089..595a36bca4 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -169,8 +169,6 @@ public:
virtual bool eof_reached() const override;
- virtual uint8_t get_8() const override;
-
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual void set_big_endian(bool p_big_endian) override;
@@ -179,8 +177,6 @@ public:
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override;
-
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
virtual bool file_exists(const String &p_name) override;
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index c0d1afc8e1..b33b7b35c3 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -291,12 +291,6 @@ bool FileAccessZip::eof_reached() const {
return at_eof;
}
-uint8_t FileAccessZip::get_8() const {
- uint8_t ret = 0;
- get_buffer(&ret, 1);
- return ret;
-}
-
uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_NULL_V(zfile, -1);
@@ -328,7 +322,7 @@ void FileAccessZip::flush() {
ERR_FAIL();
}
-void FileAccessZip::store_8(uint8_t p_dest) {
+void FileAccessZip::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL();
}
diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h
index 88b63e93e2..1e11e050df 100644
--- a/core/io/file_access_zip.h
+++ b/core/io/file_access_zip.h
@@ -95,14 +95,13 @@ public:
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/image.cpp b/core/io/image.cpp
index f6065d984b..fcbe483e38 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -4225,7 +4225,7 @@ Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool
result["root_mean_squared"] = INFINITY;
result["peak_snr"] = 0.0f;
- ERR_FAIL_NULL_V(p_compared_image, result);
+ ERR_FAIL_COND_V(p_compared_image.is_null(), result);
Error err = OK;
Ref<Image> compared_image = duplicate(true);
if (compared_image->is_compressed()) {
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 61051727c1..664ff7857b 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -588,10 +588,756 @@ void JSON::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line);
ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message);
+ ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_classes", "allow_scripts"), &JSON::to_native, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "allow_classes", "allow_scripts"), &JSON::from_native, DEFVAL(false), DEFVAL(false));
+
ADD_PROPERTY(PropertyInfo(Variant::NIL, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), "set_data", "get_data"); // Ensures that it can be serialized as binary.
}
-////
+#define GDTYPE "__gdtype"
+#define VALUES "values"
+#define PASS_ARG p_allow_classes, p_allow_scripts
+
+Variant JSON::from_native(const Variant &p_variant, bool p_allow_classes, bool p_allow_scripts) {
+ switch (p_variant.get_type()) {
+ case Variant::NIL: {
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+ } break;
+ case Variant::BOOL: {
+ return p_variant;
+ } break;
+ case Variant::INT: {
+ return p_variant;
+ } break;
+ case Variant::FLOAT: {
+ return p_variant;
+ } break;
+ case Variant::STRING: {
+ return p_variant;
+ } break;
+ case Variant::VECTOR2: {
+ Dictionary d;
+ Vector2 v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR2I: {
+ Dictionary d;
+ Vector2i v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::RECT2: {
+ Dictionary d;
+ Rect2 r = p_variant;
+ d["position"] = from_native(r.position);
+ d["size"] = from_native(r.size);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::RECT2I: {
+ Dictionary d;
+ Rect2i r = p_variant;
+ d["position"] = from_native(r.position);
+ d["size"] = from_native(r.size);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR3: {
+ Dictionary d;
+ Vector3 v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR3I: {
+ Dictionary d;
+ Vector3i v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::TRANSFORM2D: {
+ Dictionary d;
+ Transform2D t = p_variant;
+ d["x"] = from_native(t[0]);
+ d["y"] = from_native(t[1]);
+ d["origin"] = from_native(t[2]);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR4: {
+ Dictionary d;
+ Vector4 v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ values.push_back(v.w);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR4I: {
+ Dictionary d;
+ Vector4i v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ values.push_back(v.w);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PLANE: {
+ Dictionary d;
+ Plane p = p_variant;
+ d["normal"] = from_native(p.normal);
+ d["d"] = p.d;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::QUATERNION: {
+ Dictionary d;
+ Quaternion q = p_variant;
+ Array values;
+ values.push_back(q.x);
+ values.push_back(q.y);
+ values.push_back(q.z);
+ values.push_back(q.w);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::AABB: {
+ Dictionary d;
+ AABB aabb = p_variant;
+ d["position"] = from_native(aabb.position);
+ d["size"] = from_native(aabb.size);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::BASIS: {
+ Dictionary d;
+ Basis t = p_variant;
+ d["x"] = from_native(t.get_column(0));
+ d["y"] = from_native(t.get_column(1));
+ d["z"] = from_native(t.get_column(2));
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::TRANSFORM3D: {
+ Dictionary d;
+ Transform3D t = p_variant;
+ d["basis"] = from_native(t.basis);
+ d["origin"] = from_native(t.origin);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PROJECTION: {
+ Dictionary d;
+ Projection t = p_variant;
+ d["x"] = from_native(t[0]);
+ d["y"] = from_native(t[1]);
+ d["z"] = from_native(t[2]);
+ d["w"] = from_native(t[3]);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::COLOR: {
+ Dictionary d;
+ Color c = p_variant;
+ Array values;
+ values.push_back(c.r);
+ values.push_back(c.g);
+ values.push_back(c.b);
+ values.push_back(c.a);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::STRING_NAME: {
+ Dictionary d;
+ d["name"] = String(p_variant);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::NODE_PATH: {
+ Dictionary d;
+ d["path"] = String(p_variant);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::RID: {
+ Dictionary d;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::OBJECT: {
+ Object *obj = p_variant.get_validated_object();
+
+ if (p_allow_classes && obj) {
+ Dictionary d;
+ List<PropertyInfo> property_list;
+ obj->get_property_list(&property_list);
+
+ d["type"] = obj->get_class();
+ Dictionary p;
+ for (const PropertyInfo &P : property_list) {
+ if (P.usage & PROPERTY_USAGE_STORAGE) {
+ if (P.name == "script" && !p_allow_scripts) {
+ continue;
+ }
+ p[P.name] = from_native(obj->get(P.name), PASS_ARG);
+ }
+ }
+ d["properties"] = p;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } else {
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+ }
+ } break;
+ case Variant::CALLABLE:
+ case Variant::SIGNAL: {
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = p_variant;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ bool all_strings = true;
+ for (const Variant &K : keys) {
+ if (K.get_type() != Variant::STRING) {
+ all_strings = false;
+ break;
+ }
+ }
+
+ if (all_strings) {
+ Dictionary ret_dict;
+ for (const Variant &K : keys) {
+ ret_dict[K] = from_native(d[K], PASS_ARG);
+ }
+ return ret_dict;
+ } else {
+ Dictionary ret;
+ Array pairs;
+ for (const Variant &K : keys) {
+ Dictionary pair;
+ pair["key"] = from_native(K, PASS_ARG);
+ pair["value"] = from_native(d[K], PASS_ARG);
+ pairs.push_back(pair);
+ }
+ ret["pairs"] = pairs;
+ ret[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return ret;
+ }
+ } break;
+ case Variant::ARRAY: {
+ Array arr = p_variant;
+ Array ret;
+ for (int i = 0; i < arr.size(); i++) {
+ ret.push_back(from_native(arr[i], PASS_ARG));
+ }
+ return ret;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ Dictionary d;
+ PackedByteArray arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ Dictionary d;
+ PackedInt32Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+
+ } break;
+ case Variant::PACKED_INT64_ARRAY: {
+ Dictionary d;
+ PackedInt64Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ Dictionary d;
+ PackedFloat32Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_FLOAT64_ARRAY: {
+ Dictionary d;
+ PackedFloat64Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+ Dictionary d;
+ PackedStringArray arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ Dictionary d;
+ PackedVector2Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Vector2 v = arr[i];
+ values.push_back(v.x);
+ values.push_back(v.y);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ Dictionary d;
+ PackedVector3Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Vector3 v = arr[i];
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ Dictionary d;
+ PackedColorArray arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Color v = arr[i];
+ values.push_back(v.r);
+ values.push_back(v.g);
+ values.push_back(v.b);
+ values.push_back(v.a);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_VECTOR4_ARRAY: {
+ Dictionary d;
+ PackedVector4Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Vector4 v = arr[i];
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ values.push_back(v.w);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ default: {
+ ERR_PRINT(vformat("Unhandled conversion from native Variant type '%s' to JSON.", Variant::get_type_name(p_variant.get_type())));
+ } break;
+ }
+
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+}
+
+Variant JSON::to_native(const Variant &p_json, bool p_allow_classes, bool p_allow_scripts) {
+ switch (p_json.get_type()) {
+ case Variant::BOOL: {
+ return p_json;
+ } break;
+ case Variant::INT: {
+ return p_json;
+ } break;
+ case Variant::FLOAT: {
+ return p_json;
+ } break;
+ case Variant::STRING: {
+ return p_json;
+ } break;
+ case Variant::STRING_NAME: {
+ return p_json;
+ } break;
+ case Variant::CALLABLE: {
+ return p_json;
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = p_json;
+ if (d.has(GDTYPE)) {
+ // Specific Godot Variant types serialized to JSON.
+ String type = d[GDTYPE];
+ if (type == Variant::get_type_name(Variant::VECTOR2)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 2, Variant());
+ Vector2 v;
+ v.x = values[0];
+ v.y = values[1];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::VECTOR2I)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 2, Variant());
+ Vector2i v;
+ v.x = values[0];
+ v.y = values[1];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::RECT2)) {
+ ERR_FAIL_COND_V(!d.has("position"), Variant());
+ ERR_FAIL_COND_V(!d.has("size"), Variant());
+ Rect2 r;
+ r.position = to_native(d["position"]);
+ r.size = to_native(d["size"]);
+ return r;
+ } else if (type == Variant::get_type_name(Variant::RECT2I)) {
+ ERR_FAIL_COND_V(!d.has("position"), Variant());
+ ERR_FAIL_COND_V(!d.has("size"), Variant());
+ Rect2i r;
+ r.position = to_native(d["position"]);
+ r.size = to_native(d["size"]);
+ return r;
+ } else if (type == Variant::get_type_name(Variant::VECTOR3)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 3, Variant());
+ Vector3 v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::VECTOR3I)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 3, Variant());
+ Vector3i v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::TRANSFORM2D)) {
+ ERR_FAIL_COND_V(!d.has("x"), Variant());
+ ERR_FAIL_COND_V(!d.has("y"), Variant());
+ ERR_FAIL_COND_V(!d.has("origin"), Variant());
+ Transform2D t;
+ t[0] = to_native(d["x"]);
+ t[1] = to_native(d["y"]);
+ t[2] = to_native(d["origin"]);
+ return t;
+ } else if (type == Variant::get_type_name(Variant::VECTOR4)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Vector4 v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ v.w = values[3];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::VECTOR4I)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Vector4i v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ v.w = values[3];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::PLANE)) {
+ ERR_FAIL_COND_V(!d.has("normal"), Variant());
+ ERR_FAIL_COND_V(!d.has("d"), Variant());
+ Plane p;
+ p.normal = to_native(d["normal"]);
+ p.d = d["d"];
+ return p;
+ } else if (type == Variant::get_type_name(Variant::QUATERNION)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Quaternion v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ v.w = values[3];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::AABB)) {
+ ERR_FAIL_COND_V(!d.has("position"), Variant());
+ ERR_FAIL_COND_V(!d.has("size"), Variant());
+ AABB r;
+ r.position = to_native(d["position"]);
+ r.size = to_native(d["size"]);
+ return r;
+ } else if (type == Variant::get_type_name(Variant::BASIS)) {
+ ERR_FAIL_COND_V(!d.has("x"), Variant());
+ ERR_FAIL_COND_V(!d.has("y"), Variant());
+ ERR_FAIL_COND_V(!d.has("z"), Variant());
+ Basis b;
+ b.set_column(0, to_native(d["x"]));
+ b.set_column(1, to_native(d["y"]));
+ b.set_column(2, to_native(d["z"]));
+ return b;
+ } else if (type == Variant::get_type_name(Variant::TRANSFORM3D)) {
+ ERR_FAIL_COND_V(!d.has("basis"), Variant());
+ ERR_FAIL_COND_V(!d.has("origin"), Variant());
+ Transform3D t;
+ t.basis = to_native(d["basis"]);
+ t.origin = to_native(d["origin"]);
+ return t;
+ } else if (type == Variant::get_type_name(Variant::PROJECTION)) {
+ ERR_FAIL_COND_V(!d.has("x"), Variant());
+ ERR_FAIL_COND_V(!d.has("y"), Variant());
+ ERR_FAIL_COND_V(!d.has("z"), Variant());
+ ERR_FAIL_COND_V(!d.has("w"), Variant());
+ Projection p;
+ p[0] = to_native(d["x"]);
+ p[1] = to_native(d["y"]);
+ p[2] = to_native(d["z"]);
+ p[3] = to_native(d["w"]);
+ return p;
+ } else if (type == Variant::get_type_name(Variant::COLOR)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Color c;
+ c.r = values[0];
+ c.g = values[1];
+ c.b = values[2];
+ c.a = values[3];
+ return c;
+ } else if (type == Variant::get_type_name(Variant::NODE_PATH)) {
+ ERR_FAIL_COND_V(!d.has("path"), Variant());
+ NodePath np = d["path"];
+ return np;
+ } else if (type == Variant::get_type_name(Variant::STRING_NAME)) {
+ ERR_FAIL_COND_V(!d.has("name"), Variant());
+ StringName s = d["name"];
+ return s;
+ } else if (type == Variant::get_type_name(Variant::OBJECT)) {
+ ERR_FAIL_COND_V(!d.has("type"), Variant());
+ ERR_FAIL_COND_V(!d.has("properties"), Variant());
+
+ ERR_FAIL_COND_V(!p_allow_classes, Variant());
+
+ String obj_type = d["type"];
+ bool is_script = obj_type == "Script" || ClassDB::is_parent_class(obj_type, "Script");
+ ERR_FAIL_COND_V(!p_allow_scripts && is_script, Variant());
+ Object *obj = ClassDB::instantiate(obj_type);
+ ERR_FAIL_NULL_V(obj, Variant());
+
+ Dictionary p = d["properties"];
+
+ List<Variant> keys;
+ p.get_key_list(&keys);
+
+ for (const Variant &K : keys) {
+ String property = K;
+ Variant value = to_native(p[K], PASS_ARG);
+ obj->set(property, value);
+ }
+
+ Variant v(obj);
+
+ return v;
+ } else if (type == Variant::get_type_name(Variant::DICTIONARY)) {
+ ERR_FAIL_COND_V(!d.has("pairs"), Variant());
+ Array pairs = d["pairs"];
+ Dictionary r;
+ for (int i = 0; i < pairs.size(); i++) {
+ Dictionary p = pairs[i];
+ ERR_CONTINUE(!p.has("key"));
+ ERR_CONTINUE(!p.has("value"));
+ r[to_native(p["key"], PASS_ARG)] = to_native(p["value"]);
+ }
+ return r;
+ } else if (type == Variant::get_type_name(Variant::ARRAY)) {
+ ERR_PRINT(vformat("Unexpected Array with '%s' key. Arrays are supported natively.", GDTYPE));
+ } else if (type == Variant::get_type_name(Variant::PACKED_BYTE_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedByteArray pbarr;
+ pbarr.resize(values.size());
+ for (int i = 0; i < pbarr.size(); i++) {
+ pbarr.write[i] = values[i];
+ }
+ return pbarr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_INT32_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedInt32Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_INT64_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedInt64Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedFloat32Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedFloat64Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_STRING_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedStringArray arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 2 != 0, Variant());
+ PackedVector2Array arr;
+ arr.resize(values.size() / 2);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Vector2(values[i * 2 + 0], values[i * 2 + 1]);
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR3_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 3 != 0, Variant());
+ PackedVector3Array arr;
+ arr.resize(values.size() / 3);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Vector3(values[i * 3 + 0], values[i * 3 + 1], values[i * 3 + 2]);
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 4 != 0, Variant());
+ PackedColorArray arr;
+ arr.resize(values.size() / 4);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Color(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]);
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR4_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 4 != 0, Variant());
+ PackedVector4Array arr;
+ arr.resize(values.size() / 4);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Vector4(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]);
+ }
+ return arr;
+ } else {
+ return Variant();
+ }
+ } else {
+ // Regular dictionary with string keys.
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ Dictionary r;
+ for (const Variant &K : keys) {
+ r[K] = to_native(d[K], PASS_ARG);
+ }
+ return r;
+ }
+ } break;
+ case Variant::ARRAY: {
+ Array arr = p_json;
+ Array ret;
+ ret.resize(arr.size());
+ for (int i = 0; i < arr.size(); i++) {
+ ret[i] = to_native(arr[i], PASS_ARG);
+ }
+ return ret;
+ } break;
+ default: {
+ ERR_PRINT(vformat("Unhandled conversion from JSON type '%s' to native Variant type.", Variant::get_type_name(p_json.get_type())));
+ return Variant();
+ }
+ }
+
+ return Variant();
+}
+
+#undef GDTYPE
+#undef VALUES
+#undef PASS_ARG
////////////
diff --git a/core/io/json.h b/core/io/json.h
index 801fa29b4b..67b5e09afa 100644
--- a/core/io/json.h
+++ b/core/io/json.h
@@ -94,6 +94,9 @@ public:
void set_data(const Variant &p_data);
inline int get_error_line() const { return err_line; }
inline String get_error_message() const { return err_str; }
+
+ static Variant from_native(const Variant &p_variant, bool p_allow_classes = false, bool p_allow_scripts = false);
+ static Variant to_native(const Variant &p_json, bool p_allow_classes = false, bool p_allow_scripts = false);
};
class ResourceFormatLoaderJSON : public ResourceFormatLoader {
diff --git a/core/io/packed_data_container.h b/core/io/packed_data_container.h
index cc9996101e..f4ffa09022 100644
--- a/core/io/packed_data_container.h
+++ b/core/io/packed_data_container.h
@@ -36,7 +36,7 @@
class PackedDataContainer : public Resource {
GDCLASS(PackedDataContainer, Resource);
- enum {
+ enum : uint32_t {
TYPE_DICT = 0xFFFFFFFF,
TYPE_ARRAY = 0xFFFFFFFE,
};
diff --git a/core/io/plist.cpp b/core/io/plist.cpp
index 86737609bf..8d91e6dec2 100644
--- a/core/io/plist.cpp
+++ b/core/io/plist.cpp
@@ -814,7 +814,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
}
PackedByteArray PList::save_asn1() const {
- if (root == nullptr) {
+ if (root.is_null()) {
ERR_FAIL_V_MSG(PackedByteArray(), "PList: Invalid PList, no root node.");
}
size_t size = root->get_asn1_size(1);
@@ -848,7 +848,7 @@ PackedByteArray PList::save_asn1() const {
}
String PList::save_text() const {
- if (root == nullptr) {
+ if (root.is_null()) {
ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node.");
}
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 598c99c188..ff12dc5851 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -60,32 +60,32 @@ void Resource::set_path(const String &p_path, bool p_take_over) {
p_take_over = false; // Can't take over an empty path
}
- ResourceCache::lock.lock();
+ {
+ MutexLock lock(ResourceCache::lock);
- if (!path_cache.is_empty()) {
- ResourceCache::resources.erase(path_cache);
- }
+ if (!path_cache.is_empty()) {
+ ResourceCache::resources.erase(path_cache);
+ }
- path_cache = "";
+ path_cache = "";
- Ref<Resource> existing = ResourceCache::get_ref(p_path);
+ Ref<Resource> existing = ResourceCache::get_ref(p_path);
- if (existing.is_valid()) {
- if (p_take_over) {
- existing->path_cache = String();
- ResourceCache::resources.erase(p_path);
- } else {
- ResourceCache::lock.unlock();
- ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion).");
+ if (existing.is_valid()) {
+ if (p_take_over) {
+ existing->path_cache = String();
+ ResourceCache::resources.erase(p_path);
+ } else {
+ ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion).");
+ }
}
- }
- path_cache = p_path;
+ path_cache = p_path;
- if (!path_cache.is_empty()) {
- ResourceCache::resources[path_cache] = this;
+ if (!path_cache.is_empty()) {
+ ResourceCache::resources[path_cache] = this;
+ }
}
- ResourceCache::lock.unlock();
_resource_path_changed();
}
@@ -486,15 +486,13 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
return;
}
- ResourceCache::lock.lock();
+ MutexLock lock(ResourceCache::lock);
if (p_remapped) {
ResourceLoader::remapped_list.add(&remapped_list);
} else {
ResourceLoader::remapped_list.remove(&remapped_list);
}
-
- ResourceCache::lock.unlock();
}
#ifdef TOOLS_ENABLED
@@ -564,14 +562,13 @@ Resource::~Resource() {
return;
}
- ResourceCache::lock.lock();
+ MutexLock lock(ResourceCache::lock);
// Only unregister from the cache if this is the actual resource listed there.
// (Other resources can have the same value in `path_cache` if loaded with `CACHE_IGNORE`.)
HashMap<String, Resource *>::Iterator E = ResourceCache::resources.find(path_cache);
if (likely(E && E->value == this)) {
ResourceCache::resources.remove(E);
}
- ResourceCache::lock.unlock();
}
HashMap<String, Resource *> ResourceCache::resources;
@@ -600,18 +597,20 @@ void ResourceCache::clear() {
}
bool ResourceCache::has(const String &p_path) {
- lock.lock();
+ Resource **res = nullptr;
- Resource **res = resources.getptr(p_path);
+ {
+ MutexLock mutex_lock(lock);
- if (res && (*res)->get_reference_count() == 0) {
- // This resource is in the process of being deleted, ignore its existence.
- (*res)->path_cache = String();
- resources.erase(p_path);
- res = nullptr;
- }
+ res = resources.getptr(p_path);
- lock.unlock();
+ if (res && (*res)->get_reference_count() == 0) {
+ // This resource is in the process of being deleted, ignore its existence.
+ (*res)->path_cache = String();
+ resources.erase(p_path);
+ res = nullptr;
+ }
+ }
if (!res) {
return false;
@@ -622,28 +621,27 @@ bool ResourceCache::has(const String &p_path) {
Ref<Resource> ResourceCache::get_ref(const String &p_path) {
Ref<Resource> ref;
- lock.lock();
-
- Resource **res = resources.getptr(p_path);
+ {
+ MutexLock mutex_lock(lock);
+ Resource **res = resources.getptr(p_path);
- if (res) {
- ref = Ref<Resource>(*res);
- }
+ if (res) {
+ ref = Ref<Resource>(*res);
+ }
- if (res && !ref.is_valid()) {
- // This resource is in the process of being deleted, ignore its existence
- (*res)->path_cache = String();
- resources.erase(p_path);
- res = nullptr;
+ if (res && !ref.is_valid()) {
+ // This resource is in the process of being deleted, ignore its existence
+ (*res)->path_cache = String();
+ resources.erase(p_path);
+ res = nullptr;
+ }
}
- lock.unlock();
-
return ref;
}
void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) {
- lock.lock();
+ MutexLock mutex_lock(lock);
LocalVector<String> to_remove;
@@ -663,14 +661,9 @@ void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) {
for (const String &E : to_remove) {
resources.erase(E);
}
-
- lock.unlock();
}
int ResourceCache::get_cached_resource_count() {
- lock.lock();
- int rc = resources.size();
- lock.unlock();
-
- return rc;
+ MutexLock mutex_lock(lock);
+ return resources.size();
}
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index b4c43abe00..a572dd562e 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -35,6 +35,8 @@
#include "core/os/os.h"
#include "core/variant/variant_parser.h"
+ResourceFormatImporterLoadOnStartup ResourceImporter::load_on_startup = nullptr;
+
bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const {
return p_a->get_importer_name() < p_b->get_importer_name();
}
@@ -137,6 +139,20 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
}
Ref<Resource> ResourceFormatImporter::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+#ifdef TOOLS_ENABLED
+ // When loading a resource on startup, we use the load_on_startup callback,
+ // which executes the loading in the EditorFileSystem. It can reimport
+ // the resource and retry the load, allowing the resource to be loaded
+ // even if it is not yet imported.
+ if (ResourceImporter::load_on_startup != nullptr) {
+ return ResourceImporter::load_on_startup(this, p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
+ }
+#endif
+
+ return load_internal(p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode, false);
+}
+
+Ref<Resource> ResourceFormatImporter::load_internal(const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode, bool p_silence_errors) {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);
@@ -148,6 +164,13 @@ Ref<Resource> ResourceFormatImporter::load(const String &p_path, const String &p
return Ref<Resource>();
}
+ if (p_silence_errors) {
+ // Note: Some importers do not create files in the .godot folder, so we need to check if the path is empty.
+ if (!pat.path.is_empty() && !FileAccess::exists(pat.path)) {
+ return Ref<Resource>();
+ }
+ }
+
Ref<Resource> res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, r_error, p_use_sub_threads, r_progress);
#ifdef TOOLS_ENABLED
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index 7b1806c3d2..6ea5d0972a 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -35,6 +35,9 @@
#include "core/io/resource_saver.h"
class ResourceImporter;
+class ResourceFormatImporter;
+
+typedef Ref<Resource> (*ResourceFormatImporterLoadOnStartup)(ResourceFormatImporter *p_importer, const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, ResourceFormatLoader::CacheMode p_cache_mode);
class ResourceFormatImporter : public ResourceFormatLoader {
struct PathAndType {
@@ -60,6 +63,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
public:
static ResourceFormatImporter *get_singleton() { return singleton; }
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
+ Ref<Resource> load_internal(const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode, bool p_silence_errors);
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const override;
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const override;
@@ -94,6 +98,7 @@ public:
String get_import_base_path(const String &p_for_file) const;
Error get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const;
+
ResourceFormatImporter();
};
@@ -104,6 +109,8 @@ protected:
static void _bind_methods();
public:
+ static ResourceFormatImporterLoadOnStartup load_on_startup;
+
virtual String get_importer_name() const = 0;
virtual String get_visible_name() const = 0;
virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 3e809ae762..7cf101b0de 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -227,28 +227,27 @@ void ResourceFormatLoader::_bind_methods() {
// This should be robust enough to be called redundantly without issues.
void ResourceLoader::LoadToken::clear() {
- thread_load_mutex.lock();
-
WorkerThreadPool::TaskID task_to_await = 0;
- // User-facing tokens shouldn't be deleted until completely claimed.
- DEV_ASSERT(user_rc == 0 && user_path.is_empty());
-
- if (!local_path.is_empty()) { // Empty is used for the special case where the load task is not registered.
- DEV_ASSERT(thread_load_tasks.has(local_path));
- ThreadLoadTask &load_task = thread_load_tasks[local_path];
- if (load_task.task_id && !load_task.awaited) {
- task_to_await = load_task.task_id;
+ {
+ MutexLock thread_load_lock(thread_load_mutex);
+ // User-facing tokens shouldn't be deleted until completely claimed.
+ DEV_ASSERT(user_rc == 0 && user_path.is_empty());
+
+ if (!local_path.is_empty()) { // Empty is used for the special case where the load task is not registered.
+ DEV_ASSERT(thread_load_tasks.has(local_path));
+ ThreadLoadTask &load_task = thread_load_tasks[local_path];
+ if (load_task.task_id && !load_task.awaited) {
+ task_to_await = load_task.task_id;
+ }
+ // Removing a task which is still in progress would be catastrophic.
+ // Tokens must be alive until the task thread function is done.
+ DEV_ASSERT(load_task.status == THREAD_LOAD_FAILED || load_task.status == THREAD_LOAD_LOADED);
+ thread_load_tasks.erase(local_path);
+ local_path.clear();
}
- // Removing a task which is still in progress would be catastrophic.
- // Tokens must be alive until the task thread function is done.
- DEV_ASSERT(load_task.status == THREAD_LOAD_FAILED || load_task.status == THREAD_LOAD_LOADED);
- thread_load_tasks.erase(local_path);
- local_path.clear();
}
- thread_load_mutex.unlock();
-
// If task is unused, await it here, locally, now the token data is consistent.
if (task_to_await) {
PREPARE_FOR_WTP_WAIT
@@ -265,7 +264,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
const String &original_path = p_original_path.is_empty() ? p_path : p_original_path;
load_nesting++;
if (load_paths_stack.size()) {
- thread_load_mutex.lock();
+ MutexLock thread_load_lock(thread_load_mutex);
const String &parent_task_path = load_paths_stack.get(load_paths_stack.size() - 1);
HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path);
// Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources).
@@ -273,7 +272,6 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
if (E && !is_remapped_load) {
E->value.sub_tasks.insert(p_original_path);
}
- thread_load_mutex.unlock();
}
load_paths_stack.push_back(original_path);
@@ -318,13 +316,13 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
void ResourceLoader::_run_load_task(void *p_userdata) {
ThreadLoadTask &load_task = *(ThreadLoadTask *)p_userdata;
- thread_load_mutex.lock();
- if (cleaning_tasks) {
- load_task.status = THREAD_LOAD_FAILED;
- thread_load_mutex.unlock();
- return;
+ {
+ MutexLock thread_load_lock(thread_load_mutex);
+ if (cleaning_tasks) {
+ load_task.status = THREAD_LOAD_FAILED;
+ return;
+ }
}
- thread_load_mutex.unlock();
// Thread-safe either if it's the current thread or a brand new one.
CallQueue *own_mq_override = nullptr;
@@ -1170,17 +1168,17 @@ String ResourceLoader::path_remap(const String &p_path) {
}
void ResourceLoader::reload_translation_remaps() {
- ResourceCache::lock.lock();
-
List<Resource *> to_reload;
- SelfList<Resource> *E = remapped_list.first();
- while (E) {
- to_reload.push_back(E->self());
- E = E->next();
- }
+ {
+ MutexLock lock(ResourceCache::lock);
+ SelfList<Resource> *E = remapped_list.first();
- ResourceCache::lock.unlock();
+ while (E) {
+ to_reload.push_back(E->self());
+ E = E->next();
+ }
+ }
//now just make sure to not delete any of these resources while changing locale..
while (to_reload.front()) {
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index fd53ed28fd..1afc5f4bbb 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -105,6 +105,9 @@ public:
static _ALWAYS_INLINE_ double fmod(double p_x, double p_y) { return ::fmod(p_x, p_y); }
static _ALWAYS_INLINE_ float fmod(float p_x, float p_y) { return ::fmodf(p_x, p_y); }
+ static _ALWAYS_INLINE_ double modf(double p_x, double *r_y) { return ::modf(p_x, r_y); }
+ static _ALWAYS_INLINE_ float modf(float p_x, float *r_y) { return ::modff(p_x, r_y); }
+
static _ALWAYS_INLINE_ double floor(double p_x) { return ::floor(p_x); }
static _ALWAYS_INLINE_ float floor(float p_x) { return ::floorf(p_x); }
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 5c793a676f..a65411629f 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -181,7 +181,7 @@ public:
return 0;
}
- static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata, bool p_notify_postinitialize) {
+ static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata, GDExtensionBool p_notify_postinitialize) {
ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata;
// Find the closest native parent, that isn't a runtime class.
@@ -192,7 +192,7 @@ public:
ERR_FAIL_NULL_V(native_parent->creation_func, nullptr);
// Construct a placeholder.
- Object *obj = native_parent->creation_func(p_notify_postinitialize);
+ Object *obj = native_parent->creation_func(static_cast<bool>(p_notify_postinitialize));
// ClassDB::set_object_extension_instance() won't be called for placeholders.
// We need need to make sure that all the things it would have done (even if
@@ -271,6 +271,22 @@ void ClassDB::get_extensions_class_list(List<StringName> *p_classes) {
p_classes->sort_custom<StringName::AlphCompare>();
}
+
+void ClassDB::get_extension_class_list(const Ref<GDExtension> &p_extension, List<StringName> *p_classes) {
+ OBJTYPE_RLOCK;
+
+ for (const KeyValue<StringName, ClassInfo> &E : classes) {
+ if (E.value.api != API_EXTENSION && E.value.api != API_EDITOR_EXTENSION) {
+ continue;
+ }
+ if (!E.value.gdextension || E.value.gdextension->library != p_extension.ptr()) {
+ continue;
+ }
+ p_classes->push_back(E.key);
+ }
+
+ p_classes->sort_custom<StringName::AlphCompare>();
+}
#endif
void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) {
@@ -1632,14 +1648,16 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia
Variant index = psg->index;
const Variant *arg[1] = { &index };
Callable::CallError ce;
- r_value = p_object->callp(psg->getter, arg, 1, ce);
+ const Variant value = p_object->callp(psg->getter, arg, 1, ce);
+ r_value = (ce.error == Callable::CallError::CALL_OK) ? value : Variant();
} else {
Callable::CallError ce;
if (psg->_getptr) {
r_value = psg->_getptr->call(p_object, nullptr, 0, ce);
} else {
- r_value = p_object->callp(psg->getter, nullptr, 0, ce);
+ const Variant value = p_object->callp(psg->getter, nullptr, 0, ce);
+ r_value = (ce.error == Callable::CallError::CALL_OK) ? value : Variant();
}
}
return true;
diff --git a/core/object/class_db.h b/core/object/class_db.h
index d6a95b58e2..620092a6c4 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -285,6 +285,7 @@ public:
static void get_class_list(List<StringName> *p_classes);
#ifdef TOOLS_ENABLED
static void get_extensions_class_list(List<StringName> *p_classes);
+ static void get_extension_class_list(const Ref<GDExtension> &p_extension, List<StringName> *p_classes);
static ObjectGDExtension *get_placeholder_extension(const StringName &p_class);
#endif
static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
diff --git a/core/object/object.cpp b/core/object/object.cpp
index a2330ecd04..4be1dc4b34 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -605,7 +605,7 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::Cal
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ if (!p_args[0]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
@@ -624,7 +624,7 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Call
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ if (!p_args[0]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
@@ -720,7 +720,7 @@ Variant Object::getvar(const Variant &p_key, bool *r_valid) const {
*r_valid = false;
}
- if (p_key.get_type() == Variant::STRING_NAME || p_key.get_type() == Variant::STRING) {
+ if (p_key.is_string()) {
return get(p_key, r_valid);
}
return Variant();
@@ -730,7 +730,7 @@ void Object::setvar(const Variant &p_key, const Variant &p_value, bool *r_valid)
if (r_valid) {
*r_valid = false;
}
- if (p_key.get_type() == Variant::STRING_NAME || p_key.get_type() == Variant::STRING) {
+ if (p_key.is_string()) {
return set(p_key, p_value, r_valid);
}
}
@@ -746,7 +746,7 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) {
}
Callable::CallError ce;
- Variant ret = callp(p_method, argptrs, p_args.size(), ce);
+ const Variant ret = callp(p_method, argptrs, p_args.size(), ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + ".");
}
@@ -787,7 +787,7 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
if (script_instance) {
ret = script_instance->callp(p_method, p_args, p_argcount, r_error);
- //force jumptable
+ // Force jump table.
switch (r_error.error) {
case Callable::CallError::CALL_OK:
return ret;
@@ -1023,6 +1023,14 @@ void Object::remove_meta(const StringName &p_name) {
set_meta(p_name, Variant());
}
+void Object::merge_meta_from(const Object *p_src) {
+ List<StringName> meta_keys;
+ p_src->get_meta_list(&meta_keys);
+ for (const StringName &key : meta_keys) {
+ set_meta(key, p_src->get_meta(key));
+ }
+}
+
TypedArray<Dictionary> Object::_get_property_list_bind() const {
List<PropertyInfo> lpi;
get_property_list(&lpi);
@@ -1096,7 +1104,7 @@ Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::Cal
ERR_FAIL_V(Error::ERR_INVALID_PARAMETER);
}
- if (unlikely(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING)) {
+ if (unlikely(!p_args[0]->is_string())) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
@@ -1904,7 +1912,7 @@ void Object::set_instance_binding(void *p_token, void *p_binding, const GDExtens
void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
void *binding = nullptr;
- _instance_binding_mutex.lock();
+ MutexLock instance_binding_lock(_instance_binding_mutex);
for (uint32_t i = 0; i < _instance_binding_count; i++) {
if (_instance_bindings[i].token == p_token) {
binding = _instance_bindings[i].binding;
@@ -1935,14 +1943,12 @@ void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindi
_instance_binding_count++;
}
- _instance_binding_mutex.unlock();
-
return binding;
}
bool Object::has_instance_binding(void *p_token) {
bool found = false;
- _instance_binding_mutex.lock();
+ MutexLock instance_binding_lock(_instance_binding_mutex);
for (uint32_t i = 0; i < _instance_binding_count; i++) {
if (_instance_bindings[i].token == p_token) {
found = true;
@@ -1950,14 +1956,12 @@ bool Object::has_instance_binding(void *p_token) {
}
}
- _instance_binding_mutex.unlock();
-
return found;
}
void Object::free_instance_binding(void *p_token) {
bool found = false;
- _instance_binding_mutex.lock();
+ MutexLock instance_binding_lock(_instance_binding_mutex);
for (uint32_t i = 0; i < _instance_binding_count; i++) {
if (!found && _instance_bindings[i].token == p_token) {
if (_instance_bindings[i].free_callback) {
@@ -1976,7 +1980,6 @@ void Object::free_instance_binding(void *p_token) {
if (found) {
_instance_binding_count--;
}
- _instance_binding_mutex.unlock();
}
#ifdef TOOLS_ENABLED
@@ -2294,7 +2297,7 @@ void ObjectDB::cleanup() {
// Ensure calling the native classes because if a leaked instance has a script
// that overrides any of those methods, it'd not be OK to call them at this point,
// now the scripting languages have already been terminated.
- MethodBind *node_get_name = ClassDB::get_method("Node", "get_name");
+ MethodBind *node_get_path = ClassDB::get_method("Node", "get_path");
MethodBind *resource_get_path = ClassDB::get_method("Resource", "get_path");
Callable::CallError call_error;
@@ -2304,7 +2307,7 @@ void ObjectDB::cleanup() {
String extra_info;
if (obj->is_class("Node")) {
- extra_info = " - Node name: " + String(node_get_name->call(obj, nullptr, 0, call_error));
+ extra_info = " - Node path: " + String(node_get_path->call(obj, nullptr, 0, call_error));
}
if (obj->is_class("Resource")) {
extra_info = " - Resource path: " + String(resource_get_path->call(obj, nullptr, 0, call_error));
diff --git a/core/object/object.h b/core/object/object.h
index ba6b309542..bc3f663baf 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -667,7 +667,7 @@ protected:
_FORCE_INLINE_ bool _instance_binding_reference(bool p_reference) {
bool can_die = true;
if (_instance_bindings) {
- _instance_binding_mutex.lock();
+ MutexLock instance_binding_lock(_instance_binding_mutex);
for (uint32_t i = 0; i < _instance_binding_count; i++) {
if (_instance_bindings[i].reference_callback) {
if (!_instance_bindings[i].reference_callback(_instance_bindings[i].token, _instance_bindings[i].binding, p_reference)) {
@@ -675,7 +675,6 @@ protected:
}
}
}
- _instance_binding_mutex.unlock();
}
return can_die;
}
@@ -868,7 +867,8 @@ public:
argptrs[i] = &args[i];
}
Callable::CallError cerr;
- return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
+ const Variant ret = callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
+ return (cerr.error == Callable::CallError::CALL_OK) ? ret : Variant();
}
void notification(int p_notification, bool p_reversed = false);
@@ -895,6 +895,7 @@ public:
MTVIRTUAL void remove_meta(const StringName &p_name);
MTVIRTUAL Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const;
MTVIRTUAL void get_meta_list(List<StringName> *p_list) const;
+ MTVIRTUAL void merge_meta_from(const Object *p_src);
#ifdef TOOLS_ENABLED
void set_edited(bool p_edited);
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index 25ad3bf964..fe7bbd474c 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -127,9 +127,8 @@ void WorkerThreadPool::_process_task(Task *p_task) {
if (finished_users == max_users) {
// Get rid of the group, because nobody else is using it.
- task_mutex.lock();
+ MutexLock task_lock(task_mutex);
group_allocator.free(p_task->group);
- task_mutex.unlock();
}
// For groups, tasks get rid of themselves.
@@ -349,17 +348,13 @@ WorkerThreadPool::TaskID WorkerThreadPool::add_task(const Callable &p_action, bo
}
bool WorkerThreadPool::is_task_completed(TaskID p_task_id) const {
- task_mutex.lock();
+ MutexLock task_lock(task_mutex);
const Task *const *taskp = tasks.getptr(p_task_id);
if (!taskp) {
- task_mutex.unlock();
ERR_FAIL_V_MSG(false, "Invalid Task ID"); // Invalid task
}
- bool completed = (*taskp)->completed;
- task_mutex.unlock();
-
- return completed;
+ return (*taskp)->completed;
}
Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
@@ -522,10 +517,9 @@ void WorkerThreadPool::yield() {
}
void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
- task_mutex.lock();
+ MutexLock task_lock(task_mutex);
Task **taskp = tasks.getptr(p_task_id);
if (!taskp) {
- task_mutex.unlock();
ERR_FAIL_MSG("Invalid Task ID.");
}
Task *task = *taskp;
@@ -534,7 +528,6 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
// This avoids a race condition where a task is created and yield-over called before it's processed.
task->pending_notify_yield_over = true;
}
- task_mutex.unlock();
return;
}
@@ -542,8 +535,6 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
td.yield_is_over = true;
td.signaled = true;
td.cond_var.notify_one();
-
- task_mutex.unlock();
}
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
@@ -601,26 +592,20 @@ WorkerThreadPool::GroupID WorkerThreadPool::add_group_task(const Callable &p_act
}
uint32_t WorkerThreadPool::get_group_processed_element_count(GroupID p_group) const {
- task_mutex.lock();
+ MutexLock task_lock(task_mutex);
const Group *const *groupp = groups.getptr(p_group);
if (!groupp) {
- task_mutex.unlock();
ERR_FAIL_V_MSG(0, "Invalid Group ID");
}
- uint32_t elements = (*groupp)->completed_index.get();
- task_mutex.unlock();
- return elements;
+ return (*groupp)->completed_index.get();
}
bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const {
- task_mutex.lock();
+ MutexLock task_lock(task_mutex);
const Group *const *groupp = groups.getptr(p_group);
if (!groupp) {
- task_mutex.unlock();
ERR_FAIL_V_MSG(false, "Invalid Group ID");
}
- bool completed = (*groupp)->completed.is_set();
- task_mutex.unlock();
- return completed;
+ return (*groupp)->completed.is_set();
}
void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
@@ -644,15 +629,13 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
if (finished_users == max_users) {
// All tasks using this group are gone (finished before the group), so clear the group too.
- task_mutex.lock();
+ MutexLock task_lock(task_mutex);
group_allocator.free(group);
- task_mutex.unlock();
}
}
- task_mutex.lock(); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread.
+ MutexLock task_lock(task_mutex); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread.
groups.erase(p_group);
- task_mutex.unlock();
#endif
}
@@ -704,6 +687,8 @@ void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio)
max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count - 1);
+ print_verbose(vformat("WorkerThreadPool: %d threads, %d max low-priority.", p_thread_count, max_low_priority_threads));
+
threads.resize(p_thread_count);
for (uint32_t i = 0; i < threads.size(); i++) {
diff --git a/core/os/os.h b/core/os/os.h
index be8820eba9..e6ce527720 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -111,9 +111,6 @@ protected:
virtual void initialize() = 0;
virtual void initialize_joypads() = 0;
- void set_current_rendering_driver_name(const String &p_driver_name) { _current_rendering_driver_name = p_driver_name; }
- void set_current_rendering_method(const String &p_name) { _current_rendering_method = p_name; }
-
void set_display_driver_id(int p_display_driver_id) { _display_driver_id = p_display_driver_id; }
virtual void set_main_loop(MainLoop *p_main_loop) = 0;
@@ -131,6 +128,9 @@ public:
static OS *get_singleton();
+ void set_current_rendering_driver_name(const String &p_driver_name) { _current_rendering_driver_name = p_driver_name; }
+ void set_current_rendering_method(const String &p_name) { _current_rendering_method = p_name; }
+
String get_current_rendering_driver_name() const { return _current_rendering_driver_name; }
String get_current_rendering_method() const { return _current_rendering_method; }
diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp
index 21fe0c8082..0294dbfbbc 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -255,11 +255,10 @@ StringName::StringName(const StringName &p_name) {
}
void StringName::assign_static_unique_class_name(StringName *ptr, const char *p_name) {
- mutex.lock();
+ MutexLock lock(mutex);
if (*ptr == StringName()) {
*ptr = StringName(p_name, true);
}
- mutex.unlock();
}
StringName::StringName(const char *p_name, bool p_static) {
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
index 6e784881d0..4ac79ad10a 100644
--- a/core/string/translation_server.cpp
+++ b/core/string/translation_server.cpp
@@ -284,6 +284,11 @@ String TranslationServer::_standardize_locale(const String &p_locale, bool p_add
}
int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const {
+ if (p_locale_a == p_locale_b) {
+ // Exact match.
+ return 10;
+ }
+
String locale_a = _standardize_locale(p_locale_a, true);
String locale_b = _standardize_locale(p_locale_b, true);
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 667aae879c..9dff5c1e91 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -112,7 +112,7 @@ Error Callable::rpcp(int p_id, const Variant **p_arguments, int p_argcount, Call
argptrs[i + 2] = p_arguments[i];
}
- CallError tmp;
+ CallError tmp; // TODO: Check `tmp`?
Error err = (Error)obj->callp(SNAME("rpc_id"), argptrs, argcount, tmp).operator int64_t();
r_call_error.error = Callable::CallError::CALL_OK;
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 7416101d51..733d13a106 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -81,15 +81,7 @@ Variant Dictionary::get_value_at_index(int p_index) const {
Variant &Dictionary::operator[](const Variant &p_key) {
if (unlikely(_p->read_only)) {
- if (p_key.get_type() == Variant::STRING_NAME) {
- const StringName *sn = VariantInternal::get_string_name(&p_key);
- const String &key = sn->operator String();
- if (likely(_p->variant_map.has(key))) {
- *_p->read_only = _p->variant_map[key];
- } else {
- *_p->read_only = Variant();
- }
- } else if (likely(_p->variant_map.has(p_key))) {
+ if (likely(_p->variant_map.has(p_key))) {
*_p->read_only = _p->variant_map[p_key];
} else {
*_p->read_only = Variant();
@@ -97,12 +89,7 @@ Variant &Dictionary::operator[](const Variant &p_key) {
return *_p->read_only;
} else {
- if (p_key.get_type() == Variant::STRING_NAME) {
- const StringName *sn = VariantInternal::get_string_name(&p_key);
- return _p->variant_map[sn->operator String()];
- } else {
- return _p->variant_map[p_key];
- }
+ return _p->variant_map[p_key];
}
}
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 24b30112bd..186643b024 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -2113,7 +2113,7 @@ Variant::operator ::RID() const {
}
#endif
Callable::CallError ce;
- Variant ret = _get_obj().obj->callp(CoreStringName(get_rid), nullptr, 0, ce);
+ const Variant ret = _get_obj().obj->callp(CoreStringName(get_rid), nullptr, 0, ce);
if (ce.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::RID) {
return ret;
}
diff --git a/core/variant/variant_construct.h b/core/variant/variant_construct.h
index 5afdb884f6..a46fadb198 100644
--- a/core/variant/variant_construct.h
+++ b/core/variant/variant_construct.h
@@ -232,7 +232,7 @@ template <typename T>
class VariantConstructorFromString {
public:
static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- if (p_args[0]->get_type() != Variant::STRING) {
+ if (!p_args[0]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING;
@@ -240,7 +240,7 @@ public:
}
VariantTypeChanger<T>::change(&r_ret);
- const String &src_str = *VariantGetInternalPtr<String>::get_ptr(p_args[0]);
+ const String src_str = *p_args[0];
if (r_ret.get_type() == Variant::Type::INT) {
r_ret = src_str.to_int();
@@ -417,7 +417,7 @@ public:
return;
}
- if (p_args[2]->get_type() != Variant::STRING_NAME) {
+ if (!p_args[2]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 2;
r_error.expected = Variant::STRING_NAME;
@@ -426,8 +426,7 @@ public:
const Array &base_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]);
const uint32_t type = p_args[1]->operator uint32_t();
- const StringName &class_name = *VariantGetInternalPtr<StringName>::get_ptr(p_args[2]);
- r_ret = Array(base_arr, type, class_name, *p_args[3]);
+ r_ret = Array(base_arr, type, *p_args[2], *p_args[3]);
}
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 48176163a1..0cd4b86fe7 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -1288,8 +1288,8 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
List<Variant> keys;
dic->get_key_list(&keys);
for (const Variant &E : keys) {
- if (E.get_type() == Variant::STRING) {
- p_list->push_back(PropertyInfo(Variant::STRING, E));
+ if (E.is_string()) {
+ p_list->push_back(PropertyInfo(dic->get_valid(E).get_type(), E));
}
}
} else if (type == OBJECT) {