diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index c3c37aa89d54..47b01ab388cd 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -454,6 +454,25 @@ Ref ResourceLoader::load(const String &p_path, const String &p_type_hi return res; } +// Returns a shallow reference of the loading resource. +// Will fail if the resource is not loading. +Ref ResourceLoader::get_loading_resource(const String &p_path, Error *r_error) { + ERR_FAIL_COND_V(r_error == nullptr, nullptr); + *r_error = OK; + + String local_path = _validate_local_path(p_path); + { + MutexLock thread_load_lock(thread_load_mutex); + + if (!thread_load_tasks.has(local_path)) { + *r_error = FAILED; + return nullptr; + } + + return thread_load_tasks[local_path].resource; + } +} + Ref ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode) { String local_path = _validate_local_path(p_path); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 11abb4dc184b..d3d0611d5c44 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -204,6 +204,7 @@ class ResourceLoader { static bool is_within_load() { return load_nesting > 0; }; 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 Ref get_loading_resource(const String &p_path, Error *r_error); static bool exists(const String &p_path, const String &p_type_hint = ""); static void get_recognized_extensions_for_type(const String &p_type, List *p_extensions); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index aa26bb222daa..87732a526648 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -4296,10 +4296,11 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { } } else { // TODO: Don't load if validating: use completion cache. + String resource_type = ResourceLoader::get_resource_type(p_preload->resolved_path); // Must load GDScript separately to permit cyclic references // as ResourceLoader::load() detects and rejects those. - if (ResourceLoader::get_resource_type(p_preload->resolved_path) == "GDScript") { + if (resource_type == "GDScript") { Error err = OK; Ref res = GDScriptCache::get_shallow_script(p_preload->resolved_path, err, parser->script_path); p_preload->resource = res; @@ -4307,9 +4308,16 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { push_error(vformat(R"(Could not preload resource script "%s".)", p_preload->resolved_path), p_preload->path); } } else { - p_preload->resource = ResourceLoader::load(p_preload->resolved_path); - if (p_preload->resource.is_null()) { - push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path); + // Permit cyclic loading if `ResourceLoader` is currently loading that resource. + Error err = OK; + Ref res = ResourceLoader::get_loading_resource(p_preload->resolved_path, &err); + if (err == OK) { + p_preload->resource = res; + } else { + p_preload->resource = ResourceLoader::load(p_preload->resolved_path); + if (p_preload->resource.is_null()) { + push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path); + } } } }