diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 6974d98234cc..204e72fc6937 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -52,6 +52,10 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & return ::ResourceLoader::load_threaded_request(p_path, p_type_hint, p_use_sub_threads, ResourceFormatLoader::CacheMode(p_cache_mode)); } +Error ResourceLoader::load_threaded_request_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, bool p_use_sub_threads, CacheMode p_cache_mode) { + return ::ResourceLoader::load_threaded_request_whitelisted(p_path, p_external_path_whitelist, p_type_whitelist, p_type_hint, p_use_sub_threads, ResourceFormatLoader::CacheMode(p_cache_mode)); +} + ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) { float progress = 0; ::ResourceLoader::ThreadLoadStatus tls = ::ResourceLoader::load_threaded_get_status(p_path, &progress); @@ -77,6 +81,14 @@ Ref ResourceLoader::load(const String &p_path, const String &p_type_hi return ret; } +Ref ResourceLoader::load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, CacheMode p_cache_mode) { + Error err = OK; + Ref ret = ::ResourceLoader::load_whitelisted(p_path, p_external_path_whitelist, p_type_whitelist, p_type_hint, ResourceFormatLoader::CacheMode(p_cache_mode), &err); + + ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'."); + return ret; +} + Vector ResourceLoader::get_recognized_extensions_for_type(const String &p_type) { List exts; ::ResourceLoader::get_recognized_extensions_for_type(p_type, &exts); @@ -136,10 +148,12 @@ Vector ResourceLoader::list_directory(const String &p_directory) { void ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE)); + ClassDB::bind_method(D_METHOD("load_threaded_request_whitelisted", "path", "external_path_whitelist", "type_whitelist", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request_whitelisted, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE)); ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY); ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &ResourceLoader::load_threaded_get); ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE)); + ClassDB::bind_method(D_METHOD("load_whitelisted", "path", "external_path_whitelist", "type_whitelist", "type_hint", "cache_mode"), &ResourceLoader::load_whitelisted, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE)); ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &ResourceLoader::get_recognized_extensions_for_type); ClassDB::bind_method(D_METHOD("add_resource_format_loader", "format_loader", "at_front"), &ResourceLoader::add_resource_format_loader, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_resource_format_loader", "format_loader"), &ResourceLoader::remove_resource_format_loader); diff --git a/core/core_bind.h b/core/core_bind.h index b96dc56b0dc0..90e8c219f52c 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -70,10 +70,12 @@ class ResourceLoader : public Object { static ResourceLoader *get_singleton() { return singleton; } Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE); + Error load_threaded_request_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE); ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = ClassDB::default_array_arg); Ref load_threaded_get(const String &p_path); Ref load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE); + Ref load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE); Vector get_recognized_extensions_for_type(const String &p_type); void add_resource_format_loader(Ref p_format_loader, bool p_at_front); void remove_resource_format_loader(Ref p_format_loader); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 891510e3dc03..ea7a0e2310b1 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -430,8 +430,10 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { path = remaps[path]; } - Ref res = ResourceLoader::load(path, exttype, cache_mode_for_external); - + Ref res; + if (!using_whitelist || external_path_whitelist.has(path)) { + res = ResourceLoader::load(path, exttype, cache_mode_for_external); + } if (res.is_null()) { WARN_PRINT(vformat("Couldn't load resource: %s.", path)); } @@ -696,8 +698,15 @@ Error ResourceLoaderBinary::load() { } external_resources.write[i].path = path; //remap happens here, not on load because on load it can actually be used for filesystem dock resource remap - external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external); - if (external_resources[i].load_token.is_null()) { + + if (using_whitelist && !external_path_whitelist.has(path)) { + error = ERR_FILE_MISSING_DEPENDENCIES; + ERR_FAIL_V_MSG(error, "External dependency not in whitelist: " + path + "."); + } + + external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external, true, using_whitelist, external_path_whitelist, type_whitelist); + + if (!external_resources[i].load_token.is_valid()) { if (!ResourceLoader::get_abort_on_missing_resources()) { ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type); } else { @@ -768,7 +777,10 @@ Error ResourceLoaderBinary::load() { if (res.is_null()) { //did not replace - Object *obj = ClassDB::instantiate(t); + Object *obj = nullptr; + if (!using_whitelist || type_whitelist.has(t)) { + obj = ClassDB::instantiate(t); + } if (!obj) { if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { //create a missing resource @@ -1246,6 +1258,41 @@ Ref ResourceFormatLoaderBinary::load(const String &p_path, const Strin String path = !p_original_path.is_empty() ? p_original_path : p_path; loader.local_path = ProjectSettings::get_singleton()->localize_path(path); loader.res_path = loader.local_path; + loader.using_whitelist = false; + loader.open(f); + + err = loader.load(); + + if (r_error) { + *r_error = err; + } + + if (err) { + return Ref(); + } + return loader.resource; +} + +Ref ResourceFormatLoaderBinary::load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { + if (r_error) { + *r_error = ERR_FILE_CANT_OPEN; + } + + Error err; + Ref f = FileAccess::open(p_path, FileAccess::READ, &err); + + ERR_FAIL_COND_V_MSG(err != OK, Ref(), "Cannot open file '" + p_path + "'."); + + ResourceLoaderBinary loader; + loader.cache_mode = p_cache_mode; + loader.use_sub_threads = p_use_sub_threads; + loader.progress = r_progress; + String path = !p_original_path.is_empty() ? p_original_path : p_path; + loader.local_path = ProjectSettings::get_singleton()->localize_path(path); + loader.res_path = loader.local_path; + loader.using_whitelist = true; + loader.external_path_whitelist = p_external_path_whitelist; + loader.type_whitelist = p_type_whitelist; loader.open(f); err = loader.load(); diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index ec8d7ead5d2f..9023a8caea2f 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -84,6 +84,10 @@ class ResourceLoaderBinary { HashMap remaps; Error error = OK; + bool using_whitelist = false; + Dictionary external_path_whitelist; + Dictionary type_whitelist; + ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE; ResourceFormatLoader::CacheMode cache_mode_for_external = ResourceFormatLoader::CACHE_MODE_REUSE; @@ -111,6 +115,7 @@ class ResourceLoaderBinary { class ResourceFormatLoaderBinary : public ResourceFormatLoader { public: virtual Ref 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; + virtual Ref load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, 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; virtual void get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const override; virtual void get_recognized_extensions(List *p_extensions) const override; virtual bool handles_type(const String &p_type) const override; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index b7a14f2b8807..54d97d4f186a 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -171,7 +171,7 @@ Ref ResourceFormatImporter::load_internal(const String &p_path, Error } } - Ref res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, r_error, p_use_sub_threads, r_progress); + Ref res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, false, Dictionary(), Dictionary(), r_error, p_use_sub_threads, r_progress); #ifdef TOOLS_ENABLED if (res.is_valid()) { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 423d424aeafd..6e890337a7c9 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -217,6 +217,7 @@ void ResourceFormatLoader::_bind_methods() { GDVIRTUAL_BIND(_exists, "path"); GDVIRTUAL_BIND(_get_classes_used, "path"); GDVIRTUAL_BIND(_load, "path", "original_path", "use_sub_threads", "cache_mode"); + //GDVIRTUAL_BIND(_load_whitelisted, "path", "external_path_whitelist", "type_Whitelist", "original_path", "use_sub_threads", "cache_mode"); } /////////////////////////////////// @@ -279,7 +280,7 @@ ResourceLoader::LoadToken::~LoadToken() { clear(); } -Ref ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) { +Ref ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, bool p_using_whitelist, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, Error *r_error, bool p_use_sub_threads, float *r_progress) { const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; if (load_paths_stack.size()) { @@ -304,8 +305,12 @@ Ref ResourceLoader::_load(const String &p_path, const String &p_origin continue; } found = true; - res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); - if (res.is_valid()) { + if (p_using_whitelist) { + res = loader[i]->load_whitelisted(p_path, p_external_path_whitelist, p_type_whitelist, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + } else { + res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + } + if (!res.is_null()) { break; } } @@ -366,7 +371,17 @@ void ResourceLoader::_run_load_task(void *p_userdata) { const String &remapped_path = _path_remap(load_task.local_path, &xl_remapped); Error load_err = OK; - Ref res = _load(remapped_path, remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_err, load_task.use_sub_threads, &load_task.progress); + Ref res = _load(remapped_path, + remapped_path != load_task.local_path ? load_task.local_path : String(), + load_task.type_hint, + load_task.cache_mode, + load_task.using_whitelist, + load_task.external_path_whitelist, + load_task.type_whitelist, + &load_err, + load_task.use_sub_threads, + &load_task.progress); + if (MessageQueue::get_singleton() != MessageQueue::get_main_singleton()) { MessageQueue::get_singleton()->flush(); } @@ -487,7 +502,12 @@ static String _validate_local_path(const String &p_path) { } Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode) { - Ref token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode, true); + Ref token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode, true, false, Dictionary(), Dictionary()); + return token.is_valid() ? OK : FAILED; +} + +Error ResourceLoader::load_threaded_request_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode) { + Ref token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode, true, true, p_external_path_whitelist, p_type_whitelist); return token.is_valid() ? OK : FAILED; } @@ -524,8 +544,33 @@ Ref ResourceLoader::load(const String &p_path, const String &p_type_hi // cyclic load detection and awaiting. thread_mode = LOAD_THREAD_SPAWN_SINGLE; } - Ref load_token = _load_start(p_path, p_type_hint, thread_mode, p_cache_mode); - if (load_token.is_null()) { + Ref load_token = _load_start(p_path, p_type_hint, thread_mode, p_cache_mode, false, false, Dictionary(), Dictionary()); + if (!load_token.is_valid()) { + if (r_error) { + *r_error = FAILED; + } + return Ref(); + } + + Ref res = _load_complete(*load_token.ptr(), r_error); + return res; +} + +Ref ResourceLoader::load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error) { + if (r_error) { + *r_error = OK; + } + + LoadThreadMode thread_mode = LOAD_THREAD_FROM_CURRENT; + if (WorkerThreadPool::get_singleton()->get_caller_task_id() != WorkerThreadPool::INVALID_TASK_ID) { + // If user is initiating a single-threaded load from a WorkerThreadPool task, + // we instead spawn a new task so there's a precondition that a load in a pool task + // is always initiated by the engine. That makes certain aspects simpler, such as + // cyclic load detection and awaiting. + thread_mode = LOAD_THREAD_SPAWN_SINGLE; + } + Ref load_token = _load_start(p_path, p_type_hint, thread_mode, p_cache_mode, false, true, p_external_path_whitelist, p_type_whitelist); + if (!load_token.is_valid()) { if (r_error) { *r_error = FAILED; } @@ -536,7 +581,7 @@ Ref ResourceLoader::load(const String &p_path, const String &p_type_hi return res; } -Ref ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user) { +Ref ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user, bool p_use_whitelist, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist) { String local_path = _validate_local_path(p_path); bool ignoring_cache = p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE || p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP; @@ -585,6 +630,9 @@ Ref ResourceLoader::_load_start(const String &p_path, load_task.type_hint = p_type_hint; load_task.cache_mode = p_cache_mode; load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE; + load_task.using_whitelist = p_use_whitelist; + load_task.external_path_whitelist = p_external_path_whitelist; + load_task.type_whitelist = p_type_whitelist; if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) { Ref existing = ResourceCache::get_ref(local_path); if (existing.is_valid()) { diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index f933e88b23bf..23a65e24051b 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -71,6 +71,9 @@ class ResourceFormatLoader : public RefCounted { public: virtual Ref 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); + virtual Ref load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, 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) { + return Ref(); + } virtual bool exists(const String &p_path) const; virtual void get_recognized_extensions(List *p_extensions) const; virtual void get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const; @@ -135,7 +138,7 @@ class ResourceLoader { static const int BINARY_MUTEX_TAG = 1; - static Ref _load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user = false); + static Ref _load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user, bool p_use_whitelist, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist); static Ref _load_complete(LoadToken &p_load_token, Error *r_error); private: @@ -164,7 +167,7 @@ class ResourceLoader { friend class ResourceFormatImporter; - static Ref _load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress); + static Ref _load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, bool p_using_whitelist, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, Error *r_error, bool p_use_sub_threads, float *r_progress); static ResourceLoadedCallback _loaded_callback; @@ -188,6 +191,7 @@ class ResourceLoader { Error error = OK; Ref resource; bool use_sub_threads = false; + bool using_whitelist = false; HashSet sub_tasks; struct ResourceChangedConnection { @@ -196,6 +200,8 @@ class ResourceLoader { uint32_t flags = 0; }; LocalVector resource_changed_connections; + Dictionary external_path_whitelist; + Dictionary type_whitelist; }; static void _run_load_task(void *p_userdata); @@ -215,9 +221,12 @@ class ResourceLoader { static float _dependency_get_progress(const String &p_path); + static Error _load_threaded_request_whitelisted_int(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, bool p_use_whitelist, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist); + static bool _ensure_load_progress(); public: + static Error load_threaded_request_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE); static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE); static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr); static Ref load_threaded_get(const String &p_path, Error *r_error = nullptr); @@ -228,6 +237,7 @@ class ResourceLoader { static void resource_changed_disconnect(Resource *p_source, const Callable &p_callable); static void resource_changed_emit(Resource *p_source); + static Ref load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint = "", ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE, Error *r_error = nullptr); static Ref load(const String &p_path, const String &p_type_hint = "", ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE, Error *r_error = nullptr); static bool exists(const String &p_path, const String &p_type_hint = ""); diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index f8096dc7b141..3d518b126bf1 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -127,6 +127,33 @@ The [param cache_mode] property defines whether and how the cache should be used or updated when loading the resource. See [enum CacheMode] for details. + + + + + + + + + + Loads the resource using threads, but only if the resource's path is in the provided whitelist. The whitelist is a dictionary where the keys are the paths to be whitelisted. + The [param type_whitelist] is a dictionary where the keys are the types of resources to be whitelisted. + If [param use_sub_threads] is [code]true[/code], multiple threads will be used to load the resource, which makes loading faster, but may affect the main thread (and thus cause game slowdowns). + The [param cache_mode] property defines whether and how the cache should be used or updated when loading the resource. See [enum CacheMode] for details. + + + + + + + + + + + Loads a resource at the given [param path], caching the result for further access, but only if the resource's path is in the provided whitelist. The whitelist is a dictionary where the keys are the paths to be whitelisted. + The [param type_whitelist] is a dictionary where the keys are the types of resources to be whitelisted. + + diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index f48d15fc8476..fd038934fe4f 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -464,8 +464,8 @@ Error ResourceLoaderText::load() { ext_resources[id].path = path; ext_resources[id].type = type; - ext_resources[id].load_token = ResourceLoader::_load_start(path, type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external); - if (ext_resources[id].load_token.is_null()) { + ext_resources[id].load_token = ResourceLoader::_load_start(path, type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external, false, false, Dictionary(), Dictionary()); + if (!ext_resources[id].load_token.is_valid()) { if (ResourceLoader::get_abort_on_missing_resources()) { error = ERR_FILE_CORRUPT; error_text = "[ext_resource] referenced non-existent resource at: " + path;