2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* resource_loader.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2021-01-01 19:13:46 +00:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 01:10:30 +00:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "resource_loader.h"
2018-09-11 16:13:45 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/project_settings.h"
2019-02-12 12:30:56 +00:00
# include "core/io/resource_importer.h"
2018-09-11 16:13:45 +00:00
# include "core/os/file_access.h"
# include "core/os/os.h"
2020-11-07 22:33:38 +00:00
# include "core/string/print_string.h"
# include "core/string/translation.h"
# include "core/variant/variant_parser.h"
2018-09-11 16:13:45 +00:00
2020-02-28 11:27:04 +00:00
# ifdef DEBUG_LOAD_THREADED
# define print_lt(m_text) print_line(m_text)
# else
# define print_lt(m_text)
# endif
2018-06-11 00:59:53 +00:00
Ref < ResourceFormatLoader > ResourceLoader : : loader [ ResourceLoader : : MAX_LOADERS ] ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int ResourceLoader : : loader_count = 0 ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
bool ResourceFormatLoader : : recognize_path ( const String & p_path , const String & p_for_type ) const {
2017-01-26 00:55:59 +00:00
String extension = p_path . get_extension ( ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
List < String > extensions ;
2017-03-05 15:44:50 +00:00
if ( p_for_type = = String ( ) ) {
2017-01-26 00:55:59 +00:00
get_recognized_extensions ( & extensions ) ;
} else {
2017-03-05 15:44:50 +00:00
get_recognized_extensions_for_type ( p_for_type , & extensions ) ;
2017-01-26 00:55:59 +00:00
}
2017-03-05 15:44:50 +00:00
for ( List < String > : : Element * E = extensions . front ( ) ; E ; E = E - > next ( ) ) {
2020-05-14 14:41:43 +00:00
if ( E - > get ( ) . nocasecmp_to ( extension ) = = 0 ) {
2014-02-10 01:10:30 +00:00
return true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
return false ;
}
2018-06-11 00:59:53 +00:00
bool ResourceFormatLoader : : handles_type ( const String & p_type ) const {
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " handles_type " ) ) {
// I guess custom loaders for custom resources should use "Resource"
return get_script_instance ( ) - > call ( " handles_type " , p_type ) ;
}
return false ;
}
String ResourceFormatLoader : : get_resource_type ( const String & p_path ) const {
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " get_resource_type " ) ) {
return get_script_instance ( ) - > call ( " get_resource_type " , p_path ) ;
}
return " " ;
}
2017-03-05 15:44:50 +00:00
void ResourceFormatLoader : : get_recognized_extensions_for_type ( const String & p_type , List < String > * p_extensions ) const {
2020-05-14 14:41:43 +00:00
if ( p_type = = " " | | handles_type ( p_type ) ) {
2014-02-10 01:10:30 +00:00
get_recognized_extensions ( p_extensions ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void ResourceLoader : : get_recognized_extensions_for_type ( const String & p_type , List < String > * p_extensions ) {
for ( int i = 0 ; i < loader_count ; i + + ) {
loader [ i ] - > get_recognized_extensions_for_type ( p_type , p_extensions ) ;
2014-02-10 01:10:30 +00:00
}
}
2018-08-10 18:57:43 +00:00
bool ResourceFormatLoader : : exists ( const String & p_path ) const {
return FileAccess : : exists ( p_path ) ; //by default just check file
}
2018-08-12 10:44:38 +00:00
2018-06-11 00:59:53 +00:00
void ResourceFormatLoader : : get_recognized_extensions ( List < String > * p_extensions ) const {
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " get_recognized_extensions " ) ) {
2020-02-17 21:06:54 +00:00
PackedStringArray exts = get_script_instance ( ) - > call ( " get_recognized_extensions " ) ;
2018-06-11 00:59:53 +00:00
{
2020-02-17 21:06:54 +00:00
const String * r = exts . ptr ( ) ;
2018-06-11 00:59:53 +00:00
for ( int i = 0 ; i < exts . size ( ) ; + + i ) {
p_extensions - > push_back ( r [ i ] ) ;
}
}
}
}
2021-02-11 17:18:45 +00:00
RES ResourceFormatLoader : : 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 ) {
2021-05-05 08:56:26 +00:00
// Check user-defined loader if there's any. Hard fail if it returns an error.
2018-06-11 00:59:53 +00:00
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " load " ) ) {
2021-02-11 17:18:45 +00:00
Variant res = get_script_instance ( ) - > call ( " load " , p_path , p_original_path , p_use_sub_threads , p_cache_mode ) ;
2018-06-11 00:59:53 +00:00
2021-05-05 08:56:26 +00:00
if ( res . get_type ( ) = = Variant : : INT ) { // Error code, abort.
2020-05-14 14:41:43 +00:00
if ( r_error ) {
2018-06-11 00:59:53 +00:00
* r_error = ( Error ) res . operator int64_t ( ) ;
2020-05-14 14:41:43 +00:00
}
2021-05-05 08:56:26 +00:00
return RES ( ) ;
} else { // Success, pass on result.
2020-05-14 14:41:43 +00:00
if ( r_error ) {
2018-06-11 00:59:53 +00:00
* r_error = OK ;
2020-05-14 14:41:43 +00:00
}
2018-06-11 00:59:53 +00:00
return res ;
}
2014-02-10 01:10:30 +00:00
}
2020-02-28 11:27:04 +00:00
2021-05-05 08:56:26 +00:00
ERR_FAIL_V_MSG ( RES ( ) , " Failed to load resource ' " + p_path + " '. ResourceFormatLoader::load was not implemented for this resource type. " ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void ResourceFormatLoader : : get_dependencies ( const String & p_path , List < String > * p_dependencies , bool p_add_types ) {
2018-06-11 00:59:53 +00:00
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " get_dependencies " ) ) {
2020-02-17 21:06:54 +00:00
PackedStringArray deps = get_script_instance ( ) - > call ( " get_dependencies " , p_path , p_add_types ) ;
2018-06-11 00:59:53 +00:00
{
2020-02-17 21:06:54 +00:00
const String * r = deps . ptr ( ) ;
2018-06-11 00:59:53 +00:00
for ( int i = 0 ; i < deps . size ( ) ; + + i ) {
p_dependencies - > push_back ( r [ i ] ) ;
}
}
}
}
Error ResourceFormatLoader : : rename_dependencies ( const String & p_path , const Map < String , String > & p_map ) {
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " rename_dependencies " ) ) {
Dictionary deps_dict ;
for ( Map < String , String > : : Element * E = p_map . front ( ) ; E ; E = E - > next ( ) ) {
deps_dict [ E - > key ( ) ] = E - > value ( ) ;
}
int64_t res = get_script_instance ( ) - > call ( " rename_dependencies " , deps_dict ) ;
return ( Error ) res ;
}
return OK ;
}
void ResourceFormatLoader : : _bind_methods ( ) {
{
2021-02-11 17:18:45 +00:00
MethodInfo info = MethodInfo ( Variant : : NIL , " load " , PropertyInfo ( Variant : : STRING , " path " ) , PropertyInfo ( Variant : : STRING , " original_path " ) , PropertyInfo ( Variant : : BOOL , " use_sub_threads " ) , PropertyInfo ( Variant : : INT , " cache_mode " ) ) ;
2018-06-11 00:59:53 +00:00
info . return_val . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
ClassDB : : add_virtual_method ( get_class_static ( ) , info ) ;
}
2020-02-17 21:06:54 +00:00
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( Variant : : PACKED_STRING_ARRAY , " get_recognized_extensions " ) ) ;
2020-02-20 21:58:05 +00:00
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( Variant : : BOOL , " handles_type " , PropertyInfo ( Variant : : STRING_NAME , " typename " ) ) ) ;
2018-06-11 00:59:53 +00:00
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( Variant : : STRING , " get_resource_type " , PropertyInfo ( Variant : : STRING , " path " ) ) ) ;
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( " get_dependencies " , PropertyInfo ( Variant : : STRING , " path " ) , PropertyInfo ( Variant : : STRING , " add_types " ) ) ) ;
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( Variant : : INT , " rename_dependencies " , PropertyInfo ( Variant : : STRING , " path " ) , PropertyInfo ( Variant : : STRING , " renames " ) ) ) ;
2021-02-19 12:35:31 +00:00
BIND_ENUM_CONSTANT ( CACHE_MODE_IGNORE ) ;
BIND_ENUM_CONSTANT ( CACHE_MODE_REUSE ) ;
BIND_ENUM_CONSTANT ( CACHE_MODE_REPLACE ) ;
2014-02-10 01:10:30 +00:00
}
///////////////////////////////////
2021-02-11 17:18:45 +00:00
RES 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 ) {
2017-08-31 21:57:03 +00:00
bool found = false ;
// Try all loaders and pick the first match for the type hint
for ( int i = 0 ; i < loader_count ; i + + ) {
if ( ! loader [ i ] - > recognize_path ( p_path , p_type_hint ) ) {
continue ;
}
found = true ;
2021-02-11 17:18:45 +00:00
RES res = loader [ i ] - > load ( p_path , p_original_path ! = String ( ) ? p_original_path : p_path , r_error , p_use_sub_threads , r_progress , p_cache_mode ) ;
2017-08-31 21:57:03 +00:00
if ( res . is_null ( ) ) {
continue ;
}
return res ;
}
2020-08-22 20:19:08 +00:00
ERR_FAIL_COND_V_MSG ( found , RES ( ) ,
vformat ( " Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once. " , p_path ) ) ;
2019-08-15 02:57:49 +00:00
2020-01-08 14:11:16 +00:00
# ifdef TOOLS_ENABLED
FileAccessRef file_check = FileAccess : : create ( FileAccess : : ACCESS_RESOURCES ) ;
ERR_FAIL_COND_V_MSG ( ! file_check - > file_exists ( p_path ) , RES ( ) , " Resource file not found: " + p_path + " . " ) ;
# endif
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( RES ( ) , " No loader found for resource: " + p_path + " . " ) ;
2017-08-31 21:57:03 +00:00
}
2020-02-28 11:27:04 +00:00
void ResourceLoader : : _thread_load_function ( void * p_userdata ) {
ThreadLoadTask & load_task = * ( ThreadLoadTask * ) p_userdata ;
load_task . loader_id = Thread : : get_caller_id ( ) ;
2019-01-27 22:24:55 +00:00
2020-02-28 11:27:04 +00:00
if ( load_task . semaphore ) {
2021-03-12 13:35:16 +00:00
//this is an actual thread, so wait for Ok from semaphore
2020-02-28 11:27:04 +00:00
thread_load_semaphore - > wait ( ) ; //wait until its ok to start loading
}
2021-02-11 17:18:45 +00:00
load_task . resource = _load ( load_task . remapped_path , load_task . remapped_path ! = load_task . local_path ? load_task . local_path : String ( ) , load_task . type_hint , load_task . cache_mode , & load_task . error , load_task . use_sub_threads , & load_task . progress ) ;
2020-02-28 11:27:04 +00:00
load_task . progress = 1.0 ; //it was fully loaded at this point, so force progress to 1.0
2019-02-16 20:38:09 +00:00
2020-02-28 11:27:04 +00:00
thread_load_mutex - > lock ( ) ;
if ( load_task . error ! = OK ) {
load_task . status = THREAD_LOAD_FAILED ;
2019-01-27 22:24:55 +00:00
} else {
2020-02-28 11:27:04 +00:00
load_task . status = THREAD_LOAD_LOADED ;
2019-01-27 22:24:55 +00:00
}
2020-02-28 11:27:04 +00:00
if ( load_task . semaphore ) {
if ( load_task . start_next & & thread_waiting_count > 0 ) {
thread_waiting_count - - ;
//thread loading count remains constant, this ends but another one begins
thread_load_semaphore - > post ( ) ;
} else {
thread_loading_count - - ; //no threads waiting, just reduce loading count
}
2019-01-27 22:24:55 +00:00
2020-02-28 11:27:04 +00:00
print_lt ( " END: load count: " + itos ( thread_loading_count ) + " / wait count: " + itos ( thread_waiting_count ) + " / suspended count: " + itos ( thread_suspended_count ) + " / active: " + itos ( thread_loading_count - thread_suspended_count ) ) ;
2019-01-27 22:24:55 +00:00
2020-02-28 11:27:04 +00:00
for ( int i = 0 ; i < load_task . poll_requests ; i + + ) {
load_task . semaphore - > post ( ) ;
}
memdelete ( load_task . semaphore ) ;
load_task . semaphore = nullptr ;
}
2019-02-16 20:38:09 +00:00
2020-02-28 11:27:04 +00:00
if ( load_task . resource . is_valid ( ) ) {
load_task . resource - > set_path ( load_task . local_path ) ;
2019-02-16 20:38:09 +00:00
2020-05-14 14:41:43 +00:00
if ( load_task . xl_remapped ) {
2020-02-28 11:27:04 +00:00
load_task . resource - > set_as_translation_remapped ( true ) ;
2020-05-14 14:41:43 +00:00
}
2019-02-16 20:38:09 +00:00
2020-02-28 11:27:04 +00:00
# ifdef TOOLS_ENABLED
2019-02-16 20:38:09 +00:00
2020-02-28 11:27:04 +00:00
load_task . resource - > set_edited ( false ) ;
if ( timestamp_on_load ) {
uint64_t mt = FileAccess : : get_modified_time ( load_task . remapped_path ) ;
//printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt);
load_task . resource - > set_last_modified_time ( mt ) ;
}
# endif
2019-01-27 22:24:55 +00:00
2020-02-28 11:27:04 +00:00
if ( _loaded_callback ) {
_loaded_callback ( load_task . resource , load_task . local_path ) ;
}
}
2015-08-23 23:15:56 +00:00
2020-02-28 11:27:04 +00:00
thread_load_mutex - > unlock ( ) ;
}
2020-05-14 12:29:06 +00:00
2021-02-11 17:18:45 +00:00
Error ResourceLoader : : load_threaded_request ( const String & p_path , const String & p_type_hint , bool p_use_sub_threads , ResourceFormatLoader : : CacheMode p_cache_mode , const String & p_source_resource ) {
2015-05-04 13:17:24 +00:00
String local_path ;
2020-05-14 14:41:43 +00:00
if ( p_path . is_rel_path ( ) ) {
2017-03-05 15:44:50 +00:00
local_path = " res:// " + p_path ;
2020-05-14 14:41:43 +00:00
} else {
2017-07-19 20:00:46 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
thread_load_mutex - > lock ( ) ;
2019-01-27 22:24:55 +00:00
2020-02-28 11:27:04 +00:00
if ( p_source_resource ! = String ( ) ) {
//must be loading from this resource
if ( ! thread_load_tasks . has ( p_source_resource ) ) {
thread_load_mutex - > unlock ( ) ;
ERR_FAIL_V_MSG ( ERR_INVALID_PARAMETER , " There is no thread loading source resource ' " + p_source_resource + " '. " ) ;
}
//must be loading from this thread
if ( thread_load_tasks [ p_source_resource ] . loader_id ! = Thread : : get_caller_id ( ) ) {
thread_load_mutex - > unlock ( ) ;
ERR_FAIL_V_MSG ( ERR_INVALID_PARAMETER , " Threading loading resource' " + local_path + " failed: Source specified: ' " + p_source_resource + " ' but was not called by it. " ) ;
2019-01-27 22:24:55 +00:00
}
2020-02-28 11:27:04 +00:00
//must not be already added as s sub tasks
if ( thread_load_tasks [ p_source_resource ] . sub_tasks . has ( local_path ) ) {
thread_load_mutex - > unlock ( ) ;
ERR_FAIL_V_MSG ( ERR_INVALID_PARAMETER , " Thread loading source resource ' " + p_source_resource + " ' already is loading ' " + local_path + " '. " ) ;
2018-09-02 16:59:33 +00:00
}
2020-02-28 11:27:04 +00:00
}
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
if ( thread_load_tasks . has ( local_path ) ) {
thread_load_tasks [ local_path ] . requests + + ;
if ( p_source_resource ! = String ( ) ) {
thread_load_tasks [ p_source_resource ] . sub_tasks . insert ( local_path ) ;
}
thread_load_mutex - > unlock ( ) ;
return OK ;
}
2018-09-02 16:59:33 +00:00
2020-02-28 11:27:04 +00:00
{
//create load task
ThreadLoadTask load_task ;
load_task . requests = 1 ;
load_task . remapped_path = _path_remap ( local_path , & load_task . xl_remapped ) ;
load_task . local_path = local_path ;
load_task . type_hint = p_type_hint ;
2021-02-11 17:18:45 +00:00
load_task . cache_mode = p_cache_mode ;
2020-02-28 11:27:04 +00:00
load_task . use_sub_threads = p_use_sub_threads ;
{ //must check if resource is already loaded before attempting to load it in a thread
if ( load_task . loader_id = = Thread : : get_caller_id ( ) ) {
thread_load_mutex - > unlock ( ) ;
ERR_FAIL_V_MSG ( ERR_INVALID_PARAMETER , " Attempted to load a resource already being loaded from this thread, cyclic reference? " ) ;
}
//lock first if possible
2021-01-18 13:01:38 +00:00
ResourceCache : : lock . read_lock ( ) ;
2020-02-28 11:27:04 +00:00
//get ptr
Resource * * rptr = ResourceCache : : resources . getptr ( local_path ) ;
if ( rptr ) {
RES res ( * rptr ) ;
//it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached
if ( res . is_valid ( ) ) {
//referencing is fine
load_task . resource = res ;
load_task . status = THREAD_LOAD_LOADED ;
load_task . progress = 1.0 ;
2018-09-02 16:59:33 +00:00
}
2020-02-28 11:27:04 +00:00
}
2021-01-18 13:01:38 +00:00
ResourceCache : : lock . read_unlock ( ) ;
2018-09-02 16:59:33 +00:00
}
2020-02-28 11:27:04 +00:00
if ( p_source_resource ! = String ( ) ) {
thread_load_tasks [ p_source_resource ] . sub_tasks . insert ( local_path ) ;
2018-09-02 16:59:33 +00:00
}
2020-02-28 11:27:04 +00:00
thread_load_tasks [ local_path ] = load_task ;
2014-02-10 01:10:30 +00:00
}
2020-02-28 11:27:04 +00:00
ThreadLoadTask & load_task = thread_load_tasks [ local_path ] ;
2017-12-14 18:33:54 +00:00
2020-02-28 11:27:04 +00:00
if ( load_task . resource . is_null ( ) ) { //needs to be loaded in thread
load_task . semaphore = memnew ( Semaphore ) ;
if ( thread_loading_count < thread_load_max ) {
thread_loading_count + + ;
thread_load_semaphore - > post ( ) ; //we have free threads, so allow one
} else {
thread_waiting_count + + ;
2019-01-27 22:24:55 +00:00
}
2020-02-28 11:27:04 +00:00
print_lt ( " REQUEST: load count: " + itos ( thread_loading_count ) + " / wait count: " + itos ( thread_waiting_count ) + " / suspended count: " + itos ( thread_suspended_count ) + " / active: " + itos ( thread_loading_count - thread_suspended_count ) ) ;
2021-01-19 12:29:41 +00:00
load_task . thread = memnew ( Thread ) ;
load_task . thread - > start ( _thread_load_function , & thread_load_tasks [ local_path ] ) ;
2020-02-28 11:27:04 +00:00
load_task . loader_id = load_task . thread - > get_id ( ) ;
2019-01-27 22:24:55 +00:00
}
2017-12-14 18:33:54 +00:00
2020-02-28 11:27:04 +00:00
thread_load_mutex - > unlock ( ) ;
return OK ;
}
float ResourceLoader : : _dependency_get_progress ( const String & p_path ) {
if ( thread_load_tasks . has ( p_path ) ) {
ThreadLoadTask & load_task = thread_load_tasks [ p_path ] ;
int dep_count = load_task . sub_tasks . size ( ) ;
if ( dep_count > 0 ) {
float dep_progress = 0 ;
for ( Set < String > : : Element * E = load_task . sub_tasks . front ( ) ; E ; E = E - > next ( ) ) {
dep_progress + = _dependency_get_progress ( E - > get ( ) ) ;
}
dep_progress / = float ( dep_count ) ;
dep_progress * = 0.5 ;
dep_progress + = load_task . progress * 0.5 ;
return dep_progress ;
} else {
return load_task . progress ;
2019-01-27 22:24:55 +00:00
}
2020-02-28 11:27:04 +00:00
} else {
return 1.0 ; //assume finished loading it so it no longer exists
2017-08-31 21:57:03 +00:00
}
2020-02-28 11:27:04 +00:00
}
2017-06-28 20:00:18 +00:00
2020-02-28 11:27:04 +00:00
ResourceLoader : : ThreadLoadStatus ResourceLoader : : load_threaded_get_status ( const String & p_path , float * r_progress ) {
String local_path ;
2020-05-14 14:41:43 +00:00
if ( p_path . is_rel_path ( ) ) {
2020-02-28 11:27:04 +00:00
local_path = " res:// " + p_path ;
2020-05-14 14:41:43 +00:00
} else {
2020-02-28 11:27:04 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
thread_load_mutex - > lock ( ) ;
if ( ! thread_load_tasks . has ( local_path ) ) {
thread_load_mutex - > unlock ( ) ;
return THREAD_LOAD_INVALID_RESOURCE ;
2014-02-10 01:10:30 +00:00
}
2020-02-28 11:27:04 +00:00
ThreadLoadTask & load_task = thread_load_tasks [ local_path ] ;
ThreadLoadStatus status ;
status = load_task . status ;
if ( r_progress ) {
* r_progress = _dependency_get_progress ( local_path ) ;
2019-01-27 22:24:55 +00:00
}
2020-02-28 11:27:04 +00:00
thread_load_mutex - > unlock ( ) ;
2018-10-29 19:36:31 +00:00
2020-02-28 11:27:04 +00:00
return status ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2020-02-28 11:27:04 +00:00
RES ResourceLoader : : load_threaded_get ( const String & p_path , Error * r_error ) {
2018-08-10 18:57:43 +00:00
String local_path ;
2020-05-14 14:41:43 +00:00
if ( p_path . is_rel_path ( ) ) {
2018-08-10 18:57:43 +00:00
local_path = " res:// " + p_path ;
2020-05-14 14:41:43 +00:00
} else {
2018-08-10 18:57:43 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path ) ;
2020-05-14 14:41:43 +00:00
}
2018-08-10 18:57:43 +00:00
2020-02-28 11:27:04 +00:00
thread_load_mutex - > lock ( ) ;
if ( ! thread_load_tasks . has ( local_path ) ) {
thread_load_mutex - > unlock ( ) ;
if ( r_error ) {
* r_error = ERR_INVALID_PARAMETER ;
}
return RES ( ) ;
2018-08-10 18:57:43 +00:00
}
2020-02-28 11:27:04 +00:00
ThreadLoadTask & load_task = thread_load_tasks [ local_path ] ;
2018-08-10 18:57:43 +00:00
2021-03-12 13:35:16 +00:00
//semaphore still exists, meaning it's still loading, request poll
2020-02-28 11:27:04 +00:00
Semaphore * semaphore = load_task . semaphore ;
if ( semaphore ) {
load_task . poll_requests + + ;
2018-08-10 18:57:43 +00:00
2020-02-28 11:27:04 +00:00
{
// As we got a semaphore, this means we are going to have to wait
// until the sub-resource is done loading
//
2021-03-12 13:35:16 +00:00
// As this thread will become 'blocked' we should "exchange" its
2020-02-28 11:27:04 +00:00
// active status with a waiting one, to ensure load continues.
//
// This ensures loading is never blocked and that is also within
// the maximum number of active threads.
if ( thread_waiting_count > 0 ) {
thread_waiting_count - - ;
thread_loading_count + + ;
thread_load_semaphore - > post ( ) ;
load_task . start_next = false ; //do not start next since we are doing it here
}
thread_suspended_count + + ;
print_lt ( " GET: load count: " + itos ( thread_loading_count ) + " / wait count: " + itos ( thread_waiting_count ) + " / suspended count: " + itos ( thread_suspended_count ) + " / active: " + itos ( thread_loading_count - thread_suspended_count ) ) ;
2018-08-10 18:57:43 +00:00
}
2020-02-28 11:27:04 +00:00
thread_load_mutex - > unlock ( ) ;
semaphore - > wait ( ) ;
thread_load_mutex - > lock ( ) ;
thread_suspended_count - - ;
if ( ! thread_load_tasks . has ( local_path ) ) { //may have been erased during unlock and this was always an invalid call
thread_load_mutex - > unlock ( ) ;
if ( r_error ) {
* r_error = ERR_INVALID_PARAMETER ;
}
return RES ( ) ;
}
2018-08-10 18:57:43 +00:00
}
2020-02-28 11:27:04 +00:00
RES resource = load_task . resource ;
if ( r_error ) {
* r_error = load_task . error ;
}
load_task . requests - - ;
if ( load_task . requests = = 0 ) {
if ( load_task . thread ) { //thread may not have been used
2021-01-19 12:29:41 +00:00
load_task . thread - > wait_to_finish ( ) ;
2020-02-28 11:27:04 +00:00
memdelete ( load_task . thread ) ;
}
thread_load_tasks . erase ( local_path ) ;
}
thread_load_mutex - > unlock ( ) ;
return resource ;
2018-08-10 18:57:43 +00:00
}
2021-02-11 17:18:45 +00:00
RES ResourceLoader : : load ( const String & p_path , const String & p_type_hint , ResourceFormatLoader : : CacheMode p_cache_mode , Error * r_error ) {
2020-05-14 14:41:43 +00:00
if ( r_error ) {
2017-03-05 15:44:50 +00:00
* r_error = ERR_CANT_OPEN ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2015-05-04 13:17:24 +00:00
String local_path ;
2020-05-14 14:41:43 +00:00
if ( p_path . is_rel_path ( ) ) {
2017-03-05 15:44:50 +00:00
local_path = " res:// " + p_path ;
2020-05-14 14:41:43 +00:00
} else {
2017-07-19 20:00:46 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2021-02-28 00:53:27 +00:00
if ( p_cache_mode ! = ResourceFormatLoader : : CACHE_MODE_IGNORE ) {
2020-02-28 11:27:04 +00:00
thread_load_mutex - > lock ( ) ;
2016-05-29 23:22:00 +00:00
2020-02-28 11:27:04 +00:00
//Is it already being loaded? poll until done
if ( thread_load_tasks . has ( local_path ) ) {
Error err = load_threaded_request ( p_path , p_type_hint ) ;
if ( err ! = OK ) {
if ( r_error ) {
* r_error = err ;
}
2020-03-02 18:17:20 +00:00
thread_load_mutex - > unlock ( ) ;
2020-02-28 11:27:04 +00:00
return RES ( ) ;
}
thread_load_mutex - > unlock ( ) ;
2019-01-27 22:24:55 +00:00
2020-02-28 11:27:04 +00:00
return load_threaded_get ( p_path , r_error ) ;
}
2019-01-27 22:24:55 +00:00
2020-02-28 11:27:04 +00:00
//Is it cached?
2021-01-18 13:01:38 +00:00
ResourceCache : : lock . read_lock ( ) ;
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
Resource * * rptr = ResourceCache : : resources . getptr ( local_path ) ;
if ( rptr ) {
RES res ( * rptr ) ;
//it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached
if ( res . is_valid ( ) ) {
2021-01-18 13:01:38 +00:00
ResourceCache : : lock . read_unlock ( ) ;
2020-02-28 11:27:04 +00:00
thread_load_mutex - > unlock ( ) ;
if ( r_error ) {
* r_error = OK ;
}
return res ; //use cached
}
2019-01-27 22:24:55 +00:00
}
2021-01-18 13:01:38 +00:00
ResourceCache : : lock . read_unlock ( ) ;
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
//load using task (but this thread)
ThreadLoadTask load_task ;
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
load_task . requests = 1 ;
load_task . local_path = local_path ;
load_task . remapped_path = _path_remap ( local_path , & load_task . xl_remapped ) ;
load_task . type_hint = p_type_hint ;
2021-02-11 17:18:45 +00:00
load_task . cache_mode = p_cache_mode ; //ignore
2020-02-28 11:27:04 +00:00
load_task . loader_id = Thread : : get_caller_id ( ) ;
thread_load_tasks [ local_path ] = load_task ;
thread_load_mutex - > unlock ( ) ;
_thread_load_function ( & thread_load_tasks [ local_path ] ) ;
return load_threaded_get ( p_path , r_error ) ;
} else {
bool xl_remapped = false ;
String path = _path_remap ( local_path , & xl_remapped ) ;
if ( path = = " " ) {
ERR_FAIL_V_MSG ( RES ( ) , " Remapping ' " + local_path + " ' failed. " ) ;
}
print_verbose ( " Loading resource: " + path ) ;
float p ;
2021-02-11 17:18:45 +00:00
RES res = _load ( path , local_path , p_type_hint , p_cache_mode , r_error , false , & p ) ;
2020-02-28 11:27:04 +00:00
if ( res . is_null ( ) ) {
print_verbose ( " Failed loading resource: " + path ) ;
return RES ( ) ;
2019-01-27 22:24:55 +00:00
}
2020-05-14 14:41:43 +00:00
if ( xl_remapped ) {
2020-02-28 11:27:04 +00:00
res - > set_as_translation_remapped ( true ) ;
2020-05-14 14:41:43 +00:00
}
2020-02-28 11:27:04 +00:00
# ifdef TOOLS_ENABLED
res - > set_edited ( false ) ;
if ( timestamp_on_load ) {
uint64_t mt = FileAccess : : get_modified_time ( path ) ;
//printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt);
res - > set_last_modified_time ( mt ) ;
}
# endif
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
return res ;
2014-02-10 01:10:30 +00:00
}
2020-02-28 11:27:04 +00:00
}
2014-02-10 01:10:30 +00:00
2020-02-28 11:27:04 +00:00
bool ResourceLoader : : exists ( const String & p_path , const String & p_type_hint ) {
String local_path ;
2020-05-14 14:41:43 +00:00
if ( p_path . is_rel_path ( ) ) {
2020-02-28 11:27:04 +00:00
local_path = " res:// " + p_path ;
2020-05-14 14:41:43 +00:00
} else {
2020-02-28 11:27:04 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path ) ;
2020-05-14 14:41:43 +00:00
}
2020-02-28 11:27:04 +00:00
if ( ResourceCache : : has ( local_path ) ) {
return true ; // If cached, it probably exists
2019-01-27 22:24:55 +00:00
}
2020-02-28 11:27:04 +00:00
bool xl_remapped = false ;
String path = _path_remap ( local_path , & xl_remapped ) ;
2019-08-15 02:57:49 +00:00
2020-02-28 11:27:04 +00:00
// Try all loaders and pick the first match for the type hint
for ( int i = 0 ; i < loader_count ; i + + ) {
if ( ! loader [ i ] - > recognize_path ( path , p_type_hint ) ) {
continue ;
}
2020-05-14 14:41:43 +00:00
if ( loader [ i ] - > exists ( path ) ) {
2020-02-28 11:27:04 +00:00
return true ;
2020-05-14 14:41:43 +00:00
}
2020-02-28 11:27:04 +00:00
}
return false ;
2014-02-10 01:10:30 +00:00
}
2018-06-11 00:59:53 +00:00
void ResourceLoader : : add_resource_format_loader ( Ref < ResourceFormatLoader > p_format_loader , bool p_at_front ) {
ERR_FAIL_COND ( p_format_loader . is_null ( ) ) ;
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( loader_count > = MAX_LOADERS ) ;
2018-06-11 00:59:53 +00:00
2016-07-20 00:40:05 +00:00
if ( p_at_front ) {
2017-03-05 15:44:50 +00:00
for ( int i = loader_count ; i > 0 ; i - - ) {
loader [ i ] = loader [ i - 1 ] ;
2016-07-20 00:40:05 +00:00
}
2017-03-05 15:44:50 +00:00
loader [ 0 ] = p_format_loader ;
2016-07-20 00:40:05 +00:00
loader_count + + ;
} else {
2017-03-05 15:44:50 +00:00
loader [ loader_count + + ] = p_format_loader ;
2016-07-20 00:40:05 +00:00
}
2014-02-10 01:10:30 +00:00
}
2018-06-11 00:59:53 +00:00
void ResourceLoader : : remove_resource_format_loader ( Ref < ResourceFormatLoader > p_format_loader ) {
ERR_FAIL_COND ( p_format_loader . is_null ( ) ) ;
// Find loader
int i = 0 ;
for ( ; i < loader_count ; + + i ) {
2020-05-14 14:41:43 +00:00
if ( loader [ i ] = = p_format_loader ) {
2018-06-11 00:59:53 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2018-06-11 00:59:53 +00:00
}
ERR_FAIL_COND ( i > = loader_count ) ; // Not found
// Shift next loaders up
for ( ; i < loader_count - 1 ; + + i ) {
loader [ i ] = loader [ i + 1 ] ;
}
loader [ loader_count - 1 ] . unref ( ) ;
- - loader_count ;
}
2017-09-20 23:59:19 +00:00
int ResourceLoader : : get_import_order ( const String & p_path ) {
String path = _path_remap ( p_path ) ;
String local_path ;
2020-05-14 14:41:43 +00:00
if ( path . is_rel_path ( ) ) {
2017-09-20 23:59:19 +00:00
local_path = " res:// " + path ;
2020-05-14 14:41:43 +00:00
} else {
2017-09-20 23:59:19 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( path ) ;
2020-05-14 14:41:43 +00:00
}
2017-09-20 23:59:19 +00:00
for ( int i = 0 ; i < loader_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( ! loader [ i ] - > recognize_path ( local_path ) ) {
2017-09-20 23:59:19 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2017-09-20 23:59:19 +00:00
/*
if ( p_type_hint ! = " " & & ! loader [ i ] - > handles_type ( p_type_hint ) )
continue ;
*/
return loader [ i ] - > get_import_order ( p_path ) ;
}
return 0 ;
}
2019-04-19 18:54:33 +00:00
String ResourceLoader : : get_import_group_file ( const String & p_path ) {
String path = _path_remap ( p_path ) ;
String local_path ;
2020-05-14 14:41:43 +00:00
if ( path . is_rel_path ( ) ) {
2019-04-19 18:54:33 +00:00
local_path = " res:// " + path ;
2020-05-14 14:41:43 +00:00
} else {
2019-04-19 18:54:33 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( path ) ;
2020-05-14 14:41:43 +00:00
}
2019-04-19 18:54:33 +00:00
for ( int i = 0 ; i < loader_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( ! loader [ i ] - > recognize_path ( local_path ) ) {
2019-04-19 18:54:33 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2019-04-19 18:54:33 +00:00
/*
if ( p_type_hint ! = " " & & ! loader [ i ] - > handles_type ( p_type_hint ) )
continue ;
*/
return loader [ i ] - > get_import_group_file ( p_path ) ;
}
return String ( ) ; //not found
}
2017-08-29 22:50:58 +00:00
bool ResourceLoader : : is_import_valid ( const String & p_path ) {
String path = _path_remap ( p_path ) ;
String local_path ;
2020-05-14 14:41:43 +00:00
if ( path . is_rel_path ( ) ) {
2017-08-29 22:50:58 +00:00
local_path = " res:// " + path ;
2020-05-14 14:41:43 +00:00
} else {
2017-08-29 22:50:58 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( path ) ;
2020-05-14 14:41:43 +00:00
}
2017-08-29 22:50:58 +00:00
for ( int i = 0 ; i < loader_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( ! loader [ i ] - > recognize_path ( local_path ) ) {
2017-08-29 22:50:58 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2017-08-29 22:50:58 +00:00
/*
if ( p_type_hint ! = " " & & ! loader [ i ] - > handles_type ( p_type_hint ) )
continue ;
*/
return loader [ i ] - > is_import_valid ( p_path ) ;
}
return false ; //not found
}
2019-03-04 14:06:15 +00:00
bool ResourceLoader : : is_imported ( const String & p_path ) {
String path = _path_remap ( p_path ) ;
String local_path ;
2020-05-14 14:41:43 +00:00
if ( path . is_rel_path ( ) ) {
2019-03-04 14:06:15 +00:00
local_path = " res:// " + path ;
2020-05-14 14:41:43 +00:00
} else {
2019-03-04 14:06:15 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( path ) ;
2020-05-14 14:41:43 +00:00
}
2019-03-04 14:06:15 +00:00
for ( int i = 0 ; i < loader_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( ! loader [ i ] - > recognize_path ( local_path ) ) {
2019-03-04 14:06:15 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2019-03-04 14:06:15 +00:00
/*
if ( p_type_hint ! = " " & & ! loader [ i ] - > handles_type ( p_type_hint ) )
continue ;
*/
return loader [ i ] - > is_imported ( p_path ) ;
}
return false ; //not found
}
2017-03-05 15:44:50 +00:00
void ResourceLoader : : get_dependencies ( const String & p_path , List < String > * p_dependencies , bool p_add_types ) {
2017-06-28 20:00:18 +00:00
String path = _path_remap ( p_path ) ;
2015-08-23 23:15:56 +00:00
String local_path ;
2020-05-14 14:41:43 +00:00
if ( path . is_rel_path ( ) ) {
2017-06-28 20:00:18 +00:00
local_path = " res:// " + path ;
2020-05-14 14:41:43 +00:00
} else {
2017-07-19 20:00:46 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( path ) ;
2020-05-14 14:41:43 +00:00
}
2015-08-23 23:15:56 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < loader_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( ! loader [ i ] - > recognize_path ( local_path ) ) {
2015-08-23 23:15:56 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2017-01-14 11:26:56 +00:00
/*
if ( p_type_hint ! = " " & & ! loader [ i ] - > handles_type ( p_type_hint ) )
continue ;
*/
2015-08-23 23:15:56 +00:00
2017-03-05 15:44:50 +00:00
loader [ i ] - > get_dependencies ( local_path , p_dependencies , p_add_types ) ;
2015-08-23 23:15:56 +00:00
}
}
2017-03-05 15:44:50 +00:00
Error ResourceLoader : : rename_dependencies ( const String & p_path , const Map < String , String > & p_map ) {
2017-06-28 20:00:18 +00:00
String path = _path_remap ( p_path ) ;
2015-05-04 13:17:24 +00:00
String local_path ;
2020-05-14 14:41:43 +00:00
if ( path . is_rel_path ( ) ) {
2017-06-28 20:00:18 +00:00
local_path = " res:// " + path ;
2020-05-14 14:41:43 +00:00
} else {
2017-07-19 20:00:46 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( path ) ;
2020-05-14 14:41:43 +00:00
}
2015-05-04 13:17:24 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < loader_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( ! loader [ i ] - > recognize_path ( local_path ) ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2017-01-14 11:26:56 +00:00
/*
if ( p_type_hint ! = " " & & ! loader [ i ] - > handles_type ( p_type_hint ) )
continue ;
*/
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
return loader [ i ] - > rename_dependencies ( local_path , p_map ) ;
2014-02-10 01:10:30 +00:00
}
2015-08-23 23:15:56 +00:00
return OK ; // ??
2014-02-10 01:10:30 +00:00
}
String ResourceLoader : : get_resource_type ( const String & p_path ) {
2015-05-04 13:17:24 +00:00
String local_path ;
2020-05-14 14:41:43 +00:00
if ( p_path . is_rel_path ( ) ) {
2017-03-05 15:44:50 +00:00
local_path = " res:// " + p_path ;
2020-05-14 14:41:43 +00:00
} else {
2017-07-19 20:00:46 +00:00
local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path ) ;
2020-05-14 14:41:43 +00:00
}
2015-05-04 13:17:24 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < loader_count ; i + + ) {
2014-02-10 01:10:30 +00:00
String result = loader [ i ] - > get_resource_type ( local_path ) ;
2019-10-03 20:39:08 +00:00
if ( result ! = " " ) {
2014-02-10 01:10:30 +00:00
return result ;
2019-10-03 20:39:08 +00:00
}
2014-02-10 01:10:30 +00:00
}
return " " ;
}
2017-06-28 20:00:18 +00:00
String ResourceLoader : : _path_remap ( const String & p_path , bool * r_translation_remapped ) {
2017-12-14 18:33:54 +00:00
String new_path = p_path ;
2017-06-28 20:00:18 +00:00
2019-12-04 15:50:43 +00:00
if ( translation_remaps . has ( p_path ) ) {
// translation_remaps has the following format:
2020-02-17 21:06:54 +00:00
// { "res://path.png": PackedStringArray( "res://path-ru.png:ru", "res://path-de.png:de" ) }
2019-12-04 15:50:43 +00:00
// To find the path of the remapped resource, we extract the locale name after
// the last ':' to match the project locale.
// We also fall back in case of regional locales as done in TranslationServer::translate
// (e.g. 'ru_RU' -> 'ru' if the former has no specific mapping).
2017-12-14 18:33:54 +00:00
2017-06-28 20:00:18 +00:00
String locale = TranslationServer : : get_singleton ( ) - > get_locale ( ) ;
2019-12-04 15:50:43 +00:00
ERR_FAIL_COND_V_MSG ( locale . length ( ) < 2 , p_path , " Could not remap path ' " + p_path + " ' for translation as configured locale ' " + locale + " ' is invalid. " ) ;
String lang = TranslationServer : : get_language_code ( locale ) ;
2017-06-28 20:00:18 +00:00
2019-12-04 15:50:43 +00:00
Vector < String > & res_remaps = * translation_remaps . getptr ( new_path ) ;
bool near_match = false ;
for ( int i = 0 ; i < res_remaps . size ( ) ; i + + ) {
2020-07-03 13:26:22 +00:00
int split = res_remaps [ i ] . rfind ( " : " ) ;
2019-12-04 15:50:43 +00:00
if ( split = = - 1 ) {
2017-06-28 20:00:18 +00:00
continue ;
2019-12-04 15:50:43 +00:00
}
2017-06-28 20:00:18 +00:00
2020-02-13 15:42:49 +00:00
String l = res_remaps [ i ] . substr ( split + 1 ) . strip_edges ( ) ;
2019-12-04 15:50:43 +00:00
if ( l = = locale ) { // Exact match.
new_path = res_remaps [ i ] . left ( split ) ;
2017-12-14 18:33:54 +00:00
break ;
2019-12-04 15:50:43 +00:00
} else if ( near_match ) {
continue ; // Already found near match, keep going for potential exact match.
2017-06-28 20:00:18 +00:00
}
2019-12-04 15:50:43 +00:00
// No exact match (e.g. locale 'ru_RU' but remap is 'ru'), let's look further
// for a near match (same language code, i.e. first 2 or 3 letters before
// regional code, if included).
if ( TranslationServer : : get_language_code ( l ) = = lang ) {
// Language code matches, that's a near match. Keep looking for exact match.
near_match = true ;
new_path = res_remaps [ i ] . left ( split ) ;
continue ;
}
}
if ( r_translation_remapped ) {
* r_translation_remapped = true ;
2017-06-28 20:00:18 +00:00
}
}
2017-12-14 18:33:54 +00:00
if ( path_remaps . has ( new_path ) ) {
new_path = path_remaps [ new_path ] ;
}
2017-12-18 14:21:13 +00:00
2019-12-04 15:50:43 +00:00
if ( new_path = = p_path ) { // Did not remap.
// Try file remap.
2017-12-18 14:21:13 +00:00
Error err ;
FileAccess * f = FileAccess : : open ( p_path + " .remap " , FileAccess : : READ , & err ) ;
if ( f ) {
VariantParser : : StreamFile stream ;
stream . f = f ;
String assign ;
Variant value ;
VariantParser : : Tag next_tag ;
int lines = 0 ;
String error_text ;
while ( true ) {
assign = Variant ( ) ;
next_tag . fields . clear ( ) ;
next_tag . name = String ( ) ;
2020-04-01 23:20:12 +00:00
err = VariantParser : : parse_tag_assign_eof ( & stream , lines , error_text , next_tag , assign , value , nullptr , true ) ;
2017-12-18 14:21:13 +00:00
if ( err = = ERR_FILE_EOF ) {
break ;
} else if ( err ! = OK ) {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Parse error: " + p_path + " .remap: " + itos ( lines ) + " error: " + error_text + " . " ) ;
2017-12-18 14:21:13 +00:00
break ;
}
if ( assign = = " path " ) {
new_path = value ;
break ;
} else if ( next_tag . name ! = " remap " ) {
break ;
}
}
memdelete ( f ) ;
}
}
2017-12-14 18:33:54 +00:00
return new_path ;
2017-06-28 20:00:18 +00:00
}
String ResourceLoader : : import_remap ( const String & p_path ) {
if ( ResourceFormatImporter : : get_singleton ( ) - > recognize_path ( p_path ) ) {
return ResourceFormatImporter : : get_singleton ( ) - > get_internal_resource_path ( p_path ) ;
}
return p_path ;
}
String ResourceLoader : : path_remap ( const String & p_path ) {
return _path_remap ( p_path ) ;
}
void ResourceLoader : : reload_translation_remaps ( ) {
2021-01-18 13:01:38 +00:00
ResourceCache : : lock . read_lock ( ) ;
2017-06-28 20:00:18 +00:00
List < Resource * > to_reload ;
SelfList < Resource > * E = remapped_list . first ( ) ;
while ( E ) {
to_reload . push_back ( E - > self ( ) ) ;
E = E - > next ( ) ;
}
2021-01-18 13:01:38 +00:00
ResourceCache : : lock . read_unlock ( ) ;
2017-06-28 20:00:18 +00:00
//now just make sure to not delete any of these resources while changing locale..
while ( to_reload . front ( ) ) {
to_reload . front ( ) - > get ( ) - > reload_from_file ( ) ;
to_reload . pop_front ( ) ;
}
}
void ResourceLoader : : load_translation_remaps ( ) {
2021-02-17 16:44:49 +00:00
if ( ! ProjectSettings : : get_singleton ( ) - > has_setting ( " internationalization/locale/translation_remaps " ) ) {
2017-07-26 13:03:13 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-07-26 13:03:13 +00:00
2021-02-17 16:44:49 +00:00
Dictionary remaps = ProjectSettings : : get_singleton ( ) - > get ( " internationalization/locale/translation_remaps " ) ;
2017-06-28 20:00:18 +00:00
List < Variant > keys ;
remaps . get_key_list ( & keys ) ;
for ( List < Variant > : : Element * E = keys . front ( ) ; E ; E = E - > next ( ) ) {
Array langs = remaps [ E - > get ( ) ] ;
Vector < String > lang_remaps ;
lang_remaps . resize ( langs . size ( ) ) ;
for ( int i = 0 ; i < langs . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
lang_remaps . write [ i ] = langs [ i ] ;
2017-06-28 20:00:18 +00:00
}
translation_remaps [ String ( E - > get ( ) ) ] = lang_remaps ;
}
}
void ResourceLoader : : clear_translation_remaps ( ) {
translation_remaps . clear ( ) ;
2020-08-30 19:44:39 +00:00
while ( remapped_list . first ( ) ! = nullptr ) {
remapped_list . remove ( remapped_list . first ( ) ) ;
}
2017-06-28 20:00:18 +00:00
}
2017-12-14 18:33:54 +00:00
void ResourceLoader : : load_path_remaps ( ) {
2020-05-14 14:41:43 +00:00
if ( ! ProjectSettings : : get_singleton ( ) - > has_setting ( " path_remap/remapped_paths " ) ) {
2017-12-14 18:33:54 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-12-14 18:33:54 +00:00
2020-02-17 21:06:54 +00:00
Vector < String > remaps = ProjectSettings : : get_singleton ( ) - > get ( " path_remap/remapped_paths " ) ;
2017-12-14 18:33:54 +00:00
int rc = remaps . size ( ) ;
ERR_FAIL_COND ( rc & 1 ) ; //must be even
2020-02-17 21:06:54 +00:00
const String * r = remaps . ptr ( ) ;
2017-12-14 18:33:54 +00:00
for ( int i = 0 ; i < rc ; i + = 2 ) {
path_remaps [ r [ i ] ] = r [ i + 1 ] ;
}
}
void ResourceLoader : : clear_path_remaps ( ) {
path_remaps . clear ( ) ;
}
2018-10-29 19:36:31 +00:00
void ResourceLoader : : set_load_callback ( ResourceLoadedCallback p_callback ) {
_loaded_callback = p_callback ;
}
2020-04-01 23:20:12 +00:00
ResourceLoadedCallback ResourceLoader : : _loaded_callback = nullptr ;
2018-10-29 19:36:31 +00:00
2018-06-11 00:59:53 +00:00
Ref < ResourceFormatLoader > ResourceLoader : : _find_custom_resource_format_loader ( String path ) {
for ( int i = 0 ; i < loader_count ; + + i ) {
if ( loader [ i ] - > get_script_instance ( ) & & loader [ i ] - > get_script_instance ( ) - > get_script ( ) - > get_path ( ) = = path ) {
return loader [ i ] ;
}
}
return Ref < ResourceFormatLoader > ( ) ;
}
bool ResourceLoader : : add_custom_resource_format_loader ( String script_path ) {
2020-05-14 14:41:43 +00:00
if ( _find_custom_resource_format_loader ( script_path ) . is_valid ( ) ) {
2018-06-11 00:59:53 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2018-06-11 00:59:53 +00:00
Ref < Resource > res = ResourceLoader : : load ( script_path ) ;
ERR_FAIL_COND_V ( res . is_null ( ) , false ) ;
ERR_FAIL_COND_V ( ! res - > is_class ( " Script " ) , false ) ;
Ref < Script > s = res ;
StringName ibt = s - > get_instance_base_type ( ) ;
bool valid_type = ClassDB : : is_parent_class ( ibt , " ResourceFormatLoader " ) ;
2019-08-15 02:57:49 +00:00
ERR_FAIL_COND_V_MSG ( ! valid_type , false , " Script does not inherit a CustomResourceLoader: " + script_path + " . " ) ;
2018-06-11 00:59:53 +00:00
Object * obj = ClassDB : : instance ( ibt ) ;
2020-04-01 23:20:12 +00:00
ERR_FAIL_COND_V_MSG ( obj = = nullptr , false , " Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String ( ibt ) + " . " ) ;
2018-06-11 00:59:53 +00:00
2020-11-15 14:14:06 +00:00
Ref < ResourceFormatLoader > crl = Object : : cast_to < ResourceFormatLoader > ( obj ) ;
2020-02-13 19:03:10 +00:00
crl - > set_script ( s ) ;
2018-06-11 00:59:53 +00:00
ResourceLoader : : add_resource_format_loader ( crl ) ;
return true ;
}
void ResourceLoader : : remove_custom_resource_format_loader ( String script_path ) {
2019-02-12 20:10:08 +00:00
Ref < ResourceFormatLoader > custom_loader = _find_custom_resource_format_loader ( script_path ) ;
2020-05-14 14:41:43 +00:00
if ( custom_loader . is_valid ( ) ) {
2019-02-12 20:10:08 +00:00
remove_resource_format_loader ( custom_loader ) ;
2020-05-14 14:41:43 +00:00
}
2018-06-11 00:59:53 +00:00
}
void ResourceLoader : : add_custom_loaders ( ) {
// Custom loaders registration exploits global class names
String custom_loader_base_class = ResourceFormatLoader : : get_class_static ( ) ;
List < StringName > global_classes ;
ScriptServer : : get_global_class_list ( & global_classes ) ;
for ( List < StringName > : : Element * E = global_classes . front ( ) ; E ; E = E - > next ( ) ) {
StringName class_name = E - > get ( ) ;
2019-03-09 03:47:27 +00:00
StringName base_class = ScriptServer : : get_global_class_native_base ( class_name ) ;
2018-06-11 00:59:53 +00:00
if ( base_class = = custom_loader_base_class ) {
String path = ScriptServer : : get_global_class_path ( class_name ) ;
add_custom_resource_format_loader ( path ) ;
}
}
}
void ResourceLoader : : remove_custom_loaders ( ) {
2020-03-17 06:33:00 +00:00
Vector < Ref < ResourceFormatLoader > > custom_loaders ;
2018-06-11 00:59:53 +00:00
for ( int i = 0 ; i < loader_count ; + + i ) {
if ( loader [ i ] - > get_script_instance ( ) ) {
custom_loaders . push_back ( loader [ i ] ) ;
}
}
for ( int i = 0 ; i < custom_loaders . size ( ) ; + + i ) {
remove_resource_format_loader ( custom_loaders [ i ] ) ;
}
}
2019-01-27 22:24:55 +00:00
void ResourceLoader : : initialize ( ) {
2020-02-28 11:27:04 +00:00
thread_load_mutex = memnew ( Mutex ) ;
thread_load_max = OS : : get_singleton ( ) - > get_processor_count ( ) ;
thread_loading_count = 0 ;
thread_waiting_count = 0 ;
thread_suspended_count = 0 ;
thread_load_semaphore = memnew ( Semaphore ) ;
2019-01-27 22:24:55 +00:00
}
void ResourceLoader : : finalize ( ) {
2020-02-28 11:27:04 +00:00
memdelete ( thread_load_mutex ) ;
memdelete ( thread_load_semaphore ) ;
2019-01-27 22:24:55 +00:00
}
2020-04-01 23:20:12 +00:00
ResourceLoadErrorNotify ResourceLoader : : err_notify = nullptr ;
void * ResourceLoader : : err_notify_ud = nullptr ;
2015-08-23 23:15:56 +00:00
2020-04-01 23:20:12 +00:00
DependencyErrorNotify ResourceLoader : : dep_err_notify = nullptr ;
void * ResourceLoader : : dep_err_notify_ud = nullptr ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
bool ResourceLoader : : abort_on_missing_resource = true ;
bool ResourceLoader : : timestamp_on_load = false ;
2017-06-28 20:00:18 +00:00
2020-02-28 11:27:04 +00:00
Mutex * ResourceLoader : : thread_load_mutex = nullptr ;
HashMap < String , ResourceLoader : : ThreadLoadTask > ResourceLoader : : thread_load_tasks ;
Semaphore * ResourceLoader : : thread_load_semaphore = nullptr ;
int ResourceLoader : : thread_loading_count = 0 ;
int ResourceLoader : : thread_waiting_count = 0 ;
int ResourceLoader : : thread_suspended_count = 0 ;
int ResourceLoader : : thread_load_max = 0 ;
2017-06-28 20:00:18 +00:00
SelfList < Resource > : : List ResourceLoader : : remapped_list ;
2020-03-17 06:33:00 +00:00
HashMap < String , Vector < String > > ResourceLoader : : translation_remaps ;
2017-12-14 18:33:54 +00:00
HashMap < String , String > ResourceLoader : : path_remaps ;
2018-10-05 02:00:02 +00:00
2020-04-01 23:20:12 +00:00
ResourceLoaderImport ResourceLoader : : import = nullptr ;